Skip to content

Add PHPStan and fix all errors #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,31 @@ jobs:
- name: "Setup PHP"
uses: shivammathur/setup-php@v2
with:
coverage: xdebug
coverage: none
php-version: '8.3'

- name: "Install dependencies with composer"
run: composer update --no-interaction --no-progress

- name: "Run checkstyle with symplify/easy-coding-standard"
run: vendor/bin/ecs

phpstan:
name: "PHPStan"
runs-on: ubuntu-latest

steps:
- name: "Checkout"
uses: actions/checkout@v2

- name: "Setup PHP"
uses: shivammathur/setup-php@v2
with:
coverage: none
php-version: '8.3'

- name: "Install dependencies with composer"
run: composer update --no-interaction --no-progress

- name: "Run static analysis with phpstan/phpstan"
run: vendor/bin/phpstan
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"require-dev": {
"doctrine/annotations": "^1.14",
"myclabs/php-enum": "^1.8",
"phpstan/phpstan": "^1.11",
"phpunit/phpunit": "^9.6",
"symfony/form": "^7.0",
"symfony/http-kernel": "^7.0",
Expand Down
6 changes: 6 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$objectOrClass of class ReflectionClass constructor expects class\\-string\\<T of object\\>\\|T of object, string given\\.$#"
count: 1
path: src/ConstantExtractor.php
7 changes: 7 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
includes:
- phpstan-baseline.neon

parameters:
level: max
paths:
- src/
14 changes: 13 additions & 1 deletion src/ConstantExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
final class ConstantExtractor
{
/**
* @return list<mixed>
* @throws LogicException
*/
public static function extract(string $pattern): array
Expand All @@ -29,17 +30,25 @@ public static function extract(string $pattern): array
);
}

/**
* @param array<string, mixed> $constants
*
* @return list<mixed>
*/
private static function filter(array $constants, string $regexp, string $pattern): array
{
$matchingNames = \preg_grep($regexp, \array_keys($constants));

if (\count($matchingNames) === 0) {
if ($matchingNames === false || \count($matchingNames) === 0) {
throw LogicException::cannotExtractConstants($pattern, 'Pattern matches no constant.');
}

return \array_values(\array_intersect_key($constants, \array_flip($matchingNames)));
}

/**
* @return array<string, mixed>
*/
private static function publicConstants(string $class, string $pattern): array
{
try {
Expand Down Expand Up @@ -67,6 +76,9 @@ private static function publicConstants(string $class, string $pattern): array
return $list;
}

/**
* @return array{string, string}
*/
private static function explode(string $pattern): array
{
if (\substr_count($pattern, '::') !== 1) {
Expand Down
14 changes: 12 additions & 2 deletions src/ConstantListEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@

namespace Yokai\EnumBundle;

use Yokai\EnumBundle\Exception\LogicException;

/**
* @author Yann Eugoné <[email protected]>
*/
class ConstantListEnum extends Enum
{
public function __construct(string $constantsPattern, ?string $name = null)
{
$values = ConstantExtractor::extract($constantsPattern);
parent::__construct(\array_combine($values, $values), $name);
$choices = [];
foreach (ConstantExtractor::extract($constantsPattern) as $value) {
if (!\is_string($value) && !\is_int($value)) {
throw new LogicException(
\sprintf('Extracted constant enum value must be string or int, %s given.', \get_debug_type($value)),
);
}
$choices[(string)$value] = $value;
}
parent::__construct($choices, $name);
}
}
3 changes: 3 additions & 0 deletions src/Enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ protected function build(): array
throw new LogicException(static::class . '::' . __FUNCTION__ . ' should have been overridden.');
}

/**
* @phpstan-assert !null $this->choices
*/
private function init(): void
{
if ($this->choices !== null) {
Expand Down
6 changes: 3 additions & 3 deletions src/Form/Extension/EnumTypeGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@ public function guessTypeForConstraint(Constraint $constraint): ?TypeGuess
);
}

public function guessRequired($class, $property): ?ValueGuess
public function guessRequired(string $class, string $property): ?ValueGuess
{
return null; //override parent : not able to guess
}

public function guessMaxLength($class, $property): ?ValueGuess
public function guessMaxLength(string $class, string $property): ?ValueGuess
{
return null; //override parent : not able to guess
}

public function guessPattern($class, $property): ?ValueGuess
public function guessPattern(string $class, string $property): ?ValueGuess
{
return null; //override parent : not able to guess
}
Expand Down
4 changes: 3 additions & 1 deletion src/Form/Type/EnumType.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ function (string $name): bool {
->setDefault(
'choices',
function (Options $options): array {
$choices = $this->enumRegistry->get($options['enum'])->getChoices();
/** @var string $name */
$name = $options['enum'];
$choices = $this->enumRegistry->get($name)->getChoices();

if ($options['enum_choice_value'] === null) {
foreach ($choices as $value) {
Expand Down
5 changes: 4 additions & 1 deletion src/TranslatedEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
class TranslatedEnum extends Enum
{
/**
* @var array
* @var array<int|string, mixed>
*/
private $values;

Expand Down Expand Up @@ -64,6 +64,9 @@ protected function build(): array
if (\is_string($key)) {
$transLabel = $key;
}
if (!\is_scalar($transLabel)) {
$transLabel = $key;
}

$label = $this->translator->trans(\sprintf($this->transPattern, $transLabel), [], $this->transDomain);
$choices[$label] = $value;
Expand Down
78 changes: 27 additions & 51 deletions src/Validator/Constraints/Enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ final class Enum extends Choice
*/
public $enum;

/**
* @param array<string, mixed> $options
*/
public function __construct(
$enum = null,
$callback = null,
array $options = [],
string|null $enum = null,
callable|null|string $callback = null,
bool $multiple = null,
bool $strict = null,
int $min = null,
Expand All @@ -31,57 +35,29 @@ public function __construct(
string $multipleMessage = null,
string $minMessage = null,
string $maxMessage = null,
$groups = null,
$payload = null,
array $options = []
array|null $groups = null,
mixed $payload = null,
) {
if (\is_array($enum)) {
// Symfony 4.4 Constraints has single constructor argument containing all options
parent::__construct($enum);
} else {
if (\is_string($enum)) {
$this->enum = $enum;
}
// Symfony 5.x Constraints has many constructor arguments for PHP 8.0 Attributes support

$firstConstructorArg = (new \ReflectionClass(Choice::class))
->getConstructor()->getParameters()[0]->getName();
if ($firstConstructorArg === 'choices') {
// Prior to Symfony 5.3, first argument of Choice was $choices
parent::__construct(
null,
$callback,
$multiple,
$strict,
$min,
$max,
$message,
$multipleMessage,
$minMessage,
$maxMessage,
$groups,
$payload,
$options
);
} else {
// Since Symfony 5.3, first argument of Choice is $options
parent::__construct(
$options,
null,
$callback,
$multiple,
$strict,
$min,
$max,
$message,
$multipleMessage,
$minMessage,
$maxMessage,
$groups,
$payload
);
}
if (\is_string($enum)) {
$this->enum = $enum;
}

// Since Symfony 5.3, first argument of Choice is $options
parent::__construct(
$options,
null,
$callback,
$multiple,
$strict,
$min,
$max,
$message,
$multipleMessage,
$minMessage,
$maxMessage,
$groups,
$payload
);
}

public function getDefaultOption(): string
Expand Down
2 changes: 1 addition & 1 deletion src/Validator/Constraints/EnumValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function __construct(EnumRegistry $enumRegistry)
$this->enumRegistry = $enumRegistry;
}

public function validate($value, Constraint $constraint): void
public function validate(mixed $value, Constraint $constraint): void
{
if (!$constraint instanceof Enum) {
throw new UnexpectedTypeException($constraint, Enum::class);
Expand Down
6 changes: 6 additions & 0 deletions tests/Unit/ConstantListEnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,10 @@ public function testLabelNotFound(): void

$enum->getLabel('unknown');
}

public function testConstantMustBeStringOrInt(): void
{
$this->expectException(LogicException::class);
new ConstantListEnum(Vehicle::class . '::PERIOD_*', 'vehicle.period');
}
}
4 changes: 4 additions & 0 deletions tests/Unit/Fixtures/Vehicle.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ class Vehicle
public const WHEELS_TWO = 2;
public const WHEELS_FOUR = 4;
public const WHEELS_EIGHT = 8;

public const PERIOD_COLLECTION = [1817, 1980];
public const PERIOD_OLD = [1981, 2010];
public const PERIOD_RECENT = [2010, 2024];
}
2 changes: 1 addition & 1 deletion tests/Unit/Form/Extension/EnumTypeGuesserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ protected function getConstraints(array $options): array
->with(self::TEST_CLASS)
->willReturn($metadata);

$this->guesser = new EnumTypeGuesser($this->metadataFactory, $this->enumRegistry);
$this->guesser = new EnumTypeGuesser($this->metadataFactory);

parent::setUp();
}
Expand Down
6 changes: 3 additions & 3 deletions tests/Unit/Validator/Constraints/EnumValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ public function testEnumIsRequired(): void
public function testValidEnumIsRequired(): void
{
$this->expectException(ConstraintDefinitionException::class);
$this->validator->validate('foo', new Enum('state'));
$this->validator->validate('foo', new Enum(enum: 'state'));
}

public function testNullIsValid(): void
{
$this->validator->validate(null, new Enum('type'));
$this->validator->validate(null, new Enum(enum: 'type'));

$this->assertNoViolation();
}

public function testValidSingleEnum(): void
{
$this->validator->validate('customer', new Enum('type'));
$this->validator->validate('customer', new Enum(enum: 'type'));

$this->assertNoViolation();
}
Expand Down
Loading