diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index bf42e19..46d82c5 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] + php-versions: ['8.4', '8.5'] name: PHP ${{ matrix.php-versions }} steps: @@ -43,6 +43,43 @@ jobs: - name: Run test suite run: composer run-script tests + sf-compatibility: + runs-on: ubuntu-latest + strategy: + matrix: + sf-versions: ['6.4', '7.4', '8.0'] + name: Symfony ${{ matrix.sf-versions }} + + steps: + - uses: actions/checkout@v2 + + - name: Set Timezone + uses: szenius/set-timezone@v1.0 + with: + timezoneLinux: "Europe/Paris" + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.4 + extensions: json, intl + ini-values: date.timezone=Europe/Paris + - name: Check PHP Version + run: php -v + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + # psalm 6 is not compatible with Symfony 6.4, so remove it + - name: Remove psalm + run: composer remove --dev vimeo/psalm + + - name: Set Symfony version + run: composer require --dev "symfony/symfony:~${{ matrix.sf-versions }}.0" + + - name: Run test suite + run: composer run-script tests + analysis: name: Analysis runs-on: ubuntu-latest @@ -58,14 +95,14 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.4 extensions: json ini-values: date.timezone=Europe/Paris - name: Install Infection run: | - wget https://github.com/infection/infection/releases/download/0.21.5/infection.phar - wget https://github.com/infection/infection/releases/download/0.21.5/infection.phar.asc + wget https://github.com/infection/infection/releases/download/0.32.6/infection.phar + wget https://github.com/infection/infection/releases/download/0.32.6/infection.phar.asc chmod +x infection.phar gpg --recv-keys C6D76C329EBADE2FB9C458CFC5095986493B4AA0 gpg --with-fingerprint --verify infection.phar.asc infection.phar @@ -83,7 +120,7 @@ jobs: - name: Run Infection run: | git fetch --depth=1 origin $GITHUB_BASE_REF - ./infection.phar --logger-github --git-diff-filter=AM --min-msi=80 + ./infection.phar --logger-github --git-diff-base=origin/$GITHUB_BASE_REF --git-diff-filter=AM --min-msi=80 coverage: name: Test coverage @@ -100,7 +137,7 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.4 extensions: json ini-values: date.timezone=Europe/Paris diff --git a/composer.json b/composer.json index 0c93755..2bcae80 100755 --- a/composer.json +++ b/composer.json @@ -11,8 +11,7 @@ "autoload": { "psr-4": { "Bdf\\Form\\": "src" - }, - "classmap": ["polyfill/WeakReference.php"] + } }, "autoload-dev": { "psr-4": { @@ -20,18 +19,17 @@ } }, "require": { - "php": ">=7.4", - "symfony/property-access": "~4.3|~5.0|~6.0|~7.0", - "symfony/validator": "~4.3|~5.0|~6.0|~7.0", - "symfony/polyfill-php80": "~1.22" + "php": "~8.4", + "symfony/property-access": "~6.4|~7.0|~8.0", + "symfony/validator": "~6.4|~7.0|~8.0" }, "require-dev": { - "symfony/security-csrf": "~4.3|~5.0|~6.0|~7.0", + "symfony/security-csrf": "~6.4|~7.0|~8.0", "giggsey/libphonenumber-for-php": "~8.0|~9.0", - "phpunit/phpunit": "~7.0|~8.0|~9.0", - "vimeo/psalm": "~4.30|~5.22", - "symfony/http-foundation": "~4.3|~5.0|~6.0|~7.0", - "symfony/form": "~4.3|~5.0|~6.0|~7.0" + "phpunit/phpunit": "~13.0", + "vimeo/psalm": "~6.15.1", + "symfony/http-foundation": "~6.4|~7.0|~8.0", + "symfony/form": "~6.4|~7.0|~8.0" }, "suggest": { "symfony/security-csrf": "For enable CSRF element", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9b87c3e..3f3dd10 100755 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,29 +1,29 @@ - - + - - - - - - - - - - - tests - - - - - src - - + + + + + + + + + + tests + + + + + src + + diff --git a/polyfill/WeakReference.php b/polyfill/WeakReference.php deleted file mode 100644 index ca73df0..0000000 --- a/polyfill/WeakReference.php +++ /dev/null @@ -1,50 +0,0 @@ -obj = $obj; - } - - /** - * Get a weakly referenced Object - * - * @return T|null - */ - public function get() - { - return $this->obj; - } - - /** - * Create a new weak reference - * - * @param T $obj - * - * @return WeakReference - */ - public static function create($obj): WeakReference - { - return new self($obj); - } - } -} diff --git a/psalm.xml b/psalm.xml index 21a9869..8e65e46 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ + + + + + + + diff --git a/src/AbstractElementBuilder.php b/src/AbstractElementBuilder.php index 9591af4..ad537a9 100644 --- a/src/AbstractElementBuilder.php +++ b/src/AbstractElementBuilder.php @@ -8,6 +8,7 @@ use Bdf\Form\Util\TransformerBuilderTrait; use Bdf\Form\Util\ValidatorBuilderTrait; use Bdf\Form\Validator\ValueValidatorInterface; +use Override; /** * Base builder for elements @@ -20,15 +21,8 @@ abstract class AbstractElementBuilder implements ElementBuilderInterface use TransformerBuilderTrait; use ValidatorBuilderTrait; - /** - * @var RegistryInterface - */ - private $registry; - - /** - * @var mixed - */ - private $value; + private readonly RegistryInterface $registry; + private mixed $value = null; /** @@ -41,19 +35,15 @@ public function __construct(?RegistryInterface $registry = null) $this->registry = $registry ?: new Registry(); } - /** - * {@inheritdoc} - */ - final public function value($value) + #[Override] + final public function value(mixed $value): static { $this->value = $value; return $this; } - /** - * {@inheritdoc} - */ + #[Override] final public function buildElement(): ElementInterface { $element = $this->createElement($this->buildValidator(), $this->buildTransformer()); @@ -75,9 +65,7 @@ final public function buildElement(): ElementInterface */ abstract protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface; - /** - * {@inheritdoc} - */ + #[Override] final protected function registry(): RegistryInterface { return $this->registry; diff --git a/src/Aggregate/ArrayChildBuilder.php b/src/Aggregate/ArrayChildBuilder.php index 1cc0806..a057adc 100644 --- a/src/Aggregate/ArrayChildBuilder.php +++ b/src/Aggregate/ArrayChildBuilder.php @@ -14,10 +14,7 @@ */ class ArrayChildBuilder extends ChildBuilder { - /** - * @var bool - */ - private $filterEmptyValues = true; + private bool $filterEmptyValues = true; /** * ArrayChildBuilder constructor. @@ -30,7 +27,7 @@ public function __construct(string $name, ElementBuilderInterface $elementBuilde { parent::__construct($name, $elementBuilder, $registry); - $this->addFilterProvider([$this, 'provideEmptyValueFilter']); + $this->addFilterProvider($this->provideEmptyValueFilter(...)); } /** diff --git a/src/Aggregate/ArrayElement.php b/src/Aggregate/ArrayElement.php index 951188a..8701dd3 100644 --- a/src/Aggregate/ArrayElement.php +++ b/src/Aggregate/ArrayElement.php @@ -25,6 +25,7 @@ use Countable; use Exception; use Iterator; +use Override; use Symfony\Component\Validator\Constraints\NotBlank; use TypeError; @@ -49,37 +50,17 @@ final class ArrayElement implements ChildAggregateInterface, Countable, Choiceab /** * @var ElementInterface */ - private $templateElement; - - /** - * @var TransformerInterface - */ - private $transformer; - - /** - * @var ValueValidatorInterface - */ - private $validator; - - /** - * @var ChoiceInterface|null - */ - private $choices; - - /** - * @var bool - */ - private $valid = false; - - /** - * @var FormError - */ - private $error; + private readonly ElementInterface $templateElement; + private readonly TransformerInterface $transformer; + private readonly ValueValidatorInterface $validator; + private readonly ?ChoiceInterface $choices; + private bool $valid = false; + private FormError $error; /** * @var ChildInterface[] */ - private $children = []; + private array $children = []; /** @@ -99,66 +80,50 @@ public function __construct(ElementInterface $templateElement, ?TransformerInter $this->choices = $choices; } - /** - * {@inheritdoc} - */ - public function offsetGet($offset): ChildInterface + #[Override] + public function offsetGet(mixed $offset): ChildInterface { return $this->children[$offset]; } - /** - * {@inheritdoc} - */ - public function offsetExists($offset): bool + #[Override] + public function offsetExists(mixed $offset): bool { return isset($this->children[$offset]); } - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value): void + #[Override] + public function offsetSet(mixed $offset, mixed $value): void { throw new BadMethodCallException('Use import() or submit() for set an offset value'); } - /** - * {@inheritdoc} - */ - public function offsetUnset($offset): void + #[Override] + public function offsetUnset(mixed $offset): void { throw new BadMethodCallException('Use import() or submit() for set an offset value'); } - /** - * {@inheritdoc} - */ + #[Override] public function getIterator(): Iterator { return new ArrayIterator($this->children); } - /** - * {@inheritdoc} - */ + #[Override] public function count(): int { return count($this->children); } - /** - * {@inheritdoc} - */ + #[Override] public function choices(): ?ChoiceInterface { return $this->choices; } - /** - * {@inheritdoc} - */ - public function submit($data): ElementInterface + #[Override] + public function submit(mixed $data): static { $this->valid = true; @@ -180,7 +145,7 @@ public function submit($data): ElementInterface $errors = []; foreach ($data as $key => $value) { - $child = $lastChildren[$key] ?? (new Child($key, $this->templateElement))->setParent($this); + $child = $lastChildren[$key] ?? new Child($key, $this->templateElement)->setParent($this); $child->element()->submit($value); @@ -204,10 +169,8 @@ public function submit($data): ElementInterface return $this; } - /** - * {@inheritdoc} - */ - public function patch($data): ElementInterface + #[Override] + public function patch(mixed $data): static { $this->valid = true; @@ -231,10 +194,8 @@ public function patch($data): ElementInterface return $this; } - /** - * {@inheritdoc} - */ - public function import($entity): ElementInterface + #[Override] + public function import(mixed $entity): static { if ($entity === null) { $entity = []; @@ -261,6 +222,7 @@ public function import($entity): ElementInterface * * @return T[] */ + #[Override] public function value(): array { $value = []; @@ -272,10 +234,8 @@ public function value(): array return $value; } - /** - * {@inheritdoc} - */ - public function httpValue() + #[Override] + public function httpValue(): mixed { $value = []; @@ -286,33 +246,25 @@ public function httpValue() return $this->transformer->transformToHttp($value, $this); } - /** - * {@inheritdoc} - */ + #[Override] public function valid(): bool { return $this->valid; } - /** - * {@inheritdoc} - */ + #[Override] public function failed(): bool { return !$this->valid; } - /** - * {@inheritdoc} - */ + #[Override] public function error(?HttpFieldPath $field = null): FormError { return $field ? $this->error->withField($field) : $this->error; } - /** - * {@inheritdoc} - */ + #[Override] public function root(): RootElementInterface { if ($container = $this->container()) { @@ -323,9 +275,7 @@ public function root(): RootElementInterface return new LeafRootElement($this); } - /** - * {@inheritdoc} - */ + #[Override] public function view(?HttpFieldPath $field = null): ElementViewInterface { $elements = []; @@ -348,9 +298,6 @@ public function view(?HttpFieldPath $field = null): ElementViewInterface ); } - /** - * {@inheritdoc} - */ public function __clone() { $children = $this->children; diff --git a/src/Aggregate/ArrayElementBuilder.php b/src/Aggregate/ArrayElementBuilder.php index bb14dcb..1364707 100644 --- a/src/Aggregate/ArrayElementBuilder.php +++ b/src/Aggregate/ArrayElementBuilder.php @@ -2,10 +2,8 @@ namespace Bdf\Form\Aggregate; -use Bdf\Form\Choice\ArrayChoice; use Bdf\Form\Choice\ChoiceBuilderTrait; use Bdf\Form\Choice\ChoiceInterface; -use Bdf\Form\Choice\LazyChoice; use Bdf\Form\ElementBuilderInterface; use Bdf\Form\ElementInterface; use Bdf\Form\Leaf\BooleanElement; @@ -16,23 +14,17 @@ use Bdf\Form\Phone\PhoneElement; use Bdf\Form\Registry\Registry; use Bdf\Form\Registry\RegistryInterface; +use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Util\MagicCallForwarding; use Bdf\Form\Util\TransformerBuilderTrait; use Bdf\Form\Util\ValidatorBuilderTrait; -use ReflectionClass; +use Override; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Choice as ChoiceConstraint; use Symfony\Component\Validator\Constraints\Count; use Symfony\Component\Validator\Constraints\NotBlank; -use TypeError; -use function func_get_arg; -use function func_num_args; -use function is_array; -use function is_bool; -use function is_callable; -use function sprintf; -use function trigger_error; +use function assert; /** * Builder for the array element @@ -67,21 +59,13 @@ class ArrayElementBuilder implements ElementBuilderInterface ValidatorBuilderTrait::satisfy as arrayConstraint; } - /** - * @var RegistryInterface - */ - private $registry; + private readonly RegistryInterface $registry; /** * @var ElementBuilderInterface>|null */ - private $element; - - /** - * @var mixed - */ - private $value; - + private ?ElementBuilderInterface $element = null; + private mixed $value = null; /** * ArrayBuilder constructor. @@ -98,9 +82,10 @@ public function __construct(?RegistryInterface $registry = null) * * Define a constraint on the inner element */ - public function satisfy($constraint, $options = null, bool $append = true) + #[Override] + public function satisfy(Constraint|callable $constraint, ?string $message = null, bool $append = true): static { - $this->getElementBuilder()->satisfy($constraint, $options, $append); + $this->getElementBuilder()->satisfy($constraint, $message, $append); return $this; } @@ -110,7 +95,8 @@ public function satisfy($constraint, $options = null, bool $append = true) * * Define a transformer on the inner element */ - public function transformer($transformer, bool $append = true) + #[Override] + public function transformer(callable|TransformerInterface $transformer, bool $append = true): static { $this->getElementBuilder()->transformer($transformer, $append); @@ -120,7 +106,8 @@ public function transformer($transformer, bool $append = true) /** * {@inheritdoc} */ - public function value($value) + #[Override] + public function value($value): static { $this->value = $value; @@ -143,15 +130,16 @@ public function value($value) * @template E as ElementInterface * @template EB as ElementBuilderInterface * - * @return ArrayElementBuilder + * @return static + * @psalm-this-out ArrayElementBuilder */ - public function element(string $element, ?callable $configurator = null): ArrayElementBuilder + public function element(string $element, ?callable $configurator = null): static { - /** @var ArrayElementBuilder $this */ // @todo exception if already defined ? + /** @psalm-suppress InvalidPropertyAssignmentValue */ $this->element = $this->registry->elementBuilder($element); - if ($configurator) { + if ($configurator !== null) { /** @psalm-suppress InvalidArgument */ $configurator($this->element); } @@ -163,15 +151,17 @@ public function element(string $element, ?callable $configurator = null): ArrayE * {@inheritdoc} * * @return ElementBuilderInterface> - * @psalm-suppress InvalidNullableReturnType */ + #[Override] public function getElementBuilder(): ElementBuilderInterface { if (!$this->element) { $this->element(StringElement::class); } - /** @psalm-suppress NullableReturnStatement */ + assert($this->element !== null); + + /** @var ElementBuilderInterface> */ return $this->element; } @@ -186,9 +176,10 @@ public function getElementBuilder(): ElementBuilderInterface * * @param callable(ElementBuilderInterface):void|null $configurator Callback for configure the inner element builder * - * @return ArrayElementBuilder + * @return static + * @psalm-this-out ArrayElementBuilder */ - public function string(?callable $configurator = null): ArrayElementBuilder + public function string(?callable $configurator = null): static { return $this->element(StringElement::class, $configurator); } @@ -204,9 +195,10 @@ public function string(?callable $configurator = null): ArrayElementBuilder * * @param callable(ElementBuilderInterface):void|null $configurator Callback for configure the inner element builder * - * @return ArrayElementBuilder + * @return static + * @psalm-this-out ArrayElementBuilder */ - public function integer(?callable $configurator = null): ArrayElementBuilder + public function integer(?callable $configurator = null): static { return $this->element(IntegerElement::class, $configurator); } @@ -222,9 +214,10 @@ public function integer(?callable $configurator = null): ArrayElementBuilder * * @param callable(ElementBuilderInterface):void|null $configurator Callback for configure the inner element builder * - * @return ArrayElementBuilder + * @return static + * @psalm-this-out ArrayElementBuilder */ - public function float(?callable $configurator = null): ArrayElementBuilder + public function float(?callable $configurator = null): static { return $this->element(FloatElement::class, $configurator); } @@ -238,9 +231,10 @@ public function float(?callable $configurator = null): ArrayElementBuilder * * @param callable(ElementBuilderInterface):void|null $configurator Callback for configure the inner element builder * - * @return ArrayElementBuilder + * @return static + * @psalm-this-out ArrayElementBuilder */ - public function boolean(?callable $configurator = null): ArrayElementBuilder + public function boolean(?callable $configurator = null): static { return $this->element(BooleanElement::class, $configurator); } @@ -256,9 +250,10 @@ public function boolean(?callable $configurator = null): ArrayElementBuilder * * @param callable(ElementBuilderInterface>):void|null $configurator Callback for configure the inner element builder * - * @return ArrayElementBuilder<\DateTimeInterface> + * @return static + * @psalm-this-out ArrayElementBuilder<\DateTimeInterface> */ - public function dateTime(?callable $configurator = null): ArrayElementBuilder + public function dateTime(?callable $configurator = null): static { return $this->element(DateTimeElement::class, $configurator); } @@ -274,9 +269,10 @@ public function dateTime(?callable $configurator = null): ArrayElementBuilder * * @param callable(ElementBuilderInterface>):void|null $configurator Callback for configure the inner element builder * - * @return ArrayElementBuilder<\libphonenumber\PhoneNumber> + * @return static + * @psalm-this-out ArrayElementBuilder<\libphonenumber\PhoneNumber> */ - public function phone(?callable $configurator = null): ArrayElementBuilder + public function phone(?callable $configurator = null): static { return $this->element(PhoneElement::class, $configurator); } @@ -295,9 +291,10 @@ public function phone(?callable $configurator = null): ArrayElementBuilder * * @param callable|null $configurator Configure the embedded form * - * @return ArrayElementBuilder + * @return static + * @psalm-this-out ArrayElementBuilder */ - public function form(?callable $configurator = null): ArrayElementBuilder + public function form(?callable $configurator = null): static { return $this->element(Form::class, $configurator); } @@ -307,9 +304,9 @@ public function form(?callable $configurator = null): ArrayElementBuilder * * Ex: `$builder->count(['min' => 3, 'max' => 5])` * - * @param array|int|null $exactly The exact expected number of elements, or an array of options (deprecated since 1.7) - * @param int|null $min Minimum expected number of elements - * @param int|null $max Maximum expected number of elements + * @param positive-int|null $exactly The exact expected number of elements + * @param non-negative-int|null $min Minimum expected number of elements + * @param positive-int|null $max Maximum expected number of elements * @param string|null $exactMessage * @param string|null $minMessage * @param string|null $maxMessage @@ -318,30 +315,17 @@ public function form(?callable $configurator = null): ArrayElementBuilder * * @see Count For the list of options */ - public function count($exactly = null, ?int $min = null, ?int $max = null, ?string $exactMessage = null, ?string $minMessage = null, ?string $maxMessage = null): ArrayElementBuilder + public function count(?int $exactly = null, ?int $min = null, ?int $max = null, ?string $exactMessage = null, ?string $minMessage = null, ?string $maxMessage = null): static { - if (is_array($exactly)) { - @trigger_error('Passing an array of options to count() is deprecated since 1.7. Pass the options as individual parameters instead.', E_USER_DEPRECATED); - - $min = $exactly['min'] ?? null; - $max = $exactly['max'] ?? null; - $exactMessage = $exactly['exactMessage'] ?? null; - $minMessage = $exactly['minMessage'] ?? null; - $maxMessage = $exactly['maxMessage'] ?? null; - $exactly = $exactly['exactly'] ?? null; - } - - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(Count::class))->getConstructor()->getNumberOfParameters() === 1; - } - return $this->arrayConstraint( - $isSf4 - ? new Count(['exactly' => $exactly, 'min' => $min, 'max' => $max, 'exactMessage' => $exactMessage, 'minMessage' => $minMessage, 'maxMessage' => $maxMessage]) - : new Count($exactly, $min, $max, null, $exactMessage, $minMessage, $maxMessage) // The constructor is consistent from sf 5 to 8, so we can safely use ordered parameters. + new Count( + exactly: $exactly, + min: $min, + max: $max, + exactMessage: $exactMessage, + minMessage: $minMessage, + maxMessage: $maxMessage + ) ); } @@ -350,55 +334,30 @@ public function count($exactly = null, ?int $min = null, ?int $max = null, ?stri * * @return $this */ - final public function required($options = null/*, ?bool $allowNull = null, ?callable $normalizer = null*/) + final public function required(string|Constraint|null $message = null, ?bool $allowNull = null, ?callable $normalizer = null): static { - // @todo rename $options to $message on bdf-form 2.0 - if (is_array($options)) { - @trigger_error('Passing an array of options to required() is deprecated since 1.7. Pass the options as individual parameters instead.', E_USER_DEPRECATED); - } - - // @todo declare allowNull and normalizer as actual parameters on bdf-form 2.0 - $allowNull = func_num_args() > 1 ? func_get_arg(1) : null; - $normalizer = func_num_args() > 2 ? func_get_arg(2) : null; - - if ($allowNull !== null && !is_bool($allowNull)) { - throw new TypeError(sprintf('The "allowNull" option of required() must be a boolean or null, "%s" given.', get_debug_type($allowNull))); + if (!$message instanceof Constraint) { + $message = new NotBlank( + message: $message, + allowNull: $allowNull, + normalizer: $normalizer + ); } - if ($normalizer !== null && !is_callable($normalizer)) { - throw new TypeError(sprintf('The "normalizer" option of required() must be a valid callable or null, "%s" given.', get_debug_type($normalizer))); - } - - if (!$options instanceof Constraint) { - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(NotBlank::class))->getConstructor()->getNumberOfParameters() === 1; - } - - if (is_array($options)) { - $message = $options['message'] ?? null; - $allowNull ??= $options['allowNull'] ?? null; - $normalizer ??= $options['normalizer'] ?? null; - } else { - $message = $options; - } - - $options = $isSf4 - ? new NotBlank(['message' => $message, 'allowNull' => $allowNull, 'normalizer' => $normalizer]) - : new NotBlank(null, $message, $allowNull, $normalizer) // The constructor is consistent from sf 5 to 8, so we can safely use ordered parameters. - ; - } - - return $this->arrayConstraint($options); + return $this->arrayConstraint($message); } /** * {@inheritdoc} + * + * @param ChoiceInterface|array|callable $choices The allowed values in PHP form. + * @param string|null $message The error message. + * @param non-negative-int $min + * @param positive-int $max */ - final public function choices($choices, $message = null, ?bool $multiple = null, ?bool $strict = null, ?int $min = null, ?int $max = null, ?string $minMessage = null, ?string $maxMessage = null): self + final public function choices(ChoiceInterface|array|callable $choices, ?string $message = null, ?bool $multiple = null, ?bool $strict = null, ?int $min = null, ?int $max = null, ?string $minMessage = null, ?string $maxMessage = null): static { + /** @psalm-suppress MissingConstructor */ $builder = new class { use ChoiceBuilderTrait { getChoices as public; @@ -406,15 +365,26 @@ final public function choices($choices, $message = null, ?bool $multiple = null, public ChoiceConstraint $constraint; - public function satisfy($constraint, $options = null, bool $append = true) + #[Override] + public function satisfy(Constraint|callable $constraint, ?string $message = null, bool $append = true): static { + assert($constraint instanceof ChoiceConstraint); $this->constraint = $constraint; return $this; } }; // Force the multiple option to true - $builder->choices($choices, $message, true, $strict, $min, $max, $minMessage, $maxMessage); + $builder->choices( + choices: $choices, + message: $message, + multiple: true, + strict: $strict, + min: $min, + max: $max, + minMessage: $minMessage, + maxMessage: $maxMessage + ); $this->arrayConstraint($builder->constraint); $this->choices = $builder->getChoices(); @@ -427,7 +397,8 @@ public function satisfy($constraint, $options = null, bool $append = true) * * @return ArrayElement */ - public function buildElement(): ElementInterface + #[Override] + public function buildElement(): ArrayElement { $element = new ArrayElement( $this->getElementBuilder()->buildElement(), @@ -443,9 +414,7 @@ public function buildElement(): ElementInterface return $element; } - /** - * {@inheritdoc} - */ + #[Override] protected function registry(): RegistryInterface { return $this->registry; diff --git a/src/Aggregate/ChildAggregateInterface.php b/src/Aggregate/ChildAggregateInterface.php index 12a4c8a..7516cf7 100644 --- a/src/Aggregate/ChildAggregateInterface.php +++ b/src/Aggregate/ChildAggregateInterface.php @@ -7,6 +7,7 @@ use Bdf\Form\ElementInterface; use Iterator; use IteratorAggregate; +use Override; /** * Form element consists of an aggregation of sub-elements wrapped into a ChildInterface @@ -27,7 +28,8 @@ interface ChildAggregateInterface extends ElementInterface, ArrayAccess, Iterato * * @param string $offset The child name */ - public function offsetGet($offset): ChildInterface; + #[Override] + public function offsetGet(mixed $offset): ChildInterface; /** * {@inheritdoc} @@ -36,7 +38,8 @@ public function offsetGet($offset): ChildInterface; * * @param string $offset The child name */ - public function offsetExists($offset): bool; + #[Override] + public function offsetExists(mixed $offset): bool; /** * {@inheritdoc} @@ -48,7 +51,8 @@ public function offsetExists($offset): bool; * * @throws \BadMethodCallException */ - public function offsetSet($offset, $value): void; + #[Override] + public function offsetSet(mixed $offset, mixed $value): void; /** * {@inheritdoc} @@ -57,7 +61,8 @@ public function offsetSet($offset, $value): void; * * @throws \BadMethodCallException */ - public function offsetUnset($offset): void; + #[Override] + public function offsetUnset(mixed $offset): void; /** * {@inheritdoc} @@ -66,5 +71,6 @@ public function offsetUnset($offset): void; * * @return Iterator */ + #[Override] public function getIterator(): Iterator; } diff --git a/src/Aggregate/Collection/ChildrenCollection.php b/src/Aggregate/Collection/ChildrenCollection.php index a898c0e..66cfe7f 100644 --- a/src/Aggregate/Collection/ChildrenCollection.php +++ b/src/Aggregate/Collection/ChildrenCollection.php @@ -8,6 +8,7 @@ use Countable; use Iterator; use IteratorAggregate; +use Override; /** * Simple implementation of children collection for handle dependencies order @@ -20,14 +21,12 @@ final class ChildrenCollection implements Countable, ChildrenCollectionInterface * * @var ChildInterface[] */ - private $children = []; + private array $children = []; /** * Flag to know if the form has view dependencies in its children - * - * @var boolean */ - private $hasViewDependencies = false; + private bool $hasViewDependencies = false; /** @@ -42,25 +41,19 @@ public function __construct(array $children = []) } } - /** - * {@inheritdoc} - */ + #[Override] public function add(ChildInterface $child): void { $this->addNamed($child->name(), $child); } - /** - * {@inheritdoc} - */ + #[Override] public function has(string $name): bool { return isset($this->children[$name]); } - /** - * {@inheritdoc} - */ + #[Override] public function remove(string $name): bool { if (!$this->has($name)) { @@ -72,81 +65,61 @@ public function remove(string $name): bool return true; } - /** - * {@inheritdoc} - */ - public function offsetExists($offset): bool + #[Override] + public function offsetExists(mixed $offset): bool { return $this->has($offset); } - /** - * {@inheritdoc} - */ - public function offsetGet($offset): ChildInterface + #[Override] + public function offsetGet(mixed $offset): ChildInterface { return $this->children[$offset]; } - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value): void + #[Override] + public function offsetSet(mixed $offset, mixed $value): void { $this->add($value); } - /** - * {@inheritdoc} - */ - public function offsetUnset($offset): void + #[Override] + public function offsetUnset(mixed $offset): void { $this->remove($offset); } - /** - * {@inheritdoc} - */ + #[Override] public function count(): int { return count($this->children); } - /** - * {@inheritdoc} - */ + #[Override] public function getIterator(): Iterator { return new ArrayIterator($this->children); } - /** - * {@inheritdoc} - */ + #[Override] public function reverseIterator(): Iterator { return new ArrayIterator($this->hasViewDependencies ? array_reverse($this->children) : $this->children); } - /** - * {@inheritdoc} - */ + #[Override] public function forwardIterator(): Iterator { return new ArrayIterator($this->children); } - /** - * {@inheritdoc} - */ + #[Override] public function all(): array { return $this->children; } - /** - * {@inheritdoc} - */ + #[Override] public function duplicate(ChildAggregateInterface $newParent): ChildrenCollectionInterface { $children = []; @@ -155,7 +128,7 @@ public function duplicate(ChildAggregateInterface $newParent): ChildrenCollectio $children[$key] = $child->setParent($newParent); } - $collection = new static(); + $collection = new self(); $collection->children = $children; $collection->hasViewDependencies = $this->hasViewDependencies; @@ -169,7 +142,7 @@ public function duplicate(ChildAggregateInterface $newParent): ChildrenCollectio * @param string $name * @param ChildInterface $child */ - private function addNamed($name, ChildInterface $child): void + private function addNamed(string $name, ChildInterface $child): void { $this->children[$name] = $child; $this->orderDependencies($child); diff --git a/src/Aggregate/Collection/ChildrenCollectionInterface.php b/src/Aggregate/Collection/ChildrenCollectionInterface.php index 7757f31..c56a02b 100644 --- a/src/Aggregate/Collection/ChildrenCollectionInterface.php +++ b/src/Aggregate/Collection/ChildrenCollectionInterface.php @@ -47,14 +47,14 @@ public function remove(string $name): bool; /** * Get the reverse iterator (i.e. iterate on higher dependencies in first) * - * @return Iterator + * @return Iterator */ public function reverseIterator(): Iterator; /** * Get the base iterator * - * @return Iterator + * @return Iterator */ public function forwardIterator(): Iterator; @@ -65,6 +65,7 @@ public function forwardIterator(): Iterator; * * @return Iterator */ + #[\Override] public function getIterator(): Iterator; /** diff --git a/src/Aggregate/Collection/DependencyIterator.php b/src/Aggregate/Collection/DependencyIterator.php index 7a0733e..df834f7 100644 --- a/src/Aggregate/Collection/DependencyIterator.php +++ b/src/Aggregate/Collection/DependencyIterator.php @@ -4,6 +4,9 @@ use Bdf\Form\Child\ChildInterface; use Iterator; +use Override; + +use function assert; /** * Iterate over @see DependencyTree @@ -17,27 +20,11 @@ final class DependencyIterator implements Iterator /** * @var ChildInterface[] */ - private $children; - - /** - * @var Level - */ - private $first; - - /** - * @var bool - */ - private $reverse; - - /** - * @var Level|null - */ - private $currentLevel; - - /** - * @var Iterator|null - */ - private $levelIterator; + private array $children; + private Level $first; + private bool $reverse; + private ?Level $currentLevel = null; + private ?Iterator $levelIterator = null; /** @@ -47,38 +34,30 @@ final class DependencyIterator implements Iterator * @param Level $first * @param bool $reverse Does iterate on reverse order on levels ? */ - public function __construct(array $children, Level $first, $reverse = true) + public function __construct(array $children, Level $first, bool $reverse = true) { $this->children = $children; $this->first = $first; $this->reverse = $reverse; } - /** - * {@inheritdoc} - * - * @return ChildInterface - */ + #[Override] public function current(): ChildInterface { return $this->children[$this->key()]; } - /** - * {@inheritdoc} - * - * @psalm-suppress PossiblyNullReference - * @psalm-suppress PossiblyNullReference - */ + #[Override] public function next(): void { + assert($this->levelIterator !== null); $this->levelIterator->next(); // The level iterator can be invalid if the level is empty // We need to skip empty levels while (!$this->levelIterator->valid()) { $this->levelIterator = null; - $this->currentLevel = $this->reverse ? $this->currentLevel->prev() : $this->currentLevel->next(); + $this->currentLevel = $this->reverse ? $this->currentLevel?->prev() : $this->currentLevel?->next(); // There is no more level, the iterator will be "invalid" if ($this->currentLevel === null) { @@ -97,28 +76,20 @@ public function next(): void } } - /** - * {@inheritdoc} - * - * @psalm-suppress PossiblyNullReference - */ - #[\ReturnTypeWillChange] - public function key() + #[Override] + public function key(): string|int { + assert($this->levelIterator !== null); return $this->levelIterator->key(); } - /** - * {@inheritdoc} - */ + #[Override] public function valid(): bool { return $this->levelIterator !== null && $this->levelIterator->valid(); } - /** - * {@inheritdoc} - */ + #[Override] public function rewind(): void { $this->currentLevel = $this->first; diff --git a/src/Aggregate/Collection/DependencyTree.php b/src/Aggregate/Collection/DependencyTree.php index c07630c..c9fa62c 100644 --- a/src/Aggregate/Collection/DependencyTree.php +++ b/src/Aggregate/Collection/DependencyTree.php @@ -6,6 +6,7 @@ use Bdf\Form\Aggregate\ChildAggregateInterface; use Bdf\Form\Child\ChildInterface; use Iterator; +use Override; /** * Handle form children dependencies @@ -33,21 +34,17 @@ final class DependencyTree implements \ArrayAccess, \IteratorAggregate, \Countab /** * @var ChildInterface[] */ - private $children = []; + private array $children = []; /** * The first level of dependencies - * - * @var Level */ - private $root; + private Level $root; /** * The last level of dependencies - * - * @var Level */ - private $last; + private Level $last; /** * Get the level of each elements @@ -55,8 +52,7 @@ final class DependencyTree implements \ArrayAccess, \IteratorAggregate, \Countab * * @var int[] */ - private $depth = []; - + private array $depth = []; /** * DependencyTree constructor. @@ -67,27 +63,19 @@ public function __construct() $this->last = $this->root; } - /** - * {@inheritdoc} - */ + #[Override] public function add(ChildInterface $child): void { $this->addNamed($child->name(), $child); } - /** - * {@inheritdoc} - */ + #[Override] public function has(string $name): bool { return isset($this->children[$name]); } - /** - * {@inheritdoc} - * - * @psalm-suppress PossiblyNullReference - */ + #[Override] public function remove(string $name): bool { if (!$this->has($name)) { @@ -112,81 +100,61 @@ public function remove(string $name): bool return true; } - /** - * {@inheritdoc} - */ - public function offsetExists($offset): bool + #[Override] + public function offsetExists(mixed $offset): bool { return $this->has($offset); } - /** - * {@inheritdoc} - */ - public function offsetGet($offset): ChildInterface + #[Override] + public function offsetGet(mixed $offset): ChildInterface { return $this->children[$offset]; } - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value): void + #[Override] + public function offsetSet(mixed $offset, mixed $value): void { $this->add($value); } - /** - * {@inheritdoc} - */ - public function offsetUnset($offset): void + #[Override] + public function offsetUnset(mixed $offset): void { $this->remove($offset); } - /** - * {@inheritdoc} - */ + #[Override] public function count(): int { return count($this->children); } - /** - * {@inheritdoc} - */ + #[Override] public function getIterator(): Iterator { return new ArrayIterator($this->children); } - /** - * {@inheritdoc} - */ + #[Override] public function reverseIterator(): Iterator { return new DependencyIterator($this->children, $this->last, true); } - /** - * {@inheritdoc} - */ + #[Override] public function forwardIterator(): Iterator { return new DependencyIterator($this->children, $this->root, false); } - /** - * {@inheritdoc} - */ + #[Override] public function all(): array { return $this->children; } - /** - * {@inheritdoc} - */ + #[Override] public function duplicate(ChildAggregateInterface $newParent): ChildrenCollectionInterface { $children = []; @@ -212,7 +180,7 @@ public function duplicate(ChildAggregateInterface $newParent): ChildrenCollectio * @psalm-suppress InvalidNullableReturnType * @psalm-suppress NullableReturnStatement */ - private function level($child) + private function level(ChildInterface|string $child): Level { if (!is_string($child)) { $child = $child->name(); @@ -238,7 +206,7 @@ private function level($child) * @param string $name * @param ChildInterface $child */ - private function addNamed($name, ChildInterface $child): void + private function addNamed(string $name, ChildInterface $child): void { $this->children[$name] = $child; @@ -261,7 +229,7 @@ private function addNamed($name, ChildInterface $child): void * * @return string[] */ - private function extractDependencies(ChildInterface $child, Level $level) + private function extractDependencies(ChildInterface $child, Level $level): array { $dependencies = []; diff --git a/src/Aggregate/Collection/Level.php b/src/Aggregate/Collection/Level.php index 2b7adfe..581466e 100644 --- a/src/Aggregate/Collection/Level.php +++ b/src/Aggregate/Collection/Level.php @@ -5,6 +5,7 @@ use ArrayIterator; use Iterator; use IteratorAggregate; +use Override; /** * The dependency tree level @@ -14,32 +15,17 @@ */ final class Level implements IteratorAggregate { - /** - * @var int - */ - private $number; - - /** - * @var Level|null - */ - private $prev; - - /** - * @var Level|null - */ - private $next; - - /** - * @var Level|null - */ - private $last; + private int $number; + private ?Level $prev; + private ?Level $next = null; + private ?Level $last = null; /** * Array of elements dependencies * * @var string[][] */ - private $elements = []; + private array $elements = []; /** @@ -48,7 +34,7 @@ final class Level implements IteratorAggregate * @param Level|null $prev * @param int $number */ - public function __construct(?Level $prev = null, $number = 0) + public function __construct(?Level $prev = null, int $number = 0) { $this->prev = $prev; $this->number = $number; @@ -62,7 +48,7 @@ public function __construct(?Level $prev = null, $number = 0) * * @return int[] Associative array, with element name as key, and element level as value */ - public function add($name, array $dependencies) + public function add(string $name, array $dependencies): array { $result = [ $name => $this->number @@ -84,7 +70,7 @@ public function add($name, array $dependencies) * * @return bool */ - public function has($element) + public function has(string $element): bool { return isset($this->elements[$element]); } @@ -96,7 +82,7 @@ public function has($element) * * @return int[] The result of add() */ - public function shift($element) + public function shift(string $element): array { if ($this->next === null) { $this->next = new self($this, $this->number + 1); @@ -122,7 +108,7 @@ public function shift($element) /** * @return int */ - public function number() + public function number(): int { return $this->number; } @@ -132,7 +118,7 @@ public function number() * * @return Level|null */ - public function prev() + public function prev(): ?Level { return $this->prev; } @@ -143,7 +129,7 @@ public function prev() * * @return Level|null */ - public function last() + public function last(): ?Level { return $this->last; } @@ -154,7 +140,7 @@ public function last() * * @return Level|null */ - public function next() + public function next(): ?Level { return $this->next; } @@ -164,6 +150,7 @@ public function next() * * @return Iterator */ + #[Override] public function getIterator(): Iterator { return new ArrayIterator($this->elements); @@ -174,7 +161,7 @@ public function getIterator(): Iterator * * @param string $name The element name */ - public function reset($name): void + public function reset(string $name): void { if ($this->has($name)) { $this->elements[$name] = []; @@ -186,7 +173,7 @@ public function reset($name): void * * @param string $name */ - public function remove($name): void + public function remove(string $name): void { unset($this->elements[$name]); } diff --git a/src/Aggregate/Form.php b/src/Aggregate/Form.php index 4b615c9..3128546 100644 --- a/src/Aggregate/Form.php +++ b/src/Aggregate/Form.php @@ -9,7 +9,6 @@ use Bdf\Form\Aggregate\View\FormView; use Bdf\Form\Child\ChildInterface; use Bdf\Form\Child\Http\HttpFieldPath; -use Bdf\Form\ElementInterface; use Bdf\Form\Error\FormError; use Bdf\Form\RootElementInterface; use Bdf\Form\Transformer\NullTransformer; @@ -17,9 +16,11 @@ use Bdf\Form\Util\ContainerTrait; use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\ValueValidatorInterface; -use Bdf\Form\View\ElementViewInterface; use Exception; use Iterator; +use Override; + +use function assert; /** * The base form element @@ -46,7 +47,7 @@ * $entity = $form->attach($entity)->value(); * * - * @template T + * @template T as array|object * @implements FormInterface */ final class Form implements FormInterface @@ -56,24 +57,24 @@ final class Form implements FormInterface /** * @var ValueValidatorInterface */ - private $validator; + private readonly ValueValidatorInterface $validator; /** * Transformer to view value * * @var TransformerInterface */ - private $transformer; + private readonly TransformerInterface $transformer; /** * @var ChildrenCollectionInterface */ - private $children; + private readonly ChildrenCollectionInterface $children; /** * @var ValueGeneratorInterface */ - private $generator; + private readonly ValueGeneratorInterface $generator; /** * Does the form is optional ? @@ -81,22 +82,10 @@ final class Form implements FormInterface * * @var bool */ - private $optional; - - /** - * @var RootElementInterface|null - */ - private $root; - - /** - * @var FormError - */ - private $error; - - /** - * @var bool - */ - private $valid = false; + private readonly bool $optional; + private ?RootElementInterface $root = null; + private FormError $error; + private bool $valid = false; /** * The generated value @@ -104,7 +93,7 @@ final class Form implements FormInterface * * @var T|null */ - private $value; + private array|object|null $value = null; /** * Does the form has been submitted ? @@ -112,7 +101,7 @@ final class Form implements FormInterface * * @var bool */ - private $submitted = false; + private bool $submitted = false; /** * Form constructor. @@ -127,17 +116,15 @@ public function __construct(ChildrenCollectionInterface $children, ?ValueValidat { $this->children = $children->duplicate($this); $this->validator = $validator ?? ConstraintValueValidator::empty(); - $this->transformer = $transformer ?: NullTransformer::instance(); + $this->transformer = $transformer ?? NullTransformer::instance(); $this->error = FormError::null(); /** @var ValueGeneratorInterface */ - $this->generator = $generator ?: new ValueGenerator(); + $this->generator = $generator ?? new ValueGenerator(); $this->optional = $optional; } - /** - * {@inheritdoc} - */ - public function submit($data): ElementInterface + #[Override] + public function submit(mixed $data): static { $this->valid = true; $this->value = null; @@ -154,10 +141,8 @@ public function submit($data): ElementInterface return $this; } - /** - * {@inheritdoc} - */ - public function patch($data): ElementInterface + #[Override] + public function patch(mixed $data): static { $this->valid = true; $this->value = null; @@ -176,39 +161,28 @@ public function patch($data): ElementInterface return $this; } - /** - * {@inheritdoc} - */ + #[Override] public function valid(): bool { return $this->valid; } - /** - * {@inheritdoc} - */ + #[Override] public function failed(): bool { return !$this->valid; } - /** - * {@inheritdoc} - */ + #[Override] public function error(?HttpFieldPath $field = null): FormError { return $field ? $this->error->withField($field) : $this->error; } - /** - * {@inheritdoc} - * - * @param T|null $entity - * @return $this - */ - public function import($entity): ElementInterface + #[Override] + public function import(mixed $entity): static { - if ($entity) { + if ($entity !== null && $entity !== []) { $this->generator->attach($entity); } @@ -223,10 +197,9 @@ public function import($entity): ElementInterface /** * {@inheritdoc} - * - * @return T */ - public function value() + #[Override] + public function value(): array|object|null { if ($this->value !== null) { return $this->value; @@ -236,19 +209,18 @@ public function value() return null; } - $this->value = $this->generator->generate($this); + $value = $this->generator->generate($this); foreach ($this->children->reverseIterator() as $child) { - $child->fill($this->value); + $child->fill($value); } - return $this->value; + /** @var T $value */ + return $this->value = $value; } - /** - * {@inheritdoc} - */ - public function httpValue() + #[Override] + public function httpValue(): mixed { $http = []; @@ -259,9 +231,7 @@ public function httpValue() return $this->transformer->transformToHttp($http, $this); } - /** - * {@inheritdoc} - */ + #[Override] public function root(): RootElementInterface { if ($container = $this->container()) { @@ -275,12 +245,8 @@ public function root(): RootElementInterface return $this->root = new RootForm($this); } - /** - * {@inheritdoc} - * - * @return FormView - */ - public function view(?HttpFieldPath $field = null): ElementViewInterface + #[Override] + public function view(?HttpFieldPath $field = null): FormView { $elements = []; @@ -291,57 +257,43 @@ public function view(?HttpFieldPath $field = null): ElementViewInterface return new FormView(self::class, $this->error->global(), $elements); } - /** - * {@inheritdoc} - */ + #[Override] public function getIterator(): Iterator { return $this->children->forwardIterator(); } - /** - * {@inheritdoc} - */ - public function offsetExists($offset): bool + #[Override] + public function offsetExists(mixed $offset): bool { return isset($this->children[$offset]); } - /** - * {@inheritdoc} - */ - public function offsetGet($offset): ChildInterface + #[Override] + public function offsetGet(mixed $offset): ChildInterface { + /** @var ChildInterface */ return $this->children[$offset]; } - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value): void + #[Override] + public function offsetSet(mixed $offset, mixed $value): void { throw new BadMethodCallException(__CLASS__.' is immutable'); } - /** - * {@inheritdoc} - */ - public function offsetUnset($offset): void + #[Override] + public function offsetUnset(mixed $offset): void { throw new BadMethodCallException(__CLASS__.' is immutable'); } - /** - * {@inheritdoc} - */ public function __clone() { $this->children = $this->children->duplicate($this); } - /** - * {@inheritdoc} - */ + #[Override] public function attach($entity): FormInterface { $this->generator->attach($entity); @@ -370,7 +322,7 @@ public function setRoot(RootElementInterface $root): void * * @return mixed The transformed value */ - private function transformHttpValue($data) + private function transformHttpValue(mixed $data): mixed { try { $data = $this->transformer->transformFromHttp($data, $this); @@ -395,7 +347,7 @@ private function transformHttpValue($data) * @param mixed $data Data to submit * @param string $method The submit method to call. Should be "submit" or "patch" */ - private function submitToChildrenAndValidate($data, string $method): void + private function submitToChildrenAndValidate(mixed $data, string $method): void { if (!$this->submitToChildren($data, $method)) { return; @@ -418,7 +370,7 @@ private function submitToChildrenAndValidate($data, string $method): void * * @return bool false on fail, or true on success */ - private function submitToChildren($data, string $method): bool + private function submitToChildren(mixed $data, string $method): bool { if (!$this->valid) { return false; @@ -450,7 +402,7 @@ private function submitToChildren($data, string $method): bool * * @return bool true if the form is optional and not submitted, false otherwise */ - private function handleOptional($data): bool + private function handleOptional(mixed $data): bool { if (!$this->optional || $data !== null && $data !== [] && $data !== '') { return false; diff --git a/src/Aggregate/FormBuilder.php b/src/Aggregate/FormBuilder.php index cf7688f..5c2a43b 100644 --- a/src/Aggregate/FormBuilder.php +++ b/src/Aggregate/FormBuilder.php @@ -36,6 +36,7 @@ use Bdf\Form\RootElementInterface; use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ValueValidatorInterface; +use Override; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -73,32 +74,16 @@ class FormBuilder extends AbstractElementBuilder implements FormBuilderInterface /** * @var array */ - private $children = []; + private array $children = []; /** * @var array */ - private $buttons = []; - - /** - * @var PropertyAccessorInterface|null - */ - private $propertyAccessor; - - /** - * @var ValidatorInterface|null - */ - private $validator; - - /** - * @var ValueGeneratorInterface|null - */ - private $generator; - - /** - * @var bool - */ - private $optional = false; + private array $buttons = []; + private ?PropertyAccessorInterface $propertyAccessor = null; + private ?ValidatorInterface $validator = null; + private ?ValueGeneratorInterface $generator = null; + private bool $optional = false; /** @@ -125,6 +110,7 @@ public function __construct(?RegistryInterface $registry = null) * @psalm-suppress LessSpecificReturnStatement * @psalm-suppress PropertyTypeCoercion */ + #[Override] public function add(string $name, string $element): ChildBuilderInterface { return $this->children[$name] = $this->registry()->childBuilder($element, $name); @@ -139,6 +125,7 @@ public function add(string $name, string $element): ChildBuilderInterface * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function any(string $name): ChildBuilderInterface { return $this->add($name, AnyElement::class); @@ -153,6 +140,7 @@ public function any(string $name): ChildBuilderInterface * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function string(string $name, ?string $default = null): ChildBuilderInterface { return $this->add($name, StringElement::class)->default($default); @@ -167,6 +155,7 @@ public function string(string $name, ?string $default = null): ChildBuilderInter * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function integer(string $name, ?int $default = null): ChildBuilderInterface { return $this->add($name, IntegerElement::class)->default($default); @@ -181,6 +170,7 @@ public function integer(string $name, ?int $default = null): ChildBuilderInterfa * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function float(string $name, ?float $default = null): ChildBuilderInterface { return $this->add($name, FloatElement::class)->default($default); @@ -195,6 +185,7 @@ public function float(string $name, ?float $default = null): ChildBuilderInterfa * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function boolean(string $name): ChildBuilderInterface { return $this->add($name, BooleanElement::class); @@ -209,6 +200,7 @@ public function boolean(string $name): ChildBuilderInterface * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function dateTime(string $name): ChildBuilderInterface { return $this->add($name, DateTimeElement::class); @@ -223,6 +215,7 @@ public function dateTime(string $name): ChildBuilderInterface * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function phone(string $name): ChildBuilderInterface { return $this->add($name, PhoneElement::class); @@ -237,6 +230,7 @@ public function phone(string $name): ChildBuilderInterface * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function csrf(string $name = '_token'): ChildBuilderInterface { return $this->add($name, CsrfElement::class); @@ -296,11 +290,12 @@ public function url(string $name): ChildBuilderInterface * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function embedded(string $name, ?callable $configurator = null): ChildBuilderInterface { $builder = $this->add($name, Form::class); - if ($configurator) { + if ($configurator !== null) { $configurator($builder); } @@ -318,6 +313,7 @@ public function embedded(string $name, ?callable $configurator = null): ChildBui * @psalm-suppress MoreSpecificReturnType * @psalm-suppress LessSpecificReturnStatement */ + #[Override] public function array(string $name, ?string $elementType = null, ?callable $elementConfigurator = null): ChildBuilderInterface { /** @var ChildBuilderInterface&ArrayChildBuilder $builder */ @@ -333,6 +329,7 @@ public function array(string $name, ?string $elementType = null, ?callable $elem /** * {@inheritdoc} */ + #[Override] public function submit(string $name): ButtonBuilderInterface { return $this->buttons[$name] = $this->registry()->buttonBuilder($name); @@ -341,6 +338,7 @@ public function submit(string $name): ButtonBuilderInterface /** * {@inheritdoc} */ + #[Override] public function propertyAccessor(PropertyAccessorInterface $propertyAccessor): FormBuilderInterface { $this->propertyAccessor = $propertyAccessor; @@ -351,6 +349,7 @@ public function propertyAccessor(PropertyAccessorInterface $propertyAccessor): F /** * {@inheritdoc} */ + #[Override] public function validator(ValidatorInterface $validator): FormBuilderInterface { $this->validator = $validator; @@ -361,6 +360,7 @@ public function validator(ValidatorInterface $validator): FormBuilderInterface /** * {@inheritdoc} */ + #[Override] public function generator(ValueGeneratorInterface $generator): FormBuilderInterface { $this->generator = $generator; @@ -371,6 +371,7 @@ public function generator(ValueGeneratorInterface $generator): FormBuilderInterf /** * {@inheritdoc} */ + #[Override] public function generates($entity): FormBuilderInterface { return $this->generator(new ValueGenerator($entity)); @@ -379,6 +380,7 @@ public function generates($entity): FormBuilderInterface /** * {@inheritdoc} */ + #[Override] public function optional(bool $flag = true): FormBuilderInterface { $this->optional = $flag; @@ -389,7 +391,8 @@ public function optional(bool $flag = true): FormBuilderInterface /** * {@inheritdoc} */ - final protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + final protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): Form { $children = new ChildrenCollection(); diff --git a/src/Aggregate/FormBuilderInterface.php b/src/Aggregate/FormBuilderInterface.php index fda02db..f05c52a 100644 --- a/src/Aggregate/FormBuilderInterface.php +++ b/src/Aggregate/FormBuilderInterface.php @@ -18,6 +18,7 @@ use Bdf\Form\Leaf\StringElementBuilder; use Bdf\Form\Phone\PhoneChildBuilder; use Bdf\Form\Phone\PhoneElementBuilder; +use Override; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -37,8 +38,6 @@ * * * @extends ElementBuilderInterface - * - * @method ChildBuilderInterface any(string $name) */ interface FormBuilderInterface extends ElementBuilderInterface { @@ -71,12 +70,11 @@ public function add(string $name, string $element): ChildBuilderInterface; * @param non-empty-string $name The child name * * @return ChildBuilder|AnyElementBuilder - * @return ChildBuilderInterface The child builder + * @psalm-return ChildBuilderInterface The child builder * * @since 1.5 - * @todo uncomment in 2.0 */ - //public function any(string $name): ChildBuilderInterface; + public function any(string $name): ChildBuilderInterface; /** * Add a new string element on the form @@ -305,7 +303,7 @@ public function generator(ValueGeneratorInterface $generator): FormBuilderInterf * @see ValueGenerator * @see ElementInterface::value() */ - public function generates($entity): FormBuilderInterface; + public function generates(mixed $entity): FormBuilderInterface; /** * Mark the form as optional @@ -335,5 +333,6 @@ public function optional(bool $flag = true): FormBuilderInterface; * * @return FormInterface */ + #[Override] public function buildElement(): ElementInterface; } diff --git a/src/Aggregate/FormInterface.php b/src/Aggregate/FormInterface.php index ae2b561..454c613 100644 --- a/src/Aggregate/FormInterface.php +++ b/src/Aggregate/FormInterface.php @@ -2,12 +2,14 @@ namespace Bdf\Form\Aggregate; +use Bdf\Form\Aggregate\View\FormView; +use Bdf\Form\Child\Http\HttpFieldPath; +use Override; + /** * The base form element type * - * @method \Bdf\Form\Aggregate\View\FormView view(?\Bdf\Form\Child\Http\HttpFieldPath $fieldPath = null) - * - * @template T + * @template T as array|object * @extends ChildAggregateInterface */ interface FormInterface extends ChildAggregateInterface @@ -31,11 +33,14 @@ interface FormInterface extends ChildAggregateInterface * $this->repository->save($form->value()); * * - * @param T|class-string|callable():T $entity The entity object, or class name + * @param T|class-string|callable():T $entity The entity object, or class name * * @return $this * * @see Form::import() For attach and extract values from properties */ - public function attach($entity): FormInterface; + public function attach(mixed $entity): FormInterface; + + #[Override] + public function view(?HttpFieldPath $field = null): FormView; } diff --git a/src/Aggregate/RootForm.php b/src/Aggregate/RootForm.php index 97e392b..ef9ab53 100644 --- a/src/Aggregate/RootForm.php +++ b/src/Aggregate/RootForm.php @@ -12,7 +12,9 @@ use Bdf\Form\Util\RootFlagsTrait; use Bdf\Form\View\ElementViewInterface; use Iterator; +use LogicException; use OutOfBoundsException; +use Override; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Validator\Constraint; @@ -20,6 +22,8 @@ use Symfony\Component\Validator\ValidatorBuilder; use WeakReference; +use function assert; + /** * Adapt a form element as root element * The root form handle constraint group, validator and property accessor instances, and submit button @@ -58,28 +62,15 @@ final class RootForm implements RootElementInterface, ChildAggregateInterface /** * @var WeakReference
*/ - private $form; + private readonly WeakReference $form; /** * @var array */ - private $buttons; - - /** - * @var ButtonInterface|null - */ - private $submitButton; - - /** - * @var PropertyAccessorInterface|null - */ - private $propertyAccessor; - - /** - * @var ValidatorInterface|null - */ - private $validator; - + private readonly array $buttons; + private ?ButtonInterface $submitButton = null; + private ?PropertyAccessorInterface $propertyAccessor = null; + private ?ValidatorInterface $validator = null; /** * RootForm constructor. @@ -97,57 +88,42 @@ public function __construct(Form $form, array $buttons = [], ?PropertyAccessorIn $this->validator = $validator; } - /** - * {@inheritdoc} - */ - public function submit($data): ElementInterface + #[Override] + public function submit(mixed $data): static { $this->submitToButtons($data); - /** @psalm-suppress PossiblyNullReference */ - $this->form->get()->submit($data); + $this->form->get()?->submit($data); return $this; } - /** - * {@inheritdoc} - */ - public function patch($data): ElementInterface + #[Override] + public function patch(mixed $data): static { $this->submitToButtons($data); - /** @psalm-suppress PossiblyNullReference */ - $this->form->get()->patch($data); + $this->form->get()?->patch($data); return $this; } - /** - * {@inheritdoc} - */ - public function import($entity): ElementInterface + #[Override] + public function import(mixed $entity): static { - /** @psalm-suppress PossiblyNullReference */ - $this->form->get()->import($entity); + $this->form->get()?->import($entity); return $this; } - /** - * {@inheritdoc} - */ - public function value() + #[Override] + public function value(): mixed { - /** @psalm-suppress PossiblyNullReference */ - return $this->form->get()->value(); + return $this->form->get()?->value(); } - /** - * {@inheritdoc} - */ - public function httpValue() + #[Override] + public function httpValue(): mixed { - /** @psalm-suppress PossiblyNullReference */ - $httpValue = $this->form->get()->httpValue(); + $httpValue = $this->form->get()?->httpValue(); if (empty($this->buttons)) { return $httpValue; @@ -162,60 +138,44 @@ public function httpValue() return $httpValue; } - /** - * {@inheritdoc} - */ + #[Override] public function valid(): bool { - /** @psalm-suppress PossiblyNullReference */ - return $this->form->get()->valid(); + return $this->form->get()?->valid() ?? false; } - /** - * {@inheritdoc} - */ + #[Override] public function failed(): bool { // Do not use $this->form->get()->failed() because it may be not implemented return !$this->valid(); } - /** - * {@inheritdoc} - */ + #[Override] public function error(?HttpFieldPath $field = null): FormError { - /** @psalm-suppress PossiblyNullReference */ - return $this->form->get()->error($field); + return $this->form->get()?->error($field) ?? throw new LogicException('Invalid reference'); } - /** - * {@inheritdoc} - */ + #[Override] public function container(): ?ChildInterface { return null; // root cannot have a container } - /** - * {@inheritdoc} - */ + #[Override] public function setContainer(ChildInterface $container): ElementInterface { throw new BadMethodCallException('Cannot wrap a root element into a container'); } - /** - * {@inheritdoc} - */ + #[Override] public function root(): RootElementInterface { return $this; } - /** - * {@inheritdoc} - */ + #[Override] public function view(?HttpFieldPath $field = null): ElementViewInterface { $buttons = []; @@ -224,24 +184,20 @@ public function view(?HttpFieldPath $field = null): ElementViewInterface $buttons[$button->name()] = $button->view($field); } - /** @psalm-suppress PossiblyNullReference */ - $view = $this->form->get()->view($field); + $view = $this->form->get()?->view($field); + assert($view !== null); $view->setButtons($buttons); return $view; } - /** - * {@inheritdoc} - */ + #[Override] public function submitButton(): ?ButtonInterface { return $this->submitButton; } - /** - * {@inheritdoc} - */ + #[Override] public function button(string $name): ButtonInterface { if ($btn = $this->buttons[$name] ?? null) { @@ -251,33 +207,23 @@ public function button(string $name): ButtonInterface throw new OutOfBoundsException("The button '{$name}' is not found"); } - /** - * {@inheritdoc} - */ + #[Override] public function getValidator(): ValidatorInterface { if ($this->validator === null) { - $this->validator = (new ValidatorBuilder())->getValidator(); + $this->validator = new ValidatorBuilder()->getValidator(); } return $this->validator; } - /** - * {@inheritdoc} - */ + #[Override] public function getPropertyAccessor(): PropertyAccessorInterface { - if ($this->propertyAccessor === null) { - $this->propertyAccessor = PropertyAccess::createPropertyAccessor(); - } - - return $this->propertyAccessor; + return $this->propertyAccessor ??= PropertyAccess::createPropertyAccessor(); } - /** - * {@inheritdoc} - */ + #[Override] public function constraintGroups(): array { if (!$button = $this->submitButton) { @@ -295,47 +241,37 @@ public function constraintGroups(): array * @psalm-suppress PossiblyNullArrayAccess * @psalm-suppress NullableReturnStatement */ - public function offsetGet($offset): ChildInterface + #[Override] + public function offsetGet(mixed $offset): ChildInterface { return $this->form->get()[$offset]; } - /** - * {@inheritdoc} - */ - public function offsetExists($offset): bool + #[Override] + public function offsetExists(mixed $offset): bool { - /** @psalm-suppress PossiblyNullReference */ return isset($this->form->get()[$offset]); } - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value): void + #[Override] + public function offsetSet(mixed $offset, mixed $value): void { /** @psalm-suppress PossiblyNullReference */ $this->form->get()[$offset] = $value; } - /** - * {@inheritdoc} - * - * @psalm-suppress PossiblyNullReference - * @psalm-suppress PossiblyNullArrayAccess - */ - public function offsetUnset($offset): void + #[Override] + public function offsetUnset(mixed $offset): void { - unset($this->form->get()[$offset]); + if ($form = $this->form->get()) { + unset($form[$offset]); + } } - /** - * {@inheritdoc} - */ + #[Override] public function getIterator(): Iterator { - /** @psalm-suppress PossiblyNullReference */ - return $this->form->get()->getIterator(); + return $this->form->get()?->getIterator() ?? throw new LogicException(); } /** @@ -343,7 +279,7 @@ public function getIterator(): Iterator * * @param mixed $data The HTTP value */ - private function submitToButtons($data): void + private function submitToButtons(mixed $data): void { $this->submitButton = null; diff --git a/src/Aggregate/Value/ValueGenerator.php b/src/Aggregate/Value/ValueGenerator.php index a281be1..1afedcb 100644 --- a/src/Aggregate/Value/ValueGenerator.php +++ b/src/Aggregate/Value/ValueGenerator.php @@ -3,6 +3,12 @@ namespace Bdf\Form\Aggregate\Value; use Bdf\Form\ElementInterface; +use Override; + +use function class_exists; +use function is_callable; +use function is_object; +use function is_string; /** * The base value generator implementation @@ -22,41 +28,37 @@ final class ValueGenerator implements ValueGeneratorInterface /** * @var callable():T|T|class-string */ - private $value; + private mixed $value; /** * @var callable():T|T|class-string|null */ - private $attachment; + private mixed $attachment = null; /** * ValueGenerator constructor. * * @param callable():T|T|class-string $value */ - public function __construct($value = []) + public function __construct(mixed $value = []) { /** @psalm-suppress PropertyTypeCoercion */ $this->value = $value; } - /** - * {@inheritdoc} - */ - public function attach($entity): void + #[Override] + public function attach(mixed $entity): void { /** @psalm-suppress PropertyTypeCoercion */ $this->attachment = $entity; } - /** - * {@inheritdoc} - */ - public function generate(ElementInterface $element) + #[Override] + public function generate(ElementInterface $element): mixed { $value = $this->attachment ?? $this->value; - if (is_string($value)) { + if (is_string($value) && class_exists($value)) { /** @var T */ return new $value; } @@ -66,10 +68,11 @@ public function generate(ElementInterface $element) } // Only clone value if it's not attached - if (!$this->attachment && is_object($value)) { + if ($this->attachment === null && is_object($value)) { return clone $value; } + /** @var T */ return $value; } } diff --git a/src/Aggregate/Value/ValueGeneratorInterface.php b/src/Aggregate/Value/ValueGeneratorInterface.php index f50d3cc..f436d2a 100644 --- a/src/Aggregate/Value/ValueGeneratorInterface.php +++ b/src/Aggregate/Value/ValueGeneratorInterface.php @@ -23,7 +23,7 @@ interface ValueGeneratorInterface * @param T|callable():T|class-string $entity * @see FormInterface::attach() */ - public function attach($entity): void; + public function attach(mixed $entity): void; /** * Generate the value @@ -34,5 +34,5 @@ public function attach($entity): void; * @return T * @see FormInterface::value() */ - public function generate(ElementInterface $element); + public function generate(ElementInterface $element): mixed; } diff --git a/src/Aggregate/View/ArrayElementView.php b/src/Aggregate/View/ArrayElementView.php index f42a6e6..3922d5a 100644 --- a/src/Aggregate/View/ArrayElementView.php +++ b/src/Aggregate/View/ArrayElementView.php @@ -13,6 +13,7 @@ use Bdf\Form\View\FieldViewTrait; use Countable; use IteratorAggregate; +use Override; /** * View object for the ArrayElement @@ -55,7 +56,7 @@ final class ArrayElementView implements IteratorAggregate, FieldViewInterface, F * @param array $constraints * @param ChoiceView[]|null $choices */ - public function __construct(string $type, string $name, $value, ?string $error, array $elements, bool $required, array $constraints, ?array $choices = []) + public function __construct(string $type, string $name, mixed $value, ?string $error, array $elements, bool $required, array $constraints, ?array $choices = []) { $this->type = $type; $this->name = $name; @@ -78,17 +79,13 @@ public function isCsv(): bool return is_scalar($this->value); } - /** - * {@inheritdoc} - */ + #[Override] public function count(): int { return count($this->elements); } - /** - * {@inheritdoc} - */ + #[Override] protected function defaultRenderer(): FieldViewRendererInterface { return ArrayElementViewRenderer::instance(); @@ -96,10 +93,8 @@ protected function defaultRenderer(): FieldViewRendererInterface /** * Ignore property "attributes" - * - * @return array */ - public function __sleep() + public function __sleep(): array { return ['type', 'name', 'error', 'value', 'elements', 'required', 'constraints', 'choices']; } diff --git a/src/Aggregate/View/ArrayElementViewRenderer.php b/src/Aggregate/View/ArrayElementViewRenderer.php index e79010e..11424ff 100644 --- a/src/Aggregate/View/ArrayElementViewRenderer.php +++ b/src/Aggregate/View/ArrayElementViewRenderer.php @@ -6,28 +6,17 @@ use Bdf\Form\Leaf\View\SimpleFieldHtmlRenderer; use Bdf\Form\View\FieldViewInterface; use Bdf\Form\View\FieldViewRendererInterface; +use Override; /** * Default renderer for @see ArrayElementView * * @implements FieldViewRendererInterface */ -final class ArrayElementViewRenderer implements FieldViewRendererInterface +final readonly class ArrayElementViewRenderer implements FieldViewRendererInterface { - /** - * @var ArrayElementViewRenderer|null - */ - private static $instance; - - /** - * @var FieldViewRendererInterface - */ - private $csvRenderer; - - /** - * @var FieldViewRendererInterface - */ - private $selectRenderer; + private FieldViewRendererInterface $csvRenderer; + private FieldViewRendererInterface $selectRenderer; /** * ArrayElementViewRenderer constructor. @@ -41,11 +30,7 @@ public function __construct(?FieldViewRendererInterface $csvRenderer = null, ?Fi $this->selectRenderer = $selectRenderer ?? SelectHtmlRenderer::instance(); } - /** - * {@inheritdoc} - * - * @param ArrayElementView $view - */ + #[Override] public function render(FieldViewInterface $view, array $attributes): string { if ($view->isCsv()) { @@ -62,10 +47,8 @@ public function render(FieldViewInterface $view, array $attributes): string */ public static function instance(): self { - if (self::$instance) { - return self::$instance; - } + static $instance = new self(); - return self::$instance = new self(); + return $instance; } } diff --git a/src/Aggregate/View/FormView.php b/src/Aggregate/View/FormView.php index 9cd3b7f..18ccdfe 100644 --- a/src/Aggregate/View/FormView.php +++ b/src/Aggregate/View/FormView.php @@ -11,6 +11,7 @@ use Bdf\Form\View\FieldSetViewTrait; use Bdf\Form\View\FieldViewInterface; use IteratorAggregate; +use Override; /** * View for a form element @@ -43,7 +44,7 @@ final class FormView implements IteratorAggregate, FieldSetViewInterface /** * @var ButtonViewInterface[] */ - private $buttons = []; + private array $buttons = []; /** * FormView constructor. @@ -59,21 +60,14 @@ public function __construct(string $type, ?string $error, array $elements) $this->elements = $elements; } - /** - * {@inheritdoc} - * - * @return ElementViewInterface|ButtonViewInterface - */ - #[\ReturnTypeWillChange] - public function offsetGet($offset) + #[Override] + public function offsetGet(mixed $offset): ElementViewInterface|ButtonViewInterface { return $this->elements[$offset] ?? $this->buttons[$offset]; } - /** - * {@inheritdoc} - */ - public function offsetExists($offset): bool + #[Override] + public function offsetExists(mixed $offset): bool { return isset($this->elements[$offset]) || isset($this->buttons[$offset]); } diff --git a/src/Button/ButtonBuilderInterface.php b/src/Button/ButtonBuilderInterface.php index 3748798..f79ee0d 100644 --- a/src/Button/ButtonBuilderInterface.php +++ b/src/Button/ButtonBuilderInterface.php @@ -15,7 +15,7 @@ interface ButtonBuilderInterface * * @return $this */ - public function value(string $value): ButtonBuilderInterface; + public function value(string $value): static; /** * Define the constraint groups to use when the button is clicked @@ -26,7 +26,7 @@ public function value(string $value): ButtonBuilderInterface; * * @see https://symfony.com/doc/current/validation/groups.html */ - public function groups(array $groups): ButtonBuilderInterface; + public function groups(array $groups): static; /** * Build the button element diff --git a/src/Button/ButtonInterface.php b/src/Button/ButtonInterface.php index 03621d4..1de9598 100644 --- a/src/Button/ButtonInterface.php +++ b/src/Button/ButtonInterface.php @@ -34,7 +34,7 @@ public function clicked(): bool; * * @return bool true is the button is clicked, or false */ - public function submit($data): bool; + public function submit(mixed $data): bool; /** * Get the http value of the button diff --git a/src/Button/SubmitButton.php b/src/Button/SubmitButton.php index aa77720..36d63ba 100644 --- a/src/Button/SubmitButton.php +++ b/src/Button/SubmitButton.php @@ -5,6 +5,7 @@ use Bdf\Form\Button\View\ButtonView; use Bdf\Form\Button\View\ButtonViewInterface; use Bdf\Form\Child\Http\HttpFieldPath; +use Override; /** * Simple button implementation @@ -12,26 +13,14 @@ */ final class SubmitButton implements ButtonInterface { - /** - * @var string - */ - private $name; - - /** - * @var string - */ - private $value; + private readonly string $name; + private readonly string $value; /** - * @var array + * @var string[] */ - private $groups; - - /** - * @var bool - */ - private $clicked = false; - + private readonly array $groups; + private bool $clicked = false; /** * SubmitButton constructor. @@ -47,41 +36,31 @@ public function __construct(string $name, string $value = 'ok', array $groups = $this->groups = $groups; } - /** - * {@inheritdoc} - */ + #[Override] public function name(): string { return $this->name; } - /** - * {@inheritdoc} - */ + #[Override] public function clicked(): bool { return $this->clicked; } - /** - * {@inheritdoc} - */ + #[Override] public function constraintGroups(): array { return $this->groups; } - /** - * {@inheritdoc} - */ + #[Override] public function submit($data): bool { return $this->clicked = isset($data[$this->name]) && (string) $data[$this->name] === $this->value; } - /** - * {@inheritdoc} - */ + #[Override] public function toHttp(): array { if (!$this->clicked) { @@ -91,9 +70,7 @@ public function toHttp(): array return [$this->name => $this->value]; } - /** - * {@inheritdoc} - */ + #[Override] public function view(?HttpFieldPath $parent = null): ButtonViewInterface { return new ButtonView($parent ? $parent->add($this->name)->get() : $this->name, $this->value, $this->clicked()); diff --git a/src/Button/SubmitButtonBuilder.php b/src/Button/SubmitButtonBuilder.php index c556571..cdc0dfb 100644 --- a/src/Button/SubmitButtonBuilder.php +++ b/src/Button/SubmitButtonBuilder.php @@ -3,6 +3,7 @@ namespace Bdf\Form\Button; use Bdf\Form\Aggregate\FormBuilderInterface; +use Override; /** * Builder for a submit button @@ -20,25 +21,22 @@ */ final class SubmitButtonBuilder implements ButtonBuilderInterface { - /** - * @var string - */ - private $name; + private readonly string $name; /** * @var class-string */ - private $buttonClass; + private string $buttonClass; /** * @var string */ - private $value = 'ok'; + private string $value = 'ok'; /** - * @var array + * @var string[] */ - private $groups = []; + private array $groups = []; /** @@ -53,29 +51,23 @@ public function __construct(string $name, string $buttonClass = SubmitButton::cl $this->buttonClass = $buttonClass; } - /** - * {@inheritdoc} - */ - public function value(string $value): ButtonBuilderInterface + #[Override] + public function value(string $value): static { $this->value = $value; return $this; } - /** - * {@inheritdoc} - */ - public function groups(array $groups): ButtonBuilderInterface + #[Override] + public function groups(array $groups): static { $this->groups = $groups; return $this; } - /** - * {@inheritdoc} - */ + #[Override] public function buildButton(): ButtonInterface { return new $this->buttonClass($this->name, $this->value, $this->groups); diff --git a/src/Button/View/ButtonView.php b/src/Button/View/ButtonView.php index d124980..efea048 100644 --- a/src/Button/View/ButtonView.php +++ b/src/Button/View/ButtonView.php @@ -3,6 +3,7 @@ namespace Bdf\Form\Button\View; use Bdf\Form\View\RenderableTrait; +use Override; /** * Base view object for buttons @@ -16,20 +17,9 @@ final class ButtonView implements ButtonViewInterface { use RenderableTrait; - /** - * @var string - */ - private $name; - - /** - * @var string - */ - private $value; - - /** - * @var bool - */ - private $clicked; + public private(set) string $name; + public private(set) string $value; + public private(set) bool $clicked; /** * ButtonView constructor. @@ -45,33 +35,25 @@ public function __construct(string $name, string $value, bool $clicked) $this->clicked = $clicked; } - /** - * {@inheritdoc} - */ + #[Override] public function name(): string { return $this->name; } - /** - * {@inheritdoc} - */ + #[Override] public function value(): string { return $this->value; } - /** - * {@inheritdoc} - */ + #[Override] public function clicked(): bool { return $this->clicked; } - /** - * {@inheritdoc} - */ + #[Override] public function render(?ButtonViewRendererInterface $renderer = null): string { return ($renderer ?? ButtonViewRenderer::instance())->render($this, $this->attributes); diff --git a/src/Button/View/ButtonViewInterface.php b/src/Button/View/ButtonViewInterface.php index c908ddb..d90d94b 100644 --- a/src/Button/View/ButtonViewInterface.php +++ b/src/Button/View/ButtonViewInterface.php @@ -43,5 +43,6 @@ public function clicked(): bool; * * @return string */ + #[\Override] public function render(?ButtonViewRendererInterface $renderer = null): string; } diff --git a/src/Button/View/ButtonViewRenderer.php b/src/Button/View/ButtonViewRenderer.php index 739b206..9bfa6da 100644 --- a/src/Button/View/ButtonViewRenderer.php +++ b/src/Button/View/ButtonViewRenderer.php @@ -3,6 +3,7 @@ namespace Bdf\Form\Button\View; use Bdf\Form\View\HtmlRenderer; +use Override; /** * Renderer for @see ButtonViewInterface @@ -14,9 +15,7 @@ final class ButtonViewRenderer implements ButtonViewRendererInterface */ private static $instance; - /** - * {@inheritdoc} - */ + #[Override] public function render(ButtonViewInterface $view, array $attributes): string { if (!isset($attributes['type'])) { diff --git a/src/Child/Child.php b/src/Child/Child.php index a980d95..0e4269b 100644 --- a/src/Child/Child.php +++ b/src/Child/Child.php @@ -15,62 +15,43 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Util\HttpValue; use Bdf\Form\View\ElementViewInterface; +use LogicException; +use Override; use WeakReference; +use function assert; + /** * Child which extract HTTP field value from a simple array access */ final class Child implements ChildInterface { - /** - * @var ElementInterface - */ - private $element; + private readonly ElementInterface $element; /** * @var WeakReference */ - private $parent; - - /** - * @var string - */ - private $name; + private ?WeakReference $parent = null; + private readonly string $name; /** * @var HttpFieldsInterface */ - private $fields; - - /** - * @var mixed - */ - private $defaultValue; + private readonly HttpFieldsInterface $fields; + private readonly mixed $defaultValue; /** * @var FilterInterface[] */ - private $filters; - - /** - * @var HydratorInterface|null - */ - private $hydrator; - - /** - * @var ExtractorInterface|null - */ - private $extractor; + private readonly array $filters; + private readonly ?HydratorInterface $hydrator; + private readonly ?ExtractorInterface $extractor; /** * @var string[] */ - private $dependencies; - - /** - * @var TransformerInterface - */ - private $transformer; + private readonly array $dependencies; + private readonly TransformerInterface $transformer; /** @@ -98,9 +79,7 @@ public function __construct(string $name, ElementInterface $element, ?HttpFields $this->transformer = $transformer ?? NullTransformer::instance(); } - /** - * {@inheritdoc} - */ + #[Override] public function element(): ElementInterface { return $this->element; @@ -108,19 +87,15 @@ public function element(): ElementInterface /** * {@inheritdoc} - * - * @psalm-suppress NullableReturnStatement - * @psalm-suppress InvalidNullableReturnType */ + #[Override] public function parent(): ChildAggregateInterface { - return $this->parent->get(); + return $this->parent?->get() ?? throw new LogicException('No parent has been set.'); } - /** - * {@inheritdoc} - */ - public function setParent(ChildAggregateInterface $parent): ChildInterface + #[Override] + public function setParent(ChildAggregateInterface $parent): static { if ($this->parent === null) { $this->parent = WeakReference::create($parent); @@ -133,33 +108,32 @@ public function setParent(ChildAggregateInterface $parent): ChildInterface return $child; } - /** - * {@inheritdoc} - */ + #[Override] public function name(): string { return $this->name; } - /** - * {@inheritdoc} - */ + #[Override] public function dependencies(): array { return $this->dependencies; } - /** - * {@inheritdoc} - */ - public function import($entity): void + #[Override] + public function import(array|object|null $entity): void { if (!$this->extractor) { return; } - /** @psalm-suppress PossiblyNullReference */ - $propertyAccessor = $this->parent->get()->root()->getPropertyAccessor(); + if ($entity === null) { + $this->element->import(null); + return; + } + + $propertyAccessor = $this->parent?->get()?->root()?->getPropertyAccessor(); + assert($propertyAccessor !== null); $this->extractor->setPropertyAccessor($propertyAccessor); $this->extractor->setFormElement($this); @@ -171,17 +145,15 @@ public function import($entity): void $this->element->import($value); } - /** - * {@inheritdoc} - */ - public function fill(&$entity): void + #[Override] + public function fill(array|object &$entity): void { if (!$this->hydrator) { return; } - /** @psalm-suppress PossiblyNullReference */ - $propertyAccessor = $this->parent->get()->root()->getPropertyAccessor(); + $propertyAccessor = $this->parent?->get()?->root()?->getPropertyAccessor(); + assert($propertyAccessor !== null); $this->hydrator->setPropertyAccessor($propertyAccessor); $this->hydrator->setFormElement($this); @@ -193,20 +165,16 @@ public function fill(&$entity): void $this->hydrator->setFormElement(null); } - /** - * {@inheritdoc} - */ - public function submit($data): bool + #[Override] + public function submit(mixed $data): bool { $value = $this->extractValue($data); return $this->element->submit($value)->valid(); } - /** - * {@inheritdoc} - */ - public function patch($data): bool + #[Override] + public function patch(mixed $data): bool { $value = $data !== null && $this->fields->contains($data) ? $this->extractValue($data) @@ -216,33 +184,24 @@ public function patch($data): bool return $this->element->patch($value)->valid(); } - /** - * {@inheritdoc} - */ + #[Override] public function httpFields(): array { return $this->fields->format($this->element->httpValue()); } - /** - * {@inheritdoc} - */ + #[Override] public function error(?HttpFieldPath $field = null): FormError { return $this->element->error($this->fields->get($field)); } - /** - * {@inheritdoc} - */ + #[Override] public function view(?HttpFieldPath $field = null): ElementViewInterface { return $this->element->view($this->fields->get($field)); } - /** - * {@inheritdoc} - */ public function __clone() { $this->element = $this->element->setContainer($this); @@ -254,7 +213,7 @@ public function __clone() * @param mixed $httpValue * @return mixed The filtered value */ - private function extractValue($httpValue) + private function extractValue(mixed $httpValue): mixed { $value = $this->fields->extract($httpValue); $default = $this->defaultValue; diff --git a/src/Child/ChildBuilder.php b/src/Child/ChildBuilder.php index 1bc9676..c378109 100644 --- a/src/Child/ChildBuilder.php +++ b/src/Child/ChildBuilder.php @@ -18,10 +18,9 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Util\MagicCallForwarding; use Bdf\Form\Util\TransformerBuilderTrait; -use Symfony\Component\Form\DataTransformerInterface; +use Override; use function is_callable; -use function trigger_error; /** * Base builder for a child @@ -40,7 +39,6 @@ * * @method $this satisfy($constraint, $options = null, bool $append = true) * @method $this value($value) - * @method $this transformer($transformer, bool $append = true) * @method $this required($options = null) * * @template B as ElementBuilderInterface @@ -53,75 +51,56 @@ class ChildBuilder implements ChildBuilderInterface transformer as modelTransformer; } - /** - * @var string - */ - private $name; - - /** - * @var RegistryInterface - */ - private $registry; + private readonly string $name; + private readonly RegistryInterface $registry; /** * The list of input dependencies * * @var string[] */ - private $viewDependencies = []; + private array $viewDependencies = []; + private mixed $default = null; /** - * @var mixed + * @var array */ - private $default; - - /** - * @var array - */ - private $filters = []; + private array $filters = []; /** * @var list */ - private $filtersProviders = []; + private array $filtersProviders = []; /** * @var class-string */ - private $childClassName = Child::class; + private string $childClassName = Child::class; /** * @var list */ - private $parametersConfigurators = []; + private array $parametersConfigurators = []; /** * @var HttpFieldsInterface|null */ - private $fields; + private ?HttpFieldsInterface $fields = null; /** * @var ElementBuilderInterface * @psalm-var B */ - private $elementBuilder; - - /** - * @var HydratorInterface|null - */ - private $hydrator; - - /** - * @var ExtractorInterface|null - */ - private $extractor; + private readonly ElementBuilderInterface $elementBuilder; + private ?HydratorInterface $hydrator = null; + private ?ExtractorInterface $extractor = null; /** * Add the trim filter * * @var bool */ - private $trim = true; + private bool $trim = true; /** @@ -135,42 +114,32 @@ public function __construct(string $name, ElementBuilderInterface $elementBuilde { $this->name = $name; $this->elementBuilder = $elementBuilder; - $this->registry = $registry ?: new Registry(); + $this->registry = $registry ?? new Registry(); } - /** - * {@inheritdoc} - */ - final public function hydrator(HydratorInterface $hydrator) + #[Override] + final public function hydrator(HydratorInterface $hydrator): static { $this->hydrator = $hydrator; return $this; } - /** - * {@inheritdoc} - */ - final public function extractor(ExtractorInterface $extractor) + #[Override] + final public function extractor(ExtractorInterface $extractor): static { $this->extractor = $extractor; return $this; } - /** - * {@inheritdoc} - */ - final public function filter($filter, bool $append = true) + #[Override] + final public function filter(FilterInterface|callable $filter, bool $append = true): static { if (is_callable($filter)) { $filter = new ClosureFilter($filter); } - if (!$filter instanceof FilterInterface) { - @trigger_error('Not passing a callable or a filter instance is deprecated since 1.7 and will be removed in 2.0.', E_USER_DEPRECATED); - } - if ($append === true) { $this->filters[] = $filter; } else { @@ -180,20 +149,16 @@ final public function filter($filter, bool $append = true) return $this; } - /** - * {@inheritdoc} - */ - final public function default($default) + #[Override] + final public function default(mixed $default): static { $this->default = $default; return $this; } - /** - * {@inheritdoc} - */ - final public function depends(string ...$inputNames) + #[Override] + final public function depends(string ...$inputNames): static { foreach ($inputNames as $inputName) { $this->viewDependencies[$inputName] = $inputName; @@ -202,19 +167,15 @@ final public function depends(string ...$inputNames) return $this; } - /** - * {@inheritdoc} - */ - public function addParametersConfigurator(callable $configurator) + #[Override] + public function addParametersConfigurator(callable $configurator): static { $this->parametersConfigurators[] = $configurator; return $this; } - /** - * {@inheritdoc} - */ + #[Override] final public function buildChild(): ChildInterface { $parameters = $this->buildParameters(); @@ -290,7 +251,7 @@ final public function buildChild(): ChildInterface * @see ChildBuilderInterface::extractor() * @see ChildInterface::import() */ - final public function getter($propertyName = null, ?callable $transformer = null, ?callable $customAccessor = null): self + final public function getter(string|callable|null $propertyName = null, ?callable $transformer = null, ?callable $customAccessor = null): static { return $this->extractor(new Getter($propertyName, $transformer, $customAccessor)); } @@ -341,7 +302,7 @@ final public function getter($propertyName = null, ?callable $transformer = null * @see ChildBuilderInterface::hydrator() * @see ChildInterface::fill() */ - final public function setter($propertyName = null, ?callable $transformer = null, ?callable $customAccessor = null): self + final public function setter(string|callable|null $propertyName = null, ?callable $transformer = null, ?callable $customAccessor = null): static { return $this->hydrator(new Setter($propertyName, $transformer, $customAccessor)); } @@ -369,7 +330,7 @@ final public function setter($propertyName = null, ?callable $transformer = null * * @since 1.5 */ - final public function getset(?string $propertyName = null): self + final public function getset(?string $propertyName = null): static { return $this->getter($propertyName)->setter($propertyName); } @@ -381,7 +342,7 @@ final public function getset(?string $propertyName = null): self * * @return $this */ - final public function childClassName(string $factory): self + final public function childClassName(string $factory): static { $this->childClassName = $factory; @@ -395,7 +356,7 @@ final public function childClassName(string $factory): self * * @return $this */ - final public function httpFields(HttpFieldsInterface $fields): self + final public function httpFields(HttpFieldsInterface $fields): static { $this->fields = $fields; @@ -428,7 +389,7 @@ final public function httpFields(HttpFieldsInterface $fields): self * * @see PrefixedHttpFields */ - final public function prefix(?string $prefix = null): self + final public function prefix(?string $prefix = null): static { return $this->httpFields(new PrefixedHttpFields($prefix ?? $this->name.'_')); } @@ -442,7 +403,7 @@ final public function prefix(?string $prefix = null): self * * @return $this */ - final public function trim(bool $active = true): self + final public function trim(bool $active = true): static { $this->trim = $active; @@ -454,7 +415,7 @@ final public function trim(bool $active = true): self * * * $builder->string('foo')->configure(function (StringElementBuilder $builder) { - * $builder->length(['min' => 3]); + * $builder->length(min: 3); * }); * * @@ -462,7 +423,7 @@ final public function trim(bool $active = true): self * * @return $this */ - final public function configure(callable $configurator): self + final public function configure(callable $configurator): static { $configurator($this->elementBuilder); @@ -472,13 +433,13 @@ final public function configure(callable $configurator): self /** * Forward call to element builder * - * @param callable|TransformerInterface|DataTransformerInterface $transformer + * @param callable|TransformerInterface $transformer * @param bool $append * @return $this * * @see ElementBuilderInterface::transformer() */ - public function transformer($transformer, bool $append = true) + public function transformer(callable|TransformerInterface $transformer, bool $append = true): static { $this->elementBuilder->transformer($transformer, $append); @@ -490,14 +451,13 @@ public function transformer($transformer, bool $append = true) * * @return B */ + #[Override] final protected function getElementBuilder(): ElementBuilderInterface { return $this->elementBuilder; } - /** - * {@inheritdoc} - */ + #[Override] final protected function registry(): RegistryInterface { return $this->registry; @@ -522,28 +482,24 @@ final protected function addFilterProvider(callable $provider): void */ private function buildParameters(): ChildParameters { - $parameters = new ChildParameters(); - - $parameters->name = $this->name; - $parameters->hydrator = $this->hydrator; - $parameters->extractor = $this->extractor; - $parameters->dependencies = $this->viewDependencies; - $parameters->modelTransformer = $this->buildTransformer(); - $parameters->className = $this->childClassName; + $parameters = new ChildParameters( + name: $this->name, + element: $this->elementBuilder->buildElement(), + fields: $this->fields ?: new ArrayOffsetHttpFields($this->name), + hydrator: $this->hydrator, + extractor: $this->extractor, + dependencies: $this->viewDependencies, + modelTransformer: $this->buildTransformer(), + className: $this->childClassName, + ); $parameters->filters = $this->trim ? [TrimFilter::instance()] : []; - - foreach ($this->filters as $filter) { - $parameters->filters[] = $this->registry->filter($filter); - } + $parameters->filters = [...$parameters->filters, ...$this->filters]; foreach ($this->filtersProviders as $provider) { - $parameters->filters = array_merge($parameters->filters, $provider($this->registry)); + $parameters->filters = [...$parameters->filters, ...$provider($this->registry)]; } - $parameters->fields = $this->fields ?: new ArrayOffsetHttpFields($this->name); - $parameters->element = $this->elementBuilder->buildElement(); - // Apply element transformation to the default value if ($this->default !== null) { $lastValue = $parameters->element->value(); diff --git a/src/Child/ChildBuilderInterface.php b/src/Child/ChildBuilderInterface.php index dc66dc2..746c503 100644 --- a/src/Child/ChildBuilderInterface.php +++ b/src/Child/ChildBuilderInterface.php @@ -28,7 +28,7 @@ interface ChildBuilderInterface * @see ChildInterface::fill() * @see ChildBuilder::setter() For define simple hydrator */ - public function hydrator(HydratorInterface $hydrator); + public function hydrator(HydratorInterface $hydrator): static; /** * Define the extractor for the child @@ -41,7 +41,7 @@ public function hydrator(HydratorInterface $hydrator); * @see ChildInterface::import() * @see ChildBuilder::getter() For define simple extractor */ - public function extractor(ExtractorInterface $extractor); + public function extractor(ExtractorInterface $extractor): static; /** * Add a filter @@ -67,7 +67,7 @@ public function extractor(ExtractorInterface $extractor); * * @see FilterInterface */ - public function filter($filter, bool $append = true); + public function filter(FilterInterface|callable $filter, bool $append = true): static; /** * Define the default value @@ -87,7 +87,7 @@ public function filter($filter, bool $append = true); * * @see ElementBuilderInterface::value() For set the initial value */ - public function default($default); + public function default($default): static; /** * Add a configurator for child parameters @@ -110,7 +110,7 @@ public function default($default); * * @return $this */ - public function addParametersConfigurator(callable $configurator); + public function addParametersConfigurator(callable $configurator): static; /** * Add an input dependency @@ -133,7 +133,7 @@ public function addParametersConfigurator(callable $configurator); * * @return $this */ - public function depends(string ...$inputNames); + public function depends(string ...$inputNames): static; /** * Add a model transformer @@ -160,14 +160,14 @@ public function depends(string ...$inputNames); * }); * * - * @param callable|TransformerInterface|DataTransformerInterface $transformer The transformer. Symfony transformer can be used + * @param callable|TransformerInterface $transformer The transformer * @param bool $append Append the transformer. Prepend if false * * @return $this * * @see TransformerInterface */ - public function modelTransformer($transformer, bool $append = true); + public function modelTransformer(callable|TransformerInterface $transformer, bool $append = true): static; /** * Creates the child instance diff --git a/src/Child/ChildCreationStrategyInterface.php b/src/Child/ChildCreationStrategyInterface.php index d4a60b9..3964a5a 100644 --- a/src/Child/ChildCreationStrategyInterface.php +++ b/src/Child/ChildCreationStrategyInterface.php @@ -29,5 +29,5 @@ interface ChildCreationStrategyInterface * * @return ChildInterface */ - public function __invoke(string $name, ElementInterface $element, HttpFieldsInterface $fields, array $filters, $defaultValue, ?HydratorInterface $hydrator, ?ExtractorInterface $extractor, array $dependencies, ?TransformerInterface $transformer): ChildInterface; + public function __invoke(string $name, ElementInterface $element, HttpFieldsInterface $fields, array $filters, mixed $defaultValue, ?HydratorInterface $hydrator, ?ExtractorInterface $extractor, array $dependencies, ?TransformerInterface $transformer): ChildInterface; } diff --git a/src/Child/ChildInterface.php b/src/Child/ChildInterface.php index c858670..f679ccb 100644 --- a/src/Child/ChildInterface.php +++ b/src/Child/ChildInterface.php @@ -49,7 +49,7 @@ public function parent(): ChildAggregateInterface; * * @return static The child instance linked with the parent */ - public function setParent(ChildAggregateInterface $parent): self; + public function setParent(ChildAggregateInterface $parent): static; /** * Get the element's name @@ -78,20 +78,20 @@ public function dependencies(): array; * Import values from the entity * Use a PropertyAccessor for extract related attributes * - * @param mixed $entity + * @param array|object|null $entity * * @see AccessorInterface */ - public function import($entity): void; + public function import(array|object|null $entity): void; /** * Hydrate entity with the child value * Use a PropertyAccessor for hydrate related attributes * This is the opposite operation of import() * - * @param mixed $entity + * @param array|object $entity */ - public function fill(&$entity): void; + public function fill(array|object &$entity): void; /** * Submit HTTP form data @@ -107,7 +107,7 @@ public function fill(&$entity): void; * * @see ElementInterface::submit() The element submit() method */ - public function submit($data): bool; + public function submit(mixed $data): bool; /** * Submit HTTP form data without override previous ones @@ -119,7 +119,7 @@ public function submit($data): bool; * * @see ElementInterface::patch() The element patch() method */ - public function patch($data): bool; + public function patch(mixed $data): bool; /** * Export the http value of the child as an array diff --git a/src/Child/ChildParameters.php b/src/Child/ChildParameters.php index 42c1572..83a44dd 100644 --- a/src/Child/ChildParameters.php +++ b/src/Child/ChildParameters.php @@ -14,89 +14,91 @@ */ final class ChildParameters { - /** - * The child name - * - * @var string - */ - public $name; + public function __construct( + /** + * The child name + * + * @var string + */ + public string $name, - /** - * The inner element instance - * - * @var ElementInterface - */ - public $element; + /** + * The inner element instance + * + * @var ElementInterface + */ + public ElementInterface $element, - /** - * Http Fields to use - * - * @var HttpFieldsInterface - */ - public $fields; + /** + * Http Fields to use + * + * @var HttpFieldsInterface + */ + public HttpFieldsInterface $fields, - /** - * @var FilterInterface[] - */ - public $filters; + /** + * @var HydratorInterface|null + */ + public ?HydratorInterface $hydrator, - /** - * @var mixed - */ - public $defaultValue; + /** + * @var ExtractorInterface|null + */ + public ?ExtractorInterface $extractor, - /** - * @var HydratorInterface|null - */ - public $hydrator; + /** + * Array of dependencies child names + * + * @var string[] + */ + public array $dependencies, - /** - * @var ExtractorInterface|null - */ - public $extractor; + /** + * @var TransformerInterface|null + */ + public ?TransformerInterface $modelTransformer, - /** - * Array of dependencies child names - * - * @var string[] - */ - public $dependencies; + /** + * The child class name + * + * @var class-string + */ + public string $className, - /** - * @var TransformerInterface|null - */ - public $modelTransformer; + /** + * @var mixed + */ + public mixed $defaultValue = null, - /** - * The child class name - * - * @var class-string - */ - public $className; + /** + * @var FilterInterface[] + */ + public array $filters = [], - /** - * The child instance - * Set a value to ignore the default child instantiation on the ChildBuilder - * - * @var ChildInterface|null - */ - public $child; + /** + * The child instance + * Set a value to ignore the default child instantiation on the ChildBuilder + * + * @var ChildInterface|null + */ + public ?ChildInterface $child = null, - /** - * List of child factories to apply - * The return value of each factories will fill the $this->child field - * - * This parameter can be used to decorate a child instance like : - * - * public function decorateChild(ChildParameters $parameters) - * { - * $parameters->factories[] = function (ChildParameters $parameters) { - * return new MyChildWrapper($parameters->child); - * }; - * } - * - * - * @var (callable(ChildParameters):ChildInterface)[] - */ - public $factories = []; + /** + * List of child factories to apply + * The return value of each factories will fill the $this->child field + * + * This parameter can be used to decorate a child instance like : + * + * public function decorateChild(ChildParameters $parameters) + * { + * $parameters->factories[] = function (ChildParameters $parameters) { + * return new MyChildWrapper($parameters->child); + * }; + * } + * + * + * @var (callable(ChildParameters):ChildInterface)[] + */ + public array $factories = [], + ) {} } diff --git a/src/Child/Http/ArrayOffsetHttpFields.php b/src/Child/Http/ArrayOffsetHttpFields.php index c60ab73..e2a6464 100644 --- a/src/Child/Http/ArrayOffsetHttpFields.php +++ b/src/Child/Http/ArrayOffsetHttpFields.php @@ -2,6 +2,10 @@ namespace Bdf\Form\Child\Http; +use Override; + +use function is_array; + /** * Extract HTTP fields value using a simple array offset * This is the default http fields implementation @@ -13,28 +17,17 @@ * $fields->extract(['other' => 'value'], 'not found'); // => 'not found' * */ -final class ArrayOffsetHttpFields implements HttpFieldsInterface +final readonly class ArrayOffsetHttpFields implements HttpFieldsInterface { - /** - * @var string - */ - private $offset; - - - /** - * ArrayOffsetHttpFields constructor. - * - * @param string $offset The field name - */ - public function __construct(string $offset) - { - $this->offset = $offset; - } - - /** - * {@inheritdoc} - */ - public function extract($httpFields) + public function __construct( + /** + * The HTTP field name + */ + private string $offset, + ) {} + + #[Override] + public function extract(mixed $httpFields): mixed { if (!is_array($httpFields) || !isset($httpFields[$this->offset])) { return null; @@ -43,25 +36,19 @@ public function extract($httpFields) return $httpFields[$this->offset]; } - /** - * {@inheritdoc} - */ + #[Override] public function contains($httpFields): bool { return isset($httpFields[$this->offset]); } - /** - * {@inheritdoc} - */ - public function format($value) + #[Override] + public function format(mixed $value): array { return [$this->offset => $value]; } - /** - * {@inheritdoc} - */ + #[Override] public function get(?HttpFieldPath $path = null): HttpFieldPath { return $path === null ? HttpFieldPath::named($this->offset) : $path->add($this->offset); diff --git a/src/Child/Http/HttpFieldPath.php b/src/Child/Http/HttpFieldPath.php index 5d76c3f..49b413f 100644 --- a/src/Child/Http/HttpFieldPath.php +++ b/src/Child/Http/HttpFieldPath.php @@ -2,6 +2,7 @@ namespace Bdf\Form\Child\Http; +use Override; use Stringable; /** @@ -19,25 +20,10 @@ */ final class HttpFieldPath implements Stringable { - /** - * @var HttpFieldPath|null - */ - private static $empty; - - /** - * @var string - */ - private $root = ''; - - /** - * @var string - */ - private $path = ''; - - /** - * @var string - */ - private $prefix = ''; + private static ?self $empty = null; + private string $root = ''; + private string $path = ''; + private string $prefix = ''; /** * HttpFieldPath constructor. @@ -93,6 +79,7 @@ public function add(string $name): self */ public function concat(HttpFieldPath $other): self { + // @todo use clone with + readonly when support of PHP < 8.5 will be dropped $newPath = clone $this; if ($other->root !== '') { @@ -173,6 +160,7 @@ public function isRootField(): bool * * @return string */ + #[Override] public function __toString(): string { return $this->get(); @@ -185,11 +173,7 @@ public function __toString(): string */ public static function empty(): HttpFieldPath { - if (self::$empty) { - return self::$empty; - } - - return self::$empty = new HttpFieldPath(); + return self::$empty ??= new HttpFieldPath(); } /** diff --git a/src/Child/Http/HttpFieldsInterface.php b/src/Child/Http/HttpFieldsInterface.php index 18fb1d3..0f87989 100644 --- a/src/Child/Http/HttpFieldsInterface.php +++ b/src/Child/Http/HttpFieldsInterface.php @@ -16,7 +16,7 @@ interface HttpFieldsInterface * * @return mixed */ - public function extract($httpFields); + public function extract(mixed $httpFields): mixed; /** * Does the required field is contained into given http fields @@ -26,7 +26,7 @@ public function extract($httpFields); * * @return bool true if the field is available */ - public function contains($httpFields): bool; + public function contains(mixed $httpFields): bool; /** * Format an element value to HTTP fields @@ -35,7 +35,7 @@ public function contains($httpFields): bool; * * @return mixed */ - public function format($value); + public function format(mixed $value): mixed; /** * Get the field path for the current child diff --git a/src/Child/Http/PrefixedHttpFields.php b/src/Child/Http/PrefixedHttpFields.php index b660b17..ad63685 100644 --- a/src/Child/Http/PrefixedHttpFields.php +++ b/src/Child/Http/PrefixedHttpFields.php @@ -2,6 +2,12 @@ namespace Bdf\Form\Child\Http; +use Override; + +use function str_starts_with; +use function strlen; +use function substr; + /** * Extract HTTP fields value prefixed by a given string * @@ -14,28 +20,17 @@ * $fields->extract(['other' => 'value'], ['not found']); // => ['not found'] * */ -final class PrefixedHttpFields implements HttpFieldsInterface +final readonly class PrefixedHttpFields implements HttpFieldsInterface { - /** - * @var string - */ - private $prefix; - - - /** - * ArrayOffsetHttpFields constructor. - * - * @param string $prefix The http fields prefix - */ - public function __construct(string $prefix) - { - $this->prefix = $prefix; - } + public function __construct( + /** + * The http fields prefix + */ + private string $prefix + ) {} - /** - * {@inheritdoc} - */ - public function extract($httpFields) + #[Override] + public function extract(mixed $httpFields): array { $data = (array) $httpFields; $prefixLen = strlen($this->prefix); @@ -55,31 +50,25 @@ public function extract($httpFields) return $value; } - /** - * {@inheritdoc} - */ + #[Override] public function contains($httpFields): bool { return true; // Always true ? } - /** - * {@inheritdoc} - */ - public function format($value) + #[Override] + public function format(mixed $value): array { $http = []; - foreach ($value as $field => $fieldValue) { + foreach ((array) $value as $field => $fieldValue) { $http[$this->prefix.$field] = $fieldValue; } return $http; } - /** - * {@inheritdoc} - */ + #[Override] public function get(?HttpFieldPath $path = null): HttpFieldPath { return $path === null ? HttpFieldPath::prefixed($this->prefix) : $path->prefix($this->prefix); diff --git a/src/Choice/ArrayChoice.php b/src/Choice/ArrayChoice.php index eb86a9d..bd8f760 100644 --- a/src/Choice/ArrayChoice.php +++ b/src/Choice/ArrayChoice.php @@ -2,45 +2,35 @@ namespace Bdf\Form\Choice; +use Override; + /** * Implementation of choice using an array * * @template T * @implements ChoiceInterface */ -final class ArrayChoice implements ChoiceInterface +final readonly class ArrayChoice implements ChoiceInterface { - /** - * The list of choices - * - * Key: should be the label of the choice - * Value: the value - * - * @var T[] - */ - private $choices; - - /** - * ArrayChoice constructor. - * - * @param T[] $choices The choices. To declare label, use associative array with key as label - */ - public function __construct(array $choices) - { - $this->choices = $choices; - } - - /** - * {@inheritdoc} - */ + public function __construct( + /** + * The list of choices + * + * Key: should be the label of the choice + * Value: the value + * + * @var T[] + */ + private array $choices, + ) {} + + #[Override] public function values(): array { return $this->choices; } - /** - * {@inheritdoc} - */ + #[Override] public function view(?callable $configuration = null): array { $view = []; @@ -48,7 +38,7 @@ public function view(?callable $configuration = null): array foreach ($this->choices as $label => $value) { $view[] = $choice = new ChoiceView($value, $label); - if ($configuration) { + if ($configuration !== null) { $configuration($choice); } } diff --git a/src/Choice/ChoiceBuilderTrait.php b/src/Choice/ChoiceBuilderTrait.php index 87b6b52..3551251 100644 --- a/src/Choice/ChoiceBuilderTrait.php +++ b/src/Choice/ChoiceBuilderTrait.php @@ -3,12 +3,10 @@ namespace Bdf\Form\Choice; use Bdf\Form\ElementBuilderInterface; -use ReflectionClass; +use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Choice as ChoiceConstraint; use function is_array; -use function sprintf; -use function trigger_error; /** * Trait for configure choices on an element @@ -18,7 +16,7 @@ trait ChoiceBuilderTrait /** * @var ChoiceInterface|null */ - private $choices; + protected private(set) ?ChoiceInterface $choices = null; /** * Define choices for the element @@ -45,43 +43,34 @@ trait ChoiceBuilderTrait * * * @param ChoiceInterface|array|callable $choices The allowed values in PHP form. - * @param null|string|array $message The error message. + * @param string|null $message The error message. + * @param non-negative-int|null $min + * @param positive-int|null $max * * @return $this * @see ChoiceConstraint */ - final public function choices($choices, $message = null, ?bool $multiple = null, ?bool $strict = null, ?int $min = null, ?int $max = null, ?string $minMessage = null, ?string $maxMessage = null): self + final public function choices(ChoiceInterface|array|callable $choices, ?string $message = null, ?bool $multiple = null, ?bool $strict = null, ?int $min = null, ?int $max = null, ?string $minMessage = null, ?string $maxMessage = null): static { if (!$choices instanceof ChoiceInterface) { $choices = is_array($choices) ? new ArrayChoice($choices) : new LazyChoice($choices); } - if (is_array($message)) { - @trigger_error(sprintf('Passing an array of options on %s is deprecated since 1.7 and will be removed on 2.0, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - - $multiple = $multiple ?? $message['multiple'] ?? null; - $strict = $strict ?? $message['strict'] ?? null; - $min = $min ?? $message['min'] ?? null; - $max = $max ?? $message['max'] ?? null; - $minMessage = $minMessage ?? $message['minMessage'] ?? null; - $maxMessage = $maxMessage ?? $message['maxMessage'] ?? null; - $message = $message['message'] ?? null; - } - - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(ChoiceConstraint::class))->getConstructor()->getNumberOfParameters() === 1; - } - - $callback = [$choices, 'values']; - + $callback = $choices->values(...); $this->choices = $choices; - return $this->satisfy($isSf4 - ? new ChoiceConstraint(['callback' => $callback, 'message' => $message, 'multipleMessage' => $message, 'multiple' => $multiple, 'strict' => $strict, 'min' => $min, 'max' => $max, 'minMessage' => $minMessage, 'maxMessage' => $maxMessage]) - : new ChoiceConstraint([], null, $callback, $multiple, $strict, $min, $max, $message, $message, $minMessage, $maxMessage) + return $this->satisfy( + new ChoiceConstraint( + callback: $callback, + multiple: $multiple, + strict: $strict, + min: $min, + max: $max, + message: $message, + multipleMessage: $message, + minMessage: $minMessage, + maxMessage: $maxMessage + ) ); } @@ -101,5 +90,5 @@ final protected function getChoices(): ?ChoiceInterface * * @see ElementBuilderInterface::satisfy() */ - abstract public function satisfy($constraint, $options = null, bool $append = true); + abstract public function satisfy(Constraint|callable $constraint, ?string $message = null, bool $append = true): static; } diff --git a/src/Choice/ChoiceView.php b/src/Choice/ChoiceView.php index bb92a4b..adce368 100644 --- a/src/Choice/ChoiceView.php +++ b/src/Choice/ChoiceView.php @@ -18,24 +18,18 @@ class ChoiceView { /** * The label displayed to humans. - * - * @var string */ - private $label; + public string $label; /** * The view representation of the choice. - * - * @var mixed */ - private $value; + public mixed $value; /** * The view representation of the choice. - * - * @var boolean */ - private $selected; + public bool $selected; /** * Creates a new choice view. @@ -44,7 +38,7 @@ class ChoiceView * @param string|int $label The label displayed to humans * @param boolean $selected This choice is selected */ - public function __construct($value, $label, bool $selected = false) + public function __construct(mixed $value, int|string $label, bool $selected = false) { $this->value = $value; $this->selected = $selected; diff --git a/src/Choice/LazyChoice.php b/src/Choice/LazyChoice.php index a6f2ea5..3ececca 100644 --- a/src/Choice/LazyChoice.php +++ b/src/Choice/LazyChoice.php @@ -2,6 +2,8 @@ namespace Bdf\Form\Choice; +use Override; + /** * Proxy choice using a callback for generate the choices array * @@ -10,21 +12,21 @@ * * @final */ -/*final*/ class LazyChoice implements ChoiceInterface +final class LazyChoice implements ChoiceInterface { /** * The lazy callback with returns choices * * @var callable():(T[]|ChoiceInterface) */ - private $resolver; + private readonly mixed $resolver; /** * The choice object * * @var ChoiceInterface|null */ - private $choices; + private ?ChoiceInterface $choices = null; /** * LazyChoice constructor. @@ -36,17 +38,13 @@ public function __construct(callable $resolver) $this->resolver = $resolver; } - /** - * {@inheritdoc} - */ + #[Override] public function values(): array { return $this->build()->values(); } - /** - * {@inheritdoc} - */ + #[Override] public function view(?callable $configuration = null): array { return $this->build()->view($configuration); diff --git a/src/Choice/LazzyChoice.php b/src/Choice/LazzyChoice.php deleted file mode 100644 index e822d33..0000000 --- a/src/Choice/LazzyChoice.php +++ /dev/null @@ -1,13 +0,0 @@ -callback = $callback; @@ -68,12 +55,4 @@ public function __construct($callback, ?string $message = null) $this->message = $message; } } - - /** - * {@inheritdoc} - */ - public function getDefaultOption(): ?string - { - return 'callback'; - } } diff --git a/src/Constraint/ClosureValidator.php b/src/Constraint/ClosureValidator.php index 7bad05a..345b1d2 100755 --- a/src/Constraint/ClosureValidator.php +++ b/src/Constraint/ClosureValidator.php @@ -2,6 +2,7 @@ namespace Bdf\Form\Constraint; +use Override; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -12,10 +13,8 @@ */ class ClosureValidator extends ConstraintValidator { - /** - * {@inheritdoc} - */ - public function validate($value, Constraint $constraint): void + #[Override] + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof Closure) { throw new UnexpectedTypeException($constraint, Closure::class); @@ -40,9 +39,9 @@ public function validate($value, Constraint $constraint): void $error = $error['message'] ?? null; } - $this->context->buildViolation($error ?: $constraint->message) + $this->context->buildViolation($error ?? $constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) - ->setCode($code ?: 'CUSTOM_ERROR') + ->setCode($code ?? 'CUSTOM_ERROR') ->addViolation() ; } diff --git a/src/Constraint/FieldComparisonTrait.php b/src/Constraint/FieldComparisonTrait.php index 49fe85a..0092b27 100644 --- a/src/Constraint/FieldComparisonTrait.php +++ b/src/Constraint/FieldComparisonTrait.php @@ -5,10 +5,6 @@ use Bdf\Form\Util\FieldPath; use Symfony\Component\Validator\Constraint; -use function is_array; -use function sprintf; -use function trigger_error; - /** * Add field option on comparison class * The class must extends a subclass of AbstractComparison @@ -20,31 +16,16 @@ trait FieldComparisonTrait * * @var string|FieldPath */ - public $field; + public string|FieldPath $field; /** * FieldComparisonTrait constructor. - * @param string|FieldPath|array $field + * @param string|FieldPath $field */ - public function __construct($field) + public function __construct(string|FieldPath $field) { - if (is_array($field)) { - @trigger_error(sprintf('Passing an array of options to "%s" is deprecated since version 1.2 and will not be supported in 2.0. Use named arguments instead.', static::class), E_USER_DEPRECATED); - - $options = $field; - $field = $options['field'] ?? null; - } - - Constraint::__construct($options ?? null); - - $this->field = $field ?? $this->field; - } + Constraint::__construct(); - /** - * {@inheritdoc} - */ - public function getDefaultOption(): ?string - { - return 'field'; + $this->field = $field; } } diff --git a/src/Constraint/FieldComparisonValidatorTrait.php b/src/Constraint/FieldComparisonValidatorTrait.php index 44f3815..a3a388e 100644 --- a/src/Constraint/FieldComparisonValidatorTrait.php +++ b/src/Constraint/FieldComparisonValidatorTrait.php @@ -20,7 +20,7 @@ trait FieldComparisonValidatorTrait * * @return void */ - public function validate($value, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { /** @var FieldComparisonTrait $constraint */ /** @var ElementInterface $element */ diff --git a/src/Constraint/GreaterThanFieldValidator.php b/src/Constraint/GreaterThanFieldValidator.php index 72bdc73..e915d34 100644 --- a/src/Constraint/GreaterThanFieldValidator.php +++ b/src/Constraint/GreaterThanFieldValidator.php @@ -6,6 +6,8 @@ /** * Validator for @see GreaterThanField + * + * @psalm-suppress PropertyNotSetInConstructor */ class GreaterThanFieldValidator extends GreaterThanValidator { diff --git a/src/Constraint/GreaterThanOrEqualFieldValidator.php b/src/Constraint/GreaterThanOrEqualFieldValidator.php index f5f6c50..3da246c 100644 --- a/src/Constraint/GreaterThanOrEqualFieldValidator.php +++ b/src/Constraint/GreaterThanOrEqualFieldValidator.php @@ -6,6 +6,8 @@ /** * Validator for @see GreaterThanOrEqualField + * + * @psalm-suppress PropertyNotSetInConstructor */ class GreaterThanOrEqualFieldValidator extends GreaterThanOrEqualValidator { diff --git a/src/Constraint/LessThanFieldValidator.php b/src/Constraint/LessThanFieldValidator.php index 5ac3e97..9c50310 100644 --- a/src/Constraint/LessThanFieldValidator.php +++ b/src/Constraint/LessThanFieldValidator.php @@ -6,6 +6,8 @@ /** * Validator for @see LessThanField + * + * @psalm-suppress PropertyNotSetInConstructor */ class LessThanFieldValidator extends LessThanValidator { diff --git a/src/Constraint/LessThanOrEqualFieldValidator.php b/src/Constraint/LessThanOrEqualFieldValidator.php index 72e66d5..55c90ae 100644 --- a/src/Constraint/LessThanOrEqualFieldValidator.php +++ b/src/Constraint/LessThanOrEqualFieldValidator.php @@ -6,6 +6,8 @@ /** * Validator for @see LessThanOrEqualField + * + * @psalm-suppress PropertyNotSetInConstructor */ class LessThanOrEqualFieldValidator extends LessThanOrEqualValidator { diff --git a/src/Csrf/CsrfConstraint.php b/src/Csrf/CsrfConstraint.php index f775277..751bbaf 100644 --- a/src/Csrf/CsrfConstraint.php +++ b/src/Csrf/CsrfConstraint.php @@ -5,52 +5,28 @@ use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Validator\Constraint; -use function is_array; -use function sprintf; -use function trigger_error; - /** * @internal */ class CsrfConstraint extends Constraint { - const INVALID_TOKEN_ERROR = 'cd108896-d12a-4455-a6cc-ba13708c8e7f'; + public const string INVALID_TOKEN_ERROR = 'cd108896-d12a-4455-a6cc-ba13708c8e7f'; - protected const ERROR_NAMES = [self::INVALID_TOKEN_ERROR => 'INVALID_TOKEN_ERROR']; - protected static $errorNames = self::ERROR_NAMES; + protected const array ERROR_NAMES = [self::INVALID_TOKEN_ERROR => 'INVALID_TOKEN_ERROR']; /** * The constraint message * * @var string */ - public $message = 'The CSRF token is invalid.'; + public string $message = 'The CSRF token is invalid.'; + public CsrfTokenManagerInterface $manager; - /** - * @var CsrfTokenManagerInterface - */ - public $manager; - - public function __construct($manager = null, ?string $message = null) + public function __construct(CsrfTokenManagerInterface $manager, ?string $message = null) { - if (is_array($manager)) { - @trigger_error(sprintf('Passing an array of options on %s is deprecated since 1.7 and will be removed on 2.0, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - $options = $manager; - $manager = $options['manager'] ?? null; - $message = $message ?? $options['message'] ?? null; - } - - parent::__construct($options ?? null); + parent::__construct(); - $this->manager = $manager ?? $this->manager; + $this->manager = $manager; $this->message = $message ?? $this->message; } - - /** - * {@inheritdoc} - */ - public function getDefaultOption(): ?string - { - return 'manager'; - } } diff --git a/src/Csrf/CsrfConstraintValidator.php b/src/Csrf/CsrfConstraintValidator.php index 59bfaf6..f4406ed 100644 --- a/src/Csrf/CsrfConstraintValidator.php +++ b/src/Csrf/CsrfConstraintValidator.php @@ -2,6 +2,7 @@ namespace Bdf\Form\Csrf; +use Override; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -12,10 +13,8 @@ */ class CsrfConstraintValidator extends ConstraintValidator { - /** - * {@inheritdoc} - */ - public function validate($value, Constraint $constraint): void + #[Override] + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof CsrfConstraint) { throw new UnexpectedTypeException($constraint, CsrfConstraint::class); diff --git a/src/Csrf/CsrfElement.php b/src/Csrf/CsrfElement.php index 11e11d0..e0b2a5d 100644 --- a/src/Csrf/CsrfElement.php +++ b/src/Csrf/CsrfElement.php @@ -11,6 +11,7 @@ use Bdf\Form\RootElementInterface; use Bdf\Form\Util\ContainerTrait; use Bdf\Form\View\ElementViewInterface; +use Override; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManager; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; @@ -31,30 +32,11 @@ final class CsrfElement implements ElementInterface { use ContainerTrait; - /** - * @var string - */ - private $tokenId; - - /** - * @var CsrfValueValidator - */ - private $validator; - - /** - * @var CsrfTokenManagerInterface - */ - private $tokenManager; - - /** - * @var CsrfToken|null - */ - private $value = null; - - /** - * @var FormError - */ - private $error; + private readonly string $tokenId; + private readonly CsrfValueValidator $validator; + private readonly CsrfTokenManagerInterface $tokenManager; + private ?CsrfToken $value = null; + private FormError $error; /** * CsrfElement constructor. @@ -65,17 +47,15 @@ final class CsrfElement implements ElementInterface */ public function __construct(?string $tokenId = null, ?CsrfValueValidator $validator = null, ?CsrfTokenManagerInterface $tokenManager = null) { - $this->tokenId = $tokenId ?: self::class; - $this->validator = $validator ?: new CsrfValueValidator(); - $this->tokenManager = $tokenManager ?: new CsrfTokenManager(); + $this->tokenId = $tokenId ?? self::class; + $this->validator = $validator ?? new CsrfValueValidator(); + $this->tokenManager = $tokenManager ?? new CsrfTokenManager(); $this->error = FormError::null(); } - /** - * {@inheritdoc} - */ - public function submit($data): ElementInterface + #[Override] + public function submit(mixed $data): static { $this->value = new CsrfToken($this->tokenId, $data); $this->error = $this->validator->validate($this->value, $this); @@ -83,28 +63,20 @@ public function submit($data): ElementInterface return $this; } - /** - * {@inheritdoc} - */ - public function patch($data): ElementInterface + #[Override] + public function patch(mixed $data): static { // CSRF element must be submitted return $this->submit($data); } - /** - * {@inheritdoc} - */ - public function import($entity): ElementInterface + #[Override] + public function import(mixed $entity): static { throw new BadMethodCallException('Cannot set a Csrf token value'); } - /** - * {@inheritdoc} - * - * @return CsrfToken - */ + #[Override] public function value(): CsrfToken { if ($this->value) { @@ -114,49 +86,37 @@ public function value(): CsrfToken return $this->value = $this->tokenManager->getToken($this->tokenId); } - /** - * {@inheritdoc} - */ + #[Override] public function httpValue(): string { return $this->value()->getValue(); } - /** - * {@inheritdoc} - */ + #[Override] public function valid(): bool { return $this->value && $this->error->empty(); } - /** - * {@inheritdoc} - */ + #[Override] public function failed(): bool { return !$this->valid(); } - /** - * {@inheritdoc} - */ + #[Override] public function error(?HttpFieldPath $field = null): FormError { return $field ? $this->error->withField($field) : $this->error; } - /** - * {@inheritdoc} - */ + #[Override] public function root(): RootElementInterface { return ($container = $this->container()) ? $container->parent()->root() : new LeafRootElement($this); } - /** - * {@inheritdoc} - */ + #[Override] public function view(?HttpFieldPath $field = null): ElementViewInterface { return new SimpleElementView( diff --git a/src/Csrf/CsrfElementBuilder.php b/src/Csrf/CsrfElementBuilder.php index eaba81d..1285127 100644 --- a/src/Csrf/CsrfElementBuilder.php +++ b/src/Csrf/CsrfElementBuilder.php @@ -6,8 +6,11 @@ use Bdf\Form\Aggregate\FormBuilderInterface; use Bdf\Form\ElementBuilderInterface; use Bdf\Form\ElementInterface; +use Bdf\Form\Transformer\TransformerInterface; use LogicException; +use Override; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Validator\Constraint; /** * Builder for a CsrfElement @@ -29,33 +32,16 @@ */ class CsrfElementBuilder implements ElementBuilderInterface { - /** - * @var string - */ - private $tokenId = CsrfElement::class; - - /** - * @var string|null - */ - private $message = null; - - /** - * @var bool - */ - private $invalidate = false; - - /** - * @var CsrfTokenManagerInterface - */ - private $tokenManager; + private string $tokenId = CsrfElement::class; + private ?string $message = null; + private bool $invalidate = false; + private ?CsrfTokenManagerInterface $tokenManager = null; /** * Only validate the csrf token if the element is on the root form * If false, all csrf tokens on sub forms will be validated - * - * @var bool */ - private $onlyValidateRoot = true; + private bool $onlyValidateRoot = true; /** * CsrfElementBuilder constructor. @@ -143,18 +129,14 @@ public function validateOnSubForms(bool $validateOnSubForms = true): self return $this; } - /** - * {@inheritdoc} - */ - public function satisfy($constraint, $options = null, bool $append = true) + #[Override] + public function satisfy(Constraint|callable $constraint, ?string $message = null, bool $append = true): static { throw new BadMethodCallException(); } - /** - * {@inheritdoc} - */ - public function transformer($transformer, bool $append = true) + #[Override] + public function transformer(callable|TransformerInterface $transformer, bool $append = true): static { throw new BadMethodCallException(); } @@ -162,7 +144,8 @@ public function transformer($transformer, bool $append = true) /** * {@inheritdoc} */ - public function value($value) + #[Override] + public function value(mixed $value): static { throw new BadMethodCallException(); } @@ -170,6 +153,7 @@ public function value($value) /** * {@inheritdoc} */ + #[Override] public function buildElement(): ElementInterface { return new CsrfElement( diff --git a/src/Csrf/CsrfValueValidator.php b/src/Csrf/CsrfValueValidator.php index 7f74685..88726f8 100644 --- a/src/Csrf/CsrfValueValidator.php +++ b/src/Csrf/CsrfValueValidator.php @@ -9,17 +9,17 @@ use Bdf\Form\Validator\ValueValidatorInterface; use Exception; -use function is_array; +use Override; + +use function assert; use function method_exists; -use function sprintf; -use function trigger_error; /** * Class CsrfValueValidator * * @implements ValueValidatorInterface<\Symfony\Component\Security\Csrf\CsrfToken> */ -final class CsrfValueValidator implements ValueValidatorInterface +final readonly class CsrfValueValidator implements ValueValidatorInterface { /** * Flag for disable the CSRF validation @@ -34,41 +34,29 @@ final class CsrfValueValidator implements ValueValidatorInterface /** * Invalidate the token after verification ? - * - * @var boolean */ - private $invalidate; + private bool $invalidate; /** * The error message - * - * @var string|null */ - private $message; + private ?string $message; /** * Only validate the csrf token if the element is on the root form * If false, all csrf tokens on sub forms will be validated - * - * @var bool */ - private $onlyValidateRoot; + private bool $onlyValidateRoot; /** * CsrfValueValidator constructor. * * @param bool $invalidate Always invalidate the token after validation - * @param array|string|null $message The error message + * @param string|null $message The error message * @param bool $onlyValidateRoot Only validate the csrf token if the element is on the root form */ - public function __construct(bool $invalidate = false, $message = null, bool $onlyValidateRoot = false) + public function __construct(bool $invalidate = false, ?string $message = null, bool $onlyValidateRoot = false) { - if (is_array($message)) { - @trigger_error(sprintf('Passing an array of options on %s is deprecated since 1.7 and will be removed on 2.0, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - - $message = $message['message'] ?? null; - } - $this->invalidate = $invalidate; $this->message = $message; $this->onlyValidateRoot = $onlyValidateRoot; @@ -76,15 +64,15 @@ public function __construct(bool $invalidate = false, $message = null, bool $onl /** * {@inheritdoc} - * - * @param CsrfElement $element - * @psalm-suppress MoreSpecificImplementedParamType */ - public function validate($value, ElementInterface $element): FormError + #[Override] + public function validate(mixed $value, ElementInterface $element): FormError { + assert($element instanceof CsrfElement); + $root = $element->root(); - if (method_exists($root, 'is') && $root->is(self::FLAG_DISABLE_CSRF_VALIDATION)) { + if ($root->is(self::FLAG_DISABLE_CSRF_VALIDATION)) { return FormError::null(); } @@ -93,7 +81,7 @@ public function validate($value, ElementInterface $element): FormError } try { - return (new ConstraintValueValidator([new CsrfConstraint($element->getTokenManager(), $this->message)]))->validate($value, $element); + return new ConstraintValueValidator([new CsrfConstraint($element->getTokenManager(), $this->message)])->validate($value, $element); } finally { if ($this->invalidate) { $element->invalidateToken(); @@ -101,26 +89,20 @@ public function validate($value, ElementInterface $element): FormError } } - /** - * {@inheritdoc} - */ + #[Override] public function onTransformerException(Exception $exception, $value, ElementInterface $element): FormError { // Ignore transformer exception: the CSRF token will be validated after return FormError::null(); } - /** - * {@inheritdoc} - */ + #[Override] public function constraints(): array { return []; // Does CsrfConstraint should be returns ? } - /** - * {@inheritdoc} - */ + #[Override] public function hasConstraints(): bool { return true; diff --git a/src/Custom/CustomForm.php b/src/Custom/CustomForm.php index af7916d..798432d 100644 --- a/src/Custom/CustomForm.php +++ b/src/Custom/CustomForm.php @@ -14,6 +14,7 @@ use Bdf\Form\RootElementInterface; use Bdf\Form\View\ElementViewInterface; use Iterator; +use Override; use WeakReference; use function method_exists; @@ -47,37 +48,34 @@ * * * @todo implements root form interface ? - * @template T + * @template T as array|object * @implements FormInterface */ abstract class CustomForm implements FormInterface { - /** - * @var FormBuilderInterface - */ - private $builder; + private readonly FormBuilderInterface $builder; /** * The inner form * * @var FormInterface|null */ - private $form; + private ?FormInterface $form = null; /** * @var WeakReference|null */ - private $container; + private ?WeakReference $container = null; /** * @var list */ - private $preConfigureHooks = []; + private array $preConfigureHooks = []; /** * @var list): void> */ - private $postConfigureHooks = []; + private array $postConfigureHooks = []; /** * CustomForm constructor. @@ -89,128 +87,98 @@ public function __construct(?FormBuilderInterface $builder = null) $this->builder = $builder ?? new FormBuilder(); } - /** - * {@inheritdoc} - */ - public function offsetGet($offset): ChildInterface + #[Override] + public function offsetGet(mixed $offset): ChildInterface { return $this->form()[$offset]; } - /** - * {@inheritdoc} - */ - public function offsetExists($offset): bool + #[Override] + public function offsetExists(mixed $offset): bool { return isset($this->form()[$offset]); } - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value): void + #[Override] + public function offsetSet(mixed $offset, mixed $value): void { $this->form()[$offset] = $value; } - /** - * {@inheritdoc} - */ - public function offsetUnset($offset): void + #[Override] + public function offsetUnset(mixed $offset): void { unset($this->form()[$offset]); } - /** - * {@inheritdoc} - */ + #[Override] public function getIterator(): Iterator { return $this->form()->getIterator(); } - /** - * {@inheritdoc} - */ - public function submit($data): ElementInterface + #[Override] + public function submit(mixed $data): static { $this->submitTarget()->submit($data); return $this; } - /** - * {@inheritdoc} - */ - public function patch($data): ElementInterface + #[Override] + public function patch(mixed $data): static { $this->submitTarget()->patch($data); return $this; } - /** - * {@inheritdoc} - */ - public function import($entity): ElementInterface + #[Override] + public function import($entity): static { $this->form()->import($entity); return $this; } - /** - * {@inheritdoc} - */ - public function value() + #[Override] + public function value(): array|object|null { return $this->form()->value(); } - /** - * {@inheritdoc} - */ - public function httpValue() + #[Override] + public function httpValue(): mixed { return $this->form()->httpValue(); } - /** - * {@inheritdoc} - */ + #[Override] public function valid(): bool { return $this->form()->valid(); } - /** - * {@inheritdoc} - */ + #[Override] public function failed(): bool { // Do not use $this->form()->failed() because it may be not implemented return !$this->valid(); } - /** - * {@inheritdoc} - */ + #[Override] public function error(?HttpFieldPath $field = null): FormError { return $this->form()->error($field); } - /** - * {@inheritdoc} - */ + #[Override] public function container(): ?ChildInterface { - return $this->container ? $this->container->get() : null; + return $this->container?->get(); } - /** - * {@inheritdoc} - */ + #[Override] public function setContainer(ChildInterface $container): ElementInterface { $form = clone $this; @@ -220,29 +188,23 @@ public function setContainer(ChildInterface $container): ElementInterface return $form; } - /** - * {@inheritdoc} - */ + #[Override] public function root(): RootElementInterface { // @todo bad root form ? return $this->form()->root(); } - /** - * {@inheritdoc} - */ - public function attach($entity): FormInterface + #[Override] + public function attach(mixed $entity): FormInterface { $this->form()->attach($entity); return $this; } - /** - * {@inheritdoc} - */ - public function view(?HttpFieldPath $field = null): ElementViewInterface + #[Override] + public function view(?HttpFieldPath $field = null): FormView { $form = $this->form(); /** @var FormView $view */ @@ -322,11 +284,7 @@ final public function setPostConfigureHooks(array $hooks): void */ final public function disableCsrfValidation(): void { - $root = $this->root(); - - if (method_exists($root, 'set')) { - $root->set(CsrfValueValidator::FLAG_DISABLE_CSRF_VALIDATION, true); - } + $this->root()->set(CsrfValueValidator::FLAG_DISABLE_CSRF_VALIDATION, true); } /** @@ -343,8 +301,6 @@ final protected function form(): FormInterface // Form can be rebuilt, so we need to clone the builder to avoid side effects $builder = clone $this->builder; - /** @var static $this Psalm cannot infer this type */ - foreach ($this->preConfigureHooks as $hook) { $hook($this, $builder); } @@ -352,6 +308,7 @@ final protected function form(): FormInterface /** @psalm-suppress ArgumentTypeCoercion */ $this->configure($builder); + /** @var FormInterface $form */ $form = $builder->buildElement(); if ($this->container && $container = $this->container->get()) { diff --git a/src/Custom/CustomFormBuilder.php b/src/Custom/CustomFormBuilder.php index 7ae5ad4..9b16422 100644 --- a/src/Custom/CustomFormBuilder.php +++ b/src/Custom/CustomFormBuilder.php @@ -6,9 +6,9 @@ use Bdf\Form\Aggregate\FormBuilderInterface; use Bdf\Form\Aggregate\FormInterface; use Bdf\Form\ElementBuilderInterface; -use Bdf\Form\ElementInterface; use Bdf\Form\Registry\RegistryInterface; use Bdf\Form\Util\DelegateElementBuilderTrait; +use Override; use function is_string; @@ -33,25 +33,22 @@ class CustomFormBuilder implements ElementBuilderInterface { use DelegateElementBuilderTrait; - /** - * @var FormBuilderInterface - */ - private $builder; + private readonly FormBuilderInterface $builder; /** * @var class-string|callable(FormBuilderInterface):CustomForm */ - private $formFactory; + private readonly mixed $formFactory; /** * @var list */ - private $preConfigureHooks = []; + private array $preConfigureHooks = []; /** * @var list */ - private $postConfigureHooks = []; + private array $postConfigureHooks = []; /** @@ -66,17 +63,14 @@ public function __construct($formFactory, ?FormBuilderInterface $builder = null) $this->builder = $builder ?: new FormBuilder(); } - /** - * {@inheritdoc} - * - * @return CustomForm - */ - public function buildElement(): ElementInterface + #[Override] + public function buildElement(): CustomForm { if (is_string($this->formFactory)) { /** @var class-string $className */ $className = $this->formFactory; + /** @psalm-suppress UnsafeInstantiation */ $form = new $className($this->builder); } else { $form = ($this->formFactory)($this->builder); @@ -128,9 +122,7 @@ public function postConfigure(callable $hook): self return $this; } - /** - * {@inheritdoc} - */ + #[Override] protected function getElementBuilder(): ElementBuilderInterface { return $this->builder; diff --git a/src/ElementBuilderInterface.php b/src/ElementBuilderInterface.php index e6540f1..de3cd55 100644 --- a/src/ElementBuilderInterface.php +++ b/src/ElementBuilderInterface.php @@ -22,17 +22,12 @@ interface ElementBuilderInterface * * Prototypes: * function satisfy(Constraint $constraint, null, bool $append = true) - Add a constraint object. The 2nd parameter is ignored - * function satisfy(string $constraintClassName, ?array $options = null, bool $append = true) - Equivalent to satisfy(new $constraintClassName($options), null, $append) - * function satisfy(string $constraintClassName, ?string $errorMessage = null, bool $append = true) - Equivalent to satisfy(new $constraintClassName(['message' => $errorMessage]), null, $append) - * function satisfy(callable $inlineConstraint, null, bool $append = true) - Create a new constraint using a callback. The 2nd parameter is ignored + * function satisfy(callable $inlineConstraint, ?string $message = null, bool $append = true) - Create a new constraint using a callback. The 2nd parameter is the error message * * Usage: * * $builder->satisfy(new MyConstraint()); // Add a constraint * $builder->satisfy(new MyConstraint(), null, false); // Prepend the constraint (it will be validated first) - * $builder->satisfy(MyConstraint::class); // Use class name (will be used by the registry to make the constraint) - * $builder->satisfy(MyConstraint::class, ['foo' => 'bar']); // Same as above, but with options - * $builder->satisfy(MyConstraint::class, 'my error'); // Same as above, but with the option "message" defined to "my error" * * // Register a custom constraint * // Take the value as first parameter, and the input element as second @@ -53,14 +48,14 @@ interface ElementBuilderInterface * // You can also return a simple boolean * $builder->satisfy(function ($value, ElementInterface $input) { * return is_valid($value); - * }); + * }, 'my error'); * * // Use method reference also works, but the method must be public * $builder->satisfy([$this, 'checkElement']); * * - * @param Constraint|string|callable $constraint The constraint - * @param array|string|null $options Constraint options if a class name is given as first parameter. If a string is given, it will be used as message option + * @param Constraint|callable $constraint The constraint + * @param string|null $message The error message if the first parameter is a callable. * @param bool $append Append the validator. Prepend if false * * @return $this @@ -68,7 +63,7 @@ interface ElementBuilderInterface * @see RegistryInterface::constraint() For make the constraint * @see Closure When use callback as first parameter */ - public function satisfy($constraint, $options = null, bool $append = true); + public function satisfy(Constraint|callable $constraint, ?string $message = null, bool $append = true): static; /** * Add a view transformer @@ -95,14 +90,14 @@ public function satisfy($constraint, $options = null, bool $append = true); * }); * * - * @param callable|TransformerInterface|DataTransformerInterface $transformer The transformer. Symfony transformer can be used + * @param callable|TransformerInterface $transformer The transformer. * @param bool $append Append the transformer. Prepend if false * * @return $this * * @see TransformerInterface */ - public function transformer($transformer, bool $append = true); + public function transformer(callable|TransformerInterface $transformer, bool $append = true): static; /** * Define the initial value of the element @@ -117,7 +112,7 @@ public function transformer($transformer, bool $append = true); * * @see ChildBuilderInterface::default() For setting the default value */ - public function value($value); + public function value(mixed $value): static; /** * Build the element diff --git a/src/ElementInterface.php b/src/ElementInterface.php index 4194dd9..035eaca 100644 --- a/src/ElementInterface.php +++ b/src/ElementInterface.php @@ -20,8 +20,6 @@ * * * @template T - * - * @method bool failed() */ interface ElementInterface { @@ -47,7 +45,7 @@ interface ElementInterface * @see ElementInterface::valid() For validates the element after submition * @see ElementInterface::patch() For submit value without overrides previous ones */ - public function submit($data): self; + public function submit(mixed $data): static; /** * Submit HTTP data without override previous ones @@ -70,7 +68,7 @@ public function submit($data): self; * * @see ElementInterface::submit() For submit data with override old values */ - public function patch($data): self; + public function patch(mixed $data): static; /** * Set the PHP value of the element @@ -89,7 +87,7 @@ public function patch($data): self; * * @see ElementInterface::submit() For import HTTP data */ - public function import($entity): self; + public function import(mixed $entity): static; /** * Get the element's value @@ -100,7 +98,7 @@ public function import($entity): self; * * @see ElementInterface::httpValue() For get the raw HTTP value */ - public function value(); + public function value(): mixed; /** * Get the raw HTTP value @@ -112,7 +110,7 @@ public function value(); * * @see ElementInterface::value() For get the PHP value */ - public function httpValue(); + public function httpValue(): mixed; /** * Validates the element value @@ -138,9 +136,8 @@ public function valid(): bool; * * @see ElementInterface::error() To get error * @since 1.5 - * @todo uncomment in 2.0 */ - //public function failed(): bool; + public function failed(): bool; /** * Get the errors related to the element diff --git a/src/Error/FormError.php b/src/Error/FormError.php index 6ea6909..7d87408 100644 --- a/src/Error/FormError.php +++ b/src/Error/FormError.php @@ -6,6 +6,7 @@ use Bdf\Form\Child\Http\HttpFieldPath; use Bdf\Form\ElementInterface; use InvalidArgumentException; +use Override; use Stringable; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationInterface; @@ -26,22 +27,22 @@ final class FormError implements Stringable /** * @var HttpFieldPath|null */ - private $field; + private ?HttpFieldPath $field = null; /** * @var string|null */ - private $global; + private ?string $global; /** * @var string|null */ - private $code; + private ?string $code; /** * @var FormError[] */ - private $children; + private array $children; /** @@ -111,7 +112,7 @@ public function children(): array */ public function empty(): bool { - return empty($this->global) && empty($this->code) && empty($this->children); + return $this->global === null && $this->code === null && $this->children === []; } /** @@ -129,7 +130,7 @@ public function get(string $child): ?string { $child = $this->children[$child] ?? null; - return $child ? $child->global : null; + return $child?->global; } /** @@ -159,12 +160,12 @@ public function toArray(): array { $errors = []; - if ($this->global) { + if ($this->global !== null) { $errors[0] = $this->global; } foreach ($this->children as $name => $child) { - if ($child->global) { + if ($child->global !== null) { $errors[$name] = $child->global; } else { $errors[$name] = $child->toArray(); @@ -181,17 +182,17 @@ public function toArray(): array * * @return mixed The printer result */ - public function print(FormErrorPrinterInterface $printer) + public function print(FormErrorPrinterInterface $printer): mixed { - if ($this->field) { + if ($this->field !== null) { $printer->field($this->field); } - if ($this->global) { + if ($this->global !== null) { $printer->global($this->global); } - if ($this->code) { + if ($this->code !== null) { $printer->code($this->code); } @@ -207,6 +208,7 @@ public function print(FormErrorPrinterInterface $printer) * * @return string */ + #[Override] public function __toString(): string { return $this->print(new StringErrorPrinter()); @@ -285,7 +287,7 @@ public static function violation(ConstraintViolationInterface $violation): FormE $message = (string) $violation->getMessage(); $code = $violation->getCode(); - if ($code !== null && $violation instanceof ConstraintViolation && ($constraint = $violation->getConstraint()) !== null) { + if ($code !== null && ($constraint = $violation->getConstraint()) !== null) { try { $code = $constraint->getErrorName($code); } catch (InvalidArgumentException $e) { diff --git a/src/Error/FormErrorPrinterInterface.php b/src/Error/FormErrorPrinterInterface.php index 3ec6bb7..31f3806 100644 --- a/src/Error/FormErrorPrinterInterface.php +++ b/src/Error/FormErrorPrinterInterface.php @@ -55,5 +55,5 @@ public function child(string $name, FormError $error): void; * * @return mixed */ - public function print(); + public function print(): mixed; } diff --git a/src/Error/ImplodeErrorPrinter.php b/src/Error/ImplodeErrorPrinter.php index 98810e8..e31e6a8 100644 --- a/src/Error/ImplodeErrorPrinter.php +++ b/src/Error/ImplodeErrorPrinter.php @@ -3,6 +3,7 @@ namespace Bdf\Form\Error; use Bdf\Form\Child\Http\HttpFieldPath; +use Override; /** * Implode all errors into a string @@ -15,14 +16,14 @@ final class ImplodeErrorPrinter implements FormErrorPrinterInterface * * @var string */ - private $separator; + private readonly string $separator; /** * Lines of errors * * @var string[] */ - private $lines = []; + private array $lines = []; /** * Does the printer is visiting a child ? @@ -30,7 +31,7 @@ final class ImplodeErrorPrinter implements FormErrorPrinterInterface * * @var bool */ - private $inChild = false; + private bool $inChild = false; /** @@ -43,33 +44,25 @@ public function __construct(string $separator = PHP_EOL) $this->separator = $separator; } - /** - * {@inheritdoc} - */ + #[Override] public function field(HttpFieldPath $field): void { // Ignore field name } - /** - * {@inheritdoc} - */ + #[Override] public function global(string $error): void { $this->lines[] = $error; } - /** - * {@inheritdoc} - */ + #[Override] public function code(string $code): void { // Ignore code } - /** - * {@inheritdoc} - */ + #[Override] public function child(string $name, FormError $error): void { $this->inChild = true; @@ -77,10 +70,8 @@ public function child(string $name, FormError $error): void $this->inChild = false; } - /** - * {@inheritdoc} - */ - public function print() + #[Override] + public function print(): ?string { return $this->inChild ? null : implode($this->separator, $this->lines); } diff --git a/src/Error/StringErrorPrinter.php b/src/Error/StringErrorPrinter.php index c522a2c..0fc9438 100644 --- a/src/Error/StringErrorPrinter.php +++ b/src/Error/StringErrorPrinter.php @@ -3,69 +3,39 @@ namespace Bdf\Form\Error; use Bdf\Form\Child\Http\HttpFieldPath; +use Override; /** * Format errors as a string */ final class StringErrorPrinter implements FormErrorPrinterInterface { - /** - * @var string - */ - private $lineSeparator = PHP_EOL; - - /** - * @var string - */ - private $indentString = ' '; - - /** - * @var string - */ - private $nameSeparator = ' : '; - - /** - * @var int - */ - private $maxDepth = PHP_INT_MAX; - - /** - * @var integer - */ - private $depth = 0; - - /** - * @var string - */ - private $output = ''; - - /** - * {@inheritdoc} - */ + private string $lineSeparator = PHP_EOL; + private string $indentString = ' '; + private string $nameSeparator = ' : '; + private int $maxDepth = PHP_INT_MAX; + private int $depth = 0; + private string $output = ''; + + #[Override] public function field(HttpFieldPath $field): void { // Ignore the field name } - /** - * {@inheritdoc} - */ + #[Override] public function global(string $error): void { $this->output .= $error; } - /** - * {@inheritdoc} - */ + #[Override] public function code(string $code): void { // Ignore code } - /** - * {@inheritdoc} - */ + #[Override] public function child(string $name, FormError $error): void { if ($this->maxDepth <= $this->depth) { @@ -83,10 +53,8 @@ public function child(string $name, FormError $error): void --$this->depth; } - /** - * {@inheritdoc} - */ - public function print() + #[Override] + public function print(): string { return $this->output; } diff --git a/src/Filter/ClosureFilter.php b/src/Filter/ClosureFilter.php index ad30011..362263c 100755 --- a/src/Filter/ClosureFilter.php +++ b/src/Filter/ClosureFilter.php @@ -4,6 +4,7 @@ use Bdf\Form\Child\ChildBuilderInterface; use Bdf\Form\Child\ChildInterface; +use Override; /** * Adapt a simple callback to FilterInterface @@ -17,13 +18,12 @@ * * @see ChildBuilderInterface::filter() */ -final class ClosureFilter implements FilterInterface +final readonly class ClosureFilter implements FilterInterface { /** * @var callable(mixed, ChildInterface, mixed):mixed */ - protected $callback; - + private mixed $callback; /** * @param callable(mixed, ChildInterface, mixed):mixed $callback @@ -33,10 +33,8 @@ public function __construct(callable $callback) $this->callback = $callback; } - /** - * {@inheritdoc} - */ - public function filter($value, ChildInterface $input, $default) + #[Override] + public function filter(mixed $value, ChildInterface $input, mixed $default): mixed { return ($this->callback)($value, $input, $default); } diff --git a/src/Filter/EmptyArrayValuesFilter.php b/src/Filter/EmptyArrayValuesFilter.php index 3910b7a..74bf378 100644 --- a/src/Filter/EmptyArrayValuesFilter.php +++ b/src/Filter/EmptyArrayValuesFilter.php @@ -5,6 +5,7 @@ use Attribute; use Bdf\Form\Aggregate\ArrayChildBuilder; use Bdf\Form\Child\ChildInterface; +use Override; /** * Filter empty values from an array @@ -14,15 +15,10 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class EmptyArrayValuesFilter implements FilterInterface { - /** - * @var EmptyArrayValuesFilter - */ - private static $instance; + private static ?self $instance = null; - /** - * {@inheritdoc} - */ - public function filter($value, ChildInterface $input, $default) + #[Override] + public function filter(mixed $value, ChildInterface $input, mixed $default): mixed { if (!is_array($value)) { return $value; @@ -44,10 +40,6 @@ public function filter($value, ChildInterface $input, $default) */ public static function instance(): self { - if (self::$instance) { - return self::$instance; - } - - return self::$instance = new self; + return self::$instance ??= new self; } } diff --git a/src/Filter/FilterInterface.php b/src/Filter/FilterInterface.php index d14f532..19ff769 100755 --- a/src/Filter/FilterInterface.php +++ b/src/Filter/FilterInterface.php @@ -22,5 +22,5 @@ interface FilterInterface * * @return mixed Returns the filtered value */ - public function filter($value, ChildInterface $input, $default); + public function filter(mixed $value, ChildInterface $input, mixed $default)/*: mixed*/; } diff --git a/src/Filter/FilterVar.php b/src/Filter/FilterVar.php index f2d37fc..73779d3 100644 --- a/src/Filter/FilterVar.php +++ b/src/Filter/FilterVar.php @@ -5,6 +5,8 @@ use Attribute; use Bdf\Form\Child\ChildInterface; +use Override; + use function filter_var; use function is_array; use function is_scalar; @@ -18,7 +20,7 @@ * @see filter_var() */ #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] -final class FilterVar implements FilterInterface +final readonly class FilterVar implements FilterInterface { const HTML_FILTER = -1; @@ -27,14 +29,14 @@ final class FilterVar implements FilterInterface * * @var int */ - private $filter; + private int $filter; /** * The flag option of the filter given to filter_var * * @var int */ - private $flags; + private int $flags; /** * FilterVar constructor. @@ -48,10 +50,8 @@ public function __construct(int $filter = self::HTML_FILTER, int $flags = FILTER $this->flags = $flags; } - /** - * {@inheritdoc} - */ - public function filter($value, ChildInterface $input, $default) + #[Override] + public function filter(mixed $value, ChildInterface $input, mixed $default): mixed { if (!is_array($value)) { return $this->apply($value); @@ -64,7 +64,7 @@ public function filter($value, ChildInterface $input, $default) return $value; } - private function apply($value) + private function apply(mixed $value): mixed { $value = is_scalar($value) ? (string) $value : ''; diff --git a/src/Filter/TrimFilter.php b/src/Filter/TrimFilter.php index e1d9865..6d052c2 100644 --- a/src/Filter/TrimFilter.php +++ b/src/Filter/TrimFilter.php @@ -5,6 +5,11 @@ use Attribute; use Bdf\Form\Child\ChildBuilder; use Bdf\Form\Child\ChildInterface; +use Override; + +use function is_string; +use function preg_replace; +use function trim; /** * Perform a trim on the input value @@ -15,15 +20,10 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class TrimFilter implements FilterInterface { - /** - * @var self - */ - private static $instance; + private static ?self $instance = null; - /** - * {@inheritdoc} - */ - public function filter($value, ChildInterface $input, $default) + #[Override] + public function filter(mixed $value, ChildInterface $input, mixed $default): mixed { if (!is_string($value)) { return $value; @@ -44,10 +44,6 @@ public function filter($value, ChildInterface $input, $default) */ public static function instance(): self { - if (self::$instance) { - return self::$instance; - } - - return self::$instance = new self; + return self::$instance ??= new self; } } diff --git a/src/Leaf/AbstractBooleanElement.php b/src/Leaf/AbstractBooleanElement.php index 1bab391..fcc1fef 100644 --- a/src/Leaf/AbstractBooleanElement.php +++ b/src/Leaf/AbstractBooleanElement.php @@ -2,6 +2,7 @@ namespace Bdf\Form\Leaf; +use Override; use TypeError; use function is_scalar; @@ -16,12 +17,8 @@ */ abstract class AbstractBooleanElement extends LeafElement { - /** - * {@inheritdoc} - * - * @return bool|null - */ - final protected function tryCast($value): ?bool + #[Override] + final protected function tryCast(mixed $value): ?bool { if ($value === null) { return null; diff --git a/src/Leaf/AnyElement.php b/src/Leaf/AnyElement.php index 31accc0..5ec8a55 100644 --- a/src/Leaf/AnyElement.php +++ b/src/Leaf/AnyElement.php @@ -2,6 +2,8 @@ namespace Bdf\Form\Leaf; +use Override; + /** * Element which supports any type of values * This element allow to perform any type transformation from transformers on the form declaration @@ -13,26 +15,20 @@ */ class AnyElement extends LeafElement { - /** - * {@inheritdoc} - */ - protected function toPhp($httpValue) + #[Override] + protected function toPhp(mixed $httpValue): mixed { return $httpValue; } - /** - * {@inheritdoc} - */ - protected function toHttp($phpValue) + #[Override] + protected function toHttp(mixed $phpValue): mixed { return $phpValue; } - /** - * {@inheritdoc} - */ - protected function sanitize($rawValue) + #[Override] + protected function sanitize(mixed $rawValue): mixed { return $rawValue; } diff --git a/src/Leaf/AnyElementBuilder.php b/src/Leaf/AnyElementBuilder.php index 9b1d0bb..bbf1c5a 100644 --- a/src/Leaf/AnyElementBuilder.php +++ b/src/Leaf/AnyElementBuilder.php @@ -7,6 +7,7 @@ use Bdf\Form\ElementInterface; use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ValueValidatorInterface; +use Override; /** * Builder for any element @@ -19,10 +20,8 @@ class AnyElementBuilder extends AbstractElementBuilder { use ChoiceBuilderTrait; - /** - * {@inheritdoc} - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): AnyElement { return new AnyElement($validator, $transformer, $this->getChoices()); } diff --git a/src/Leaf/BooleanElement.php b/src/Leaf/BooleanElement.php index b8494d5..4e7f97d 100644 --- a/src/Leaf/BooleanElement.php +++ b/src/Leaf/BooleanElement.php @@ -8,6 +8,7 @@ use Bdf\Form\Validator\ValueValidatorInterface; use Bdf\Form\View\ElementViewInterface; use Bdf\Form\View\FieldViewInterface; +use Override; /** * Handle a boolean value, like with checkbox input @@ -17,10 +18,7 @@ */ class BooleanElement extends AbstractBooleanElement { - /** - * @var string - */ - private $httpValue = '1'; + private string $httpValue = '1'; /** * BooleanElement constructor. @@ -36,28 +34,20 @@ public function __construct(?ValueValidatorInterface $validator = null, ?Transfo $this->httpValue = $httpValue; } - /** - * {@inheritdoc} - */ - protected function toPhp($httpValue) + #[Override] + protected function toPhp(mixed $httpValue): bool { return (bool) $httpValue; } - /** - * {@inheritdoc} - */ - protected function toHttp($phpValue) + #[Override] + protected function toHttp(mixed $phpValue): ?string { return $phpValue ? $this->httpValue : null; } - /** - * {@inheritdoc} - * - * @return FieldViewInterface - */ - public function view(?HttpFieldPath $field = null): ElementViewInterface + #[Override] + public function view(?HttpFieldPath $field = null): FieldViewInterface { return new BooleanElementView(self::class, (string) $field, $this->httpValue(), $this->httpValue, (bool) $this->value(), $this->error()->global()); } diff --git a/src/Leaf/BooleanElementBuilder.php b/src/Leaf/BooleanElementBuilder.php index c70759c..6709248 100644 --- a/src/Leaf/BooleanElementBuilder.php +++ b/src/Leaf/BooleanElementBuilder.php @@ -8,6 +8,7 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ValueValidatorInterface; use InvalidArgumentException; +use Override; /** * Builder for a boolean element @@ -28,15 +29,8 @@ */ class BooleanElementBuilder extends AbstractElementBuilder { - /** - * @var string - */ - private $httpValue = '1'; - - /** - * @var bool - */ - private $booleanString = false; + private string $httpValue = '1'; + private bool $booleanString = false; /** * Define the HTTP value used for represent the true value @@ -83,10 +77,8 @@ public function booleanString(bool $flag = true): self return $this; } - /** - * {@inheritdoc} - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): BooleanStringElement|BooleanElement { return $this->booleanString ? new BooleanStringElement($validator, $transformer) diff --git a/src/Leaf/BooleanStringElement.php b/src/Leaf/BooleanStringElement.php index b50b3b1..4314a96 100644 --- a/src/Leaf/BooleanStringElement.php +++ b/src/Leaf/BooleanStringElement.php @@ -2,6 +2,8 @@ namespace Bdf\Form\Leaf; +use Override; + use function filter_var; use function is_bool; @@ -22,21 +24,16 @@ class BooleanStringElement extends AbstractBooleanElement * {@inheritdoc} * * @return scalar|null - * @psalm-suppress ImplementedReturnTypeMismatch - * @psalm-suppress LessSpecificImplementedReturnType */ - protected function sanitize($rawValue) + #[Override] + protected function sanitize(mixed $rawValue): int|float|string|bool|null { // Does not cast to string, to allow boolean value return is_scalar($rawValue) ? $rawValue : null; } - /** - * {@inheritdoc} - * - * @return bool|null - */ - protected function toPhp($httpValue): ?bool + #[Override] + protected function toPhp(mixed $httpValue): ?bool { if ($httpValue === null || $httpValue === '') { return null; @@ -50,10 +47,8 @@ protected function toPhp($httpValue): ?bool return filter_var((string) $httpValue, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); } - /** - * {@inheritdoc} - */ - protected function toHttp($phpValue): ?string + #[Override] + protected function toHttp(mixed $phpValue): ?string { if ($phpValue === null) { return null; diff --git a/src/Leaf/Date/DateTimeElement.php b/src/Leaf/Date/DateTimeElement.php index 8c5c141..11f3229 100644 --- a/src/Leaf/Date/DateTimeElement.php +++ b/src/Leaf/Date/DateTimeElement.php @@ -10,6 +10,7 @@ use DateTimeInterface; use DateTimeZone; use InvalidArgumentException; +use Override; use TypeError; /** @@ -24,24 +25,14 @@ final class DateTimeElement extends LeafElement /** * @var class-string */ - private $className; - - /** - * @var string - */ - private $format; - - /** - * @var DateTimeZone|null - */ - private $timezone; + private readonly string $className; + private readonly string $format; + private readonly ?DateTimeZone $timezone; /** * Reset the fields value which are not provided by the format - * - * @var bool */ - private $resetNotProvidedFields; + private bool $resetNotProvidedFields; /** * DateTimeType constructor. @@ -84,10 +75,8 @@ public function dateTimeClassName(): string return $this->className; } - /** - * {@inheritdoc} - */ - protected function toPhp($httpValue): ?DateTimeInterface + #[Override] + protected function toPhp(mixed $httpValue): ?DateTimeInterface { if ($httpValue === null || $httpValue === '') { return null; @@ -129,10 +118,8 @@ protected function toPhp($httpValue): ?DateTimeInterface return $dateTime; } - /** - * {@inheritdoc} - */ - protected function toHttp($phpValue) + #[Override] + protected function toHttp(mixed $phpValue): mixed { // Because of legacy behavior, the raw value can be saved when a transformer failed // So the raw string is kept as is @@ -148,12 +135,8 @@ protected function toHttp($phpValue) return $phpValue->format($this->format); } - /** - * {@inheritdoc} - * - * @return DateTimeInterface|null - */ - protected function tryCast($value): ?DateTimeInterface + #[Override] + protected function tryCast(mixed $value): ?DateTimeInterface { if ($value === null) { return null; diff --git a/src/Leaf/Date/DateTimeElementBuilder.php b/src/Leaf/Date/DateTimeElementBuilder.php index e101d46..9bc2c72 100644 --- a/src/Leaf/Date/DateTimeElementBuilder.php +++ b/src/Leaf/Date/DateTimeElementBuilder.php @@ -9,7 +9,6 @@ use Bdf\Form\Constraint\GreaterThanOrEqualField; use Bdf\Form\Constraint\LessThanField; use Bdf\Form\Constraint\LessThanOrEqualField; -use Bdf\Form\ElementInterface; use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Util\FieldPath; use Bdf\Form\Validator\TransformerExceptionConstraint; @@ -18,6 +17,7 @@ use DateTimeImmutable; use DateTimeInterface; use DateTimeZone; +use Override; use Symfony\Component\Validator\Constraints\GreaterThan; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; use Symfony\Component\Validator\Constraints\LessThan; @@ -48,24 +48,14 @@ class DateTimeElementBuilder extends AbstractElementBuilder /** * @var class-string */ - private $dateTimeClassName = DateTime::class; - - /** - * @var string - */ - private $dateFormat = DateTime::ATOM; - - /** - * @var DateTimeZone|null - */ - private $timezone; + private string $dateTimeClassName = DateTime::class; + private string $dateFormat = DateTime::ATOM; + private ?DateTimeZone $timezone = null; /** * Reset the fields value which are not provided by the format - * - * @var bool */ - private $resetNotProvidedFields = true; + private bool $resetNotProvidedFields = true; /** * Define the date time class name to use @@ -80,7 +70,7 @@ class DateTimeElementBuilder extends AbstractElementBuilder * * @see DateTimeElementBuilder::immutable() For use DateTimeImmutable */ - public function className(string $dateTimeClassName): self + public function className(string $dateTimeClassName): static { $this->dateTimeClassName = $dateTimeClassName; @@ -96,7 +86,7 @@ public function className(string $dateTimeClassName): self * @see DateTimeImmutable * @see DateTimeElementBuilder::className() */ - public function immutable(): self + public function immutable(): static { return $this->className(DateTimeImmutable::class); } @@ -115,7 +105,7 @@ public function immutable(): self * * @see https://www.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters For the format */ - public function format(string $format): self + public function format(string $format): static { $this->dateFormat = $format; @@ -136,7 +126,7 @@ public function format(string $format): self * * @return $this */ - public function timezone($timezone): self + public function timezone(string|DateTimeZone|null $timezone): static { if (is_string($timezone)) { $timezone = new DateTimeZone($timezone); @@ -160,11 +150,11 @@ public function timezone($timezone): self * * @return $this */ - public function before(DateTimeInterface $dateTime, ?string $message = null, bool $orEqual = false): self + public function before(DateTimeInterface $dateTime, ?string $message = null, bool $orEqual = false): static { $constraint = $orEqual ? new LessThanOrEqual($dateTime) : new LessThan($dateTime); - if ($message) { + if ($message !== null) { $constraint->message = $message; } @@ -192,11 +182,11 @@ public function before(DateTimeInterface $dateTime, ?string $message = null, boo * * @see FieldPath::parse() For the field path syntax */ - public function beforeField(string $field, ?string $message = null, bool $orEqual = false): self + public function beforeField(string $field, ?string $message = null, bool $orEqual = false): static { $constraint = $orEqual ? new LessThanOrEqualField($field) : new LessThanField($field); - if ($message) { + if ($message !== null) { $constraint->message = $message; } @@ -216,11 +206,11 @@ public function beforeField(string $field, ?string $message = null, bool $orEqua * * @return $this */ - public function after(DateTimeInterface $dateTime, ?string $message = null, bool $orEqual = false): self + public function after(DateTimeInterface $dateTime, ?string $message = null, bool $orEqual = false): static { $constraint = $orEqual ? new GreaterThanOrEqual($dateTime) : new GreaterThan($dateTime); - if ($message) { + if ($message !== null) { $constraint->message = $message; } @@ -248,11 +238,11 @@ public function after(DateTimeInterface $dateTime, ?string $message = null, bool * * @see FieldPath::parse() For the field path syntax */ - public function afterField(string $field, ?string $message = null, bool $orEqual = false): self + public function afterField(string $field, ?string $message = null, bool $orEqual = false): static { $constraint = $orEqual ? new GreaterThanOrEqualField($field) : new GreaterThanField($field); - if ($message) { + if ($message !== null) { $constraint->message = $message; } @@ -283,42 +273,24 @@ public function afterField(string $field, ?string $message = null, bool $orEqual * * @return $this */ - public function resetNotProvidedFields(bool $flag = true): self + public function resetNotProvidedFields(bool $flag = true): static { $this->resetNotProvidedFields = $flag; return $this; } - /** - * {@inheritdoc} - */ - protected function defaultTransformerExceptionConstraintOptions(): array - { - return [ - 'message' => 'This value is not a valid datetime.', - 'code' => 'INVALID_DATETIME_ERROR', - ]; - } - - /** - * {@inheritdoc} - */ + #[Override] protected function defaultTransformerExceptionConstraint(): TransformerExceptionConstraint { return new TransformerExceptionConstraint( - null, - /*message:*/ 'This value is not a valid datetime.', - /*code:*/ 'INVALID_DATETIME_ERROR', + message: 'This value is not a valid datetime.', + code: 'INVALID_DATETIME_ERROR', ); } - /** - * {@inheritdoc} - * - * @return DateTimeElement - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): DateTimeElement { return new DateTimeElement( $validator, diff --git a/src/Leaf/Date/Transformer/DateTimeToTimestampTransformer.php b/src/Leaf/Date/Transformer/DateTimeToTimestampTransformer.php index 4a10163..b19ffe8 100644 --- a/src/Leaf/Date/Transformer/DateTimeToTimestampTransformer.php +++ b/src/Leaf/Date/Transformer/DateTimeToTimestampTransformer.php @@ -10,22 +10,26 @@ use DateTimeInterface; use DateTimeZone; use InvalidArgumentException; +use Override; + +use function assert; +use function method_exists; /** * Transform a DateTime instance from a form element to a timestamp to a model */ #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] -final class DateTimeToTimestampTransformer implements TransformerInterface +final readonly class DateTimeToTimestampTransformer implements TransformerInterface { /** * @var class-string|null */ - private $className; + private ?string $className; /** * @var DateTimeZone|null */ - private $timezone; + private ?DateTimeZone $timezone; /** @@ -40,13 +44,8 @@ public function __construct(?string $className = null, ?DateTimeZone $timezone = $this->timezone = $timezone; } - /** - * {@inheritdoc} - * - * @psalm-suppress UndefinedInterfaceMethod - * @psalm-suppress PossiblyUndefinedMethod - */ - public function transformToHttp($value, ElementInterface $input): ?DateTimeInterface + #[Override] + public function transformToHttp(mixed $value, ElementInterface $input): ?DateTimeInterface { if ($value === null) { return null; @@ -59,19 +58,20 @@ public function transformToHttp($value, ElementInterface $input): ?DateTimeInter $className = $this->className ?? ($input instanceof DateTimeElement ? $input->dateTimeClassName() : DateTime::class); $timezone = $this->timezone ?? ($input instanceof DateTimeElement ? $input->timezone() : null); + /** @psalm-suppress UnsafeInstantiation */ /** @var DateTimeInterface $dateTime */ $dateTime = new $className; if ($timezone) { + assert(method_exists($dateTime, 'setTimezone')); $dateTime = $dateTime->setTimezone($timezone); } + assert(method_exists($dateTime, 'setTimestamp')); return $dateTime->setTimestamp($value); } - /** - * {@inheritdoc} - */ + #[Override] public function transformFromHttp($value, ElementInterface $input): ?int { if (!$value instanceof DateTimeInterface) { diff --git a/src/Leaf/FloatElement.php b/src/Leaf/FloatElement.php index b6ec00c..191da1e 100644 --- a/src/Leaf/FloatElement.php +++ b/src/Leaf/FloatElement.php @@ -2,8 +2,11 @@ namespace Bdf\Form\Leaf; +use Override; use TypeError; +use function is_numeric; + /** * Element for a float value * @@ -13,30 +16,20 @@ */ class FloatElement extends LeafElement { - /** - * {@inheritdoc} - * - * @return float|null - */ - protected function toPhp($httpValue): ?float + #[Override] + protected function toPhp(mixed $httpValue): ?float { return $httpValue === null || $httpValue === '' ? null : (float) $httpValue; } - /** - * {@inheritdoc} - */ - protected function toHttp($phpValue): ?string + #[Override] + protected function toHttp(mixed $phpValue): ?string { return $phpValue === null ? null : (string) $phpValue; } - /** - * {@inheritdoc} - * - * @return float|null - */ - protected function tryCast($value): ?float + #[Override] + protected function tryCast(mixed $value): ?float { if ($value === null) { return null; diff --git a/src/Leaf/FloatElementBuilder.php b/src/Leaf/FloatElementBuilder.php index 158a622..3234664 100644 --- a/src/Leaf/FloatElementBuilder.php +++ b/src/Leaf/FloatElementBuilder.php @@ -8,6 +8,7 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ValueValidatorInterface; use NumberFormatter; +use Override; /** * Builder for a float element @@ -27,20 +28,13 @@ */ class FloatElementBuilder extends NumberElementBuilder { - /** - * @var int|null - */ - private $scale = null; - - /** - * @var bool - */ - private $grouping = false; + private ?int $scale = null; + private bool $grouping = false; /** * @var NumberFormatter::ROUND_* */ - private $roundingMode = NumberFormatter::ROUND_DOWN; + private int $roundingMode = NumberFormatter::ROUND_DOWN; /** @@ -91,17 +85,13 @@ public function scale(int $scale): self return $this; } - /** - * {@inheritdoc} - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): FloatElement { return new FloatElement($validator, $transformer, $this->getChoices()); } - /** - * {@inheritdoc} - */ + #[Override] protected function numberTransformer(): TransformerInterface { return new LocalizedNumberTransformer($this->scale, $this->grouping, $this->roundingMode); diff --git a/src/Leaf/Helper/EmailElementBuilder.php b/src/Leaf/Helper/EmailElementBuilder.php index d44602f..6ab02e1 100644 --- a/src/Leaf/Helper/EmailElementBuilder.php +++ b/src/Leaf/Helper/EmailElementBuilder.php @@ -7,11 +7,9 @@ use Bdf\Form\Registry\RegistryInterface; use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ValueValidatorInterface; -use ReflectionClass; +use Override; use Symfony\Component\Validator\Constraints\Email; -use function is_array; - /** * Provide email constraint builder for a StringElementBuilder * @@ -29,15 +27,19 @@ class EmailElementBuilder extends StringElementBuilder /** * @var bool */ - private $useConstraint = true; + private bool $useConstraint = true; private ?string $errorMessage = null; + + /** + * @var value-of|null + */ private ?string $mode = null; /** * @var (callable(string):string)|null */ - private $normalizer = null; + private mixed $normalizer = null; /** * EmailElementBuilder constructor. @@ -48,14 +50,14 @@ public function __construct(?RegistryInterface $registry = null) { parent::__construct($registry); - $this->addConstraintsProvider([$this, 'createEmailConstraint']); + $this->addConstraintsProvider($this->createEmailConstraint(...)); } /** * The validation mode * See Email::VALIDATION_MODE_* constants * - * @param string $mode + * @param value-of $mode * * @return $this * @@ -63,7 +65,7 @@ public function __construct(?RegistryInterface $registry = null) * @see Email::VALIDATION_MODE_LOOSE * @see Email::VALIDATION_MODE_STRICT */ - public function mode(string $mode): self + public function mode(string $mode): static { $this->mode = $mode; @@ -77,7 +79,7 @@ public function mode(string $mode): self * * @return $this */ - public function errorMessage(string $message): self + public function errorMessage(string $message): static { $this->errorMessage = $message; @@ -102,7 +104,7 @@ public function errorMessage(string $message): self * * @return $this */ - public function normalizer(callable $normalizer): self + public function normalizer(callable $normalizer): static { $this->normalizer = $normalizer; @@ -114,7 +116,7 @@ public function normalizer(callable $normalizer): self * * @return $this */ - public function disableConstraint(): self + public function disableConstraint(): static { $this->useConstraint = false; @@ -125,27 +127,18 @@ public function disableConstraint(): self * Define the email validation constraint options * * - * $builder->email('contact')->useConstraint(['mode' => Email::VALIDATION_MODE_HTML5, 'message' => 'my error']); + * $builder->email('contact')->useConstraint(mode: Email::VALIDATION_MODE_HTML5, message: 'my error'); * * - * @param string|array|null $message + * @param value-of $mode * * @return $this * * @see Email for list of options */ - public function useConstraint($message = null, ?string $mode = null, ?string $normalizer = null): self + public function useConstraint(?string $message = null, ?string $mode = null, ?callable $normalizer = null): static { $this->useConstraint = true; - - if (is_array($message)) { - @trigger_error(sprintf('Passing an array of options on %s is deprecated since 1.7 and will be removed on 2.0, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - - $mode = $mode ?? $message['mode'] ?? null; - $normalizer = $normalizer ?? $message['normalizer'] ?? null; - $message = $message['message'] ?? null; - } - $this->errorMessage = $message; $this->mode = $mode; $this->normalizer = $normalizer; @@ -162,24 +155,17 @@ protected function createEmailConstraint(RegistryInterface $registry): array return []; } - static $isSf4 = null; - - if (null === $isSf4) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(Email::class))->getConstructor()->getNumberOfParameters() === 1; - } - return [ - $isSf4 - ? new Email(['mode' => $this->mode, 'message' => $this->errorMessage, 'normalizer' => $this->normalizer]) - : new Email(null, $this->errorMessage, $this->mode, $this->normalizer) + new Email( + message: $this->errorMessage, + mode: $this->mode, + normalizer: $this->normalizer + ), ]; } - /** - * {@inheritdoc} - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): EmailElement { return new EmailElement($validator, $transformer, $this->getChoices()); } diff --git a/src/Leaf/Helper/UrlElementBuilder.php b/src/Leaf/Helper/UrlElementBuilder.php index dcde5be..52ddbce 100644 --- a/src/Leaf/Helper/UrlElementBuilder.php +++ b/src/Leaf/Helper/UrlElementBuilder.php @@ -2,18 +2,15 @@ namespace Bdf\Form\Leaf\Helper; -use Bdf\Form\ElementInterface; use Bdf\Form\Leaf\StringElementBuilder; use Bdf\Form\Registry\RegistryInterface; use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ValueValidatorInterface; -use ReflectionClass; +use Override; use Symfony\Component\Validator\Constraints\Url; -use function is_array; use function is_string; -use function sprintf; -use function trigger_error; +use function property_exists; /** * Provide URL constraint builder for a StringElementBuilder @@ -26,23 +23,19 @@ */ class UrlElementBuilder extends StringElementBuilder { - /** - * @var bool - */ - private $useConstraint = true; - + private bool $useConstraint = true; private ?string $errorMessage = null; /** * @var string[]|null */ - private $protocols = null; + private ?array $protocols = null; private ?bool $relativeProtocol = null; /** * @var (callable(string):string)|null */ - private $normalizer = null; + private mixed $normalizer = null; private ?bool $requireTld = false; private ?string $tldMessage = null; @@ -55,7 +48,7 @@ public function __construct(?RegistryInterface $registry = null) { parent::__construct($registry); - $this->addConstraintsProvider([$this, 'createUrlConstraint']); + $this->addConstraintsProvider($this->createUrlConstraint(...)); } /** @@ -69,7 +62,7 @@ public function __construct(?RegistryInterface $registry = null) * * @return $this */ - public function protocols(string ...$protocols): self + public function protocols(string ...$protocols): static { $this->protocols = $protocols; @@ -84,7 +77,7 @@ public function protocols(string ...$protocols): self * * @return $this */ - public function relativeProtocol(bool $enable = true): self + public function relativeProtocol(bool $enable = true): static { $this->relativeProtocol = $enable; @@ -98,7 +91,7 @@ public function relativeProtocol(bool $enable = true): self * * @return $this */ - public function errorMessage(string $message): self + public function errorMessage(string $message): static { $this->errorMessage = $message; @@ -123,7 +116,7 @@ public function errorMessage(string $message): self * * @return $this */ - public function normalizer(callable $normalizer): self + public function normalizer(callable $normalizer): static { $this->normalizer = $normalizer; @@ -135,7 +128,7 @@ public function normalizer(callable $normalizer): self * * @return $this */ - public function disableConstraint(): self + public function disableConstraint(): static { $this->useConstraint = false; @@ -153,19 +146,10 @@ public function disableConstraint(): self * * @see Url for list of options */ - public function useConstraint($message = null, $protocols = null, ?bool $relativeProtocol = null, ?callable $normalizer = null): self + public function useConstraint(?string $message = null, string|array|null $protocols = null, ?bool $relativeProtocol = null, ?callable $normalizer = null): static { $this->useConstraint = true; - if (is_array($message)) { - @trigger_error(sprintf('Passing an array of options to "%s" is deprecated since 1.7, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - - $protocols ??= $message['protocols'] ?? null; - $relativeProtocol ??= $message['relativeProtocol'] ?? null; - $normalizer ??= $message['normalizer'] ?? null; - $message = $message['message'] ?? null; - } - $this->errorMessage = $message; $this->protocols = is_string($protocols) ? [$protocols] : $protocols; $this->relativeProtocol = $relativeProtocol; @@ -176,7 +160,6 @@ public function useConstraint($message = null, $protocols = null, ?bool $relativ /** * @return \Symfony\Component\Validator\Constraint[] - * @psalm-suppress TooManyArguments */ protected function createUrlConstraint(RegistryInterface $registry): array { @@ -184,25 +167,28 @@ protected function createUrlConstraint(RegistryInterface $registry): array return []; } - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(Url::class))->getConstructor()->getNumberOfParameters() === 1; - } - return [ - $isSf4 - ? new Url(['protocols' => $this->protocols, 'relativeProtocol' => $this->relativeProtocol, 'normalizer' => $this->normalizer, 'message' => $this->errorMessage]) - : new Url(null, $this->errorMessage, $this->protocols, $this->relativeProtocol, $this->normalizer, null, null, $this->requireTld, $this->tldMessage) + property_exists(Url::class, 'requireTld') // SF >= 7.1 + ? new Url( + message: $this->errorMessage, + protocols: $this->protocols, + relativeProtocol: $this->relativeProtocol, + normalizer: $this->normalizer, + requireTld: $this->requireTld, + tldMessage: $this->tldMessage + ) + : new Url( + message: $this->errorMessage, + protocols: $this->protocols, + relativeProtocol: $this->relativeProtocol, + normalizer: $this->normalizer, + ) , ]; } - /** - * {@inheritdoc} - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): UrlElement { return new UrlElement($validator, $transformer, $this->getChoices()); } diff --git a/src/Leaf/IntegerElement.php b/src/Leaf/IntegerElement.php index 250bf4c..4627da5 100644 --- a/src/Leaf/IntegerElement.php +++ b/src/Leaf/IntegerElement.php @@ -2,8 +2,11 @@ namespace Bdf\Form\Leaf; +use Override; use TypeError; +use function is_numeric; + /** * Element for an integer * @@ -13,30 +16,20 @@ */ class IntegerElement extends LeafElement { - /** - * {@inheritdoc} - * - * @return int|null - */ - protected function toPhp($httpValue): ?int + #[Override] + protected function toPhp(mixed $httpValue): ?int { return $httpValue === null || $httpValue === '' ? null : (int) $httpValue; } - /** - * {@inheritdoc} - */ - protected function toHttp($phpValue): ?string + #[Override] + protected function toHttp(mixed $phpValue): ?string { return $phpValue === null ? null : (string) $phpValue; } - /** - * {@inheritdoc} - * - * @return int|null - */ - protected function tryCast($value): ?int + #[Override] + protected function tryCast(mixed $value): ?int { if ($value === null) { return null; diff --git a/src/Leaf/IntegerElementBuilder.php b/src/Leaf/IntegerElementBuilder.php index 247ece0..304ec8b 100644 --- a/src/Leaf/IntegerElementBuilder.php +++ b/src/Leaf/IntegerElementBuilder.php @@ -8,6 +8,7 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ValueValidatorInterface; use NumberFormatter; +use Override; /** * Builder for an integer element @@ -27,15 +28,12 @@ */ class IntegerElementBuilder extends NumberElementBuilder { - /** - * @var bool - */ - private $grouping = false; + private bool $grouping = false; /** * @var NumberFormatter::ROUND_* */ - private $roundingMode = NumberFormatter::ROUND_DOWN; + private int $roundingMode = NumberFormatter::ROUND_DOWN; /** @@ -70,17 +68,13 @@ public function roundingMode(int $mode): self return $this; } - /** - * {@inheritdoc} - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): IntegerElement { return new IntegerElement($validator, $transformer, $this->getChoices()); } - /** - * {@inheritdoc} - */ + #[Override] protected function numberTransformer(): TransformerInterface { return new LocalizedIntegerTransformer($this->grouping, $this->roundingMode); diff --git a/src/Leaf/LeafElement.php b/src/Leaf/LeafElement.php index 8ea0f80..f3388ed 100644 --- a/src/Leaf/LeafElement.php +++ b/src/Leaf/LeafElement.php @@ -16,9 +16,9 @@ use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\ValueValidatorInterface; use Bdf\Form\View\ConstraintsNormalizer; -use Bdf\Form\View\ElementViewInterface; use Bdf\Form\View\FieldViewInterface; use Exception; +use Override; use Symfony\Component\Validator\Constraints\NotBlank; /** @@ -36,34 +36,24 @@ abstract class LeafElement implements ElementInterface, Choiceable /** * @var ValueValidatorInterface */ - private $validator; + private readonly ValueValidatorInterface $validator; /** * Transformer to view value - * - * @var TransformerInterface */ - private $transformer; + private readonly TransformerInterface $transformer; /** * @var ChoiceInterface|null */ - private $choices; + private ?ChoiceInterface $choices; /** * @var T|null */ - private $value = null; - - /** - * @var FormError - */ - private $error; - - /** - * @var bool - */ - private $submitted = false; + private mixed $value = null; + private FormError $error; + private bool $submitted = false; /** @@ -81,10 +71,8 @@ public function __construct(?ValueValidatorInterface $validator = null, ?Transfo $this->choices = $choices; } - /** - * {@inheritdoc} - */ - final public function submit($data): ElementInterface + #[Override] + final public function submit(mixed $data): static { $shouldBeValidated = true; @@ -105,10 +93,8 @@ final public function submit($data): ElementInterface return $this; } - /** - * {@inheritdoc} - */ - final public function patch($data): ElementInterface + #[Override] + final public function patch(mixed $data): static { // A data is provided : simply submit the data if ($data !== null) { @@ -122,52 +108,40 @@ final public function patch($data): ElementInterface return $this; } - /** - * {@inheritdoc} - */ + #[Override] final public function valid(): bool { return $this->submitted && $this->error->empty(); } - /** - * {@inheritdoc} - */ + #[Override] final public function failed(): bool { return !$this->valid(); } - /** - * {@inheritdoc} - */ + #[Override] final public function error(?HttpFieldPath $field = null): FormError { return $field ? $this->error->withField($field) : $this->error; } - /** - * {@inheritdoc} - */ - final public function import($entity): ElementInterface + #[Override] + final public function import(mixed $entity): static { $this->value = $this->tryCast($entity); return $this; } - /** - * {@inheritdoc} - */ - final public function value() + #[Override] + final public function value(): mixed { return $this->value; } - /** - * {@inheritdoc} - */ - final public function httpValue() + #[Override] + final public function httpValue(): mixed { try { return $this->transformer->transformToHttp($this->toHttp($this->value), $this); @@ -176,9 +150,7 @@ final public function httpValue() } } - /** - * {@inheritdoc} - */ + #[Override] final public function root(): RootElementInterface { if ($container = $this->container()) { @@ -189,12 +161,8 @@ final public function root(): RootElementInterface return new LeafRootElement($this); } - /** - * {@inheritdoc} - * - * @return FieldViewInterface - */ - public function view(?HttpFieldPath $field = null): ElementViewInterface + #[Override] + public function view(?HttpFieldPath $field = null): FieldViewInterface { [$required, $normalizedConstraints] = $this->parseConstraints($this->validator); @@ -209,9 +177,7 @@ public function view(?HttpFieldPath $field = null): ElementViewInterface ); } - /** - * {@inheritdoc} - */ + #[Override] final public function choices(): ?ChoiceInterface { return $this->choices; @@ -224,7 +190,7 @@ final public function choices(): ?ChoiceInterface * * @return T|null */ - abstract protected function toPhp($httpValue); + abstract protected function toPhp(mixed $httpValue)/*: mixed*/; /** * Transform the PHP value to the HTTP representation @@ -233,7 +199,7 @@ abstract protected function toPhp($httpValue); * * @return mixed */ - abstract protected function toHttp($phpValue); + abstract protected function toHttp(mixed $phpValue)/*: mixed*/; /** * Try to convert the value into the element type @@ -247,7 +213,7 @@ abstract protected function toHttp($phpValue); * * @see LeafElement::import() */ - protected function tryCast($value) + protected function tryCast(mixed $value)/*: mixed*/ { return $value; } @@ -256,10 +222,9 @@ protected function tryCast($value) * Sanitize the raw HTTP value * * @param mixed $rawValue The raw HTTP value - * - * @return string|null + * @return mixed */ - protected function sanitize($rawValue) + protected function sanitize(mixed $rawValue)/*: mixed*/ { if (is_scalar($rawValue)) { return (string) $rawValue; @@ -277,11 +242,7 @@ protected function sanitize($rawValue) */ protected function choiceView(): ?array { - if ($this->choices === null) { - return null; - } - - return $this->choices->view(function (ChoiceView $view) { + return $this->choices?->view(function (ChoiceView $view) { $view->setSelected($view->value() == $this->value()); $view->setValue($this->transformer->transformToHttp($this->toHttp($view->value()), $this)); }); diff --git a/src/Leaf/LeafRootElement.php b/src/Leaf/LeafRootElement.php index f9faf9d..1a11fb0 100644 --- a/src/Leaf/LeafRootElement.php +++ b/src/Leaf/LeafRootElement.php @@ -12,6 +12,7 @@ use Bdf\Form\Util\RootFlagsTrait; use Bdf\Form\View\ElementViewInterface; use OutOfBoundsException; +use Override; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Validator\Constraint; @@ -26,160 +27,114 @@ final class LeafRootElement implements RootElementInterface { use RootFlagsTrait; - /** - * @var ElementInterface - */ - private $element; + public function __construct( + private readonly ElementInterface $element, + ) {} - - /** - * LeafRootElement constructor. - * - * @param ElementInterface $element - */ - public function __construct(ElementInterface $element) - { - $this->element = $element; - } - - /** - * {@inheritdoc} - */ - public function submit($data): ElementInterface + #[Override] + public function submit(mixed $data): static { $this->element->submit($data); return $this; } - /** - * {@inheritdoc} - */ - public function patch($data): ElementInterface + #[Override] + public function patch(mixed $data): static { $this->element->patch($data); return $this; } - /** - * {@inheritdoc} - */ - public function import($entity): ElementInterface + #[Override] + public function import(mixed $entity): static { $this->element->import($entity); return $this; } - /** - * {@inheritdoc} - */ - public function value() + #[Override] + public function value(): mixed { return $this->element->value(); } - /** - * {@inheritdoc} - */ - public function httpValue() + #[Override] + public function httpValue(): mixed { return $this->element->httpValue(); } - /** - * {@inheritdoc} - */ + #[Override] public function valid(): bool { return $this->element->valid(); } - /** - * {@inheritdoc} - */ + #[Override] public function failed(): bool { // Do not use $this->element->failed() because it may be not implemented return !$this->valid(); } - /** - * {@inheritdoc} - */ + #[Override] public function error(?HttpFieldPath $field = null): FormError { return $this->element->error($field); } - /** - * {@inheritdoc} - */ + #[Override] public function container(): ?ChildInterface { return null; } - /** - * {@inheritdoc} - */ + #[Override] public function setContainer(ChildInterface $container): ElementInterface { throw new BadMethodCallException('Cannot set a container on a root element'); } - /** - * {@inheritdoc} - */ + #[Override] public function root(): RootElementInterface { return $this; } - /** - * {@inheritdoc} - */ + #[Override] public function view(?HttpFieldPath $field = null): ElementViewInterface { return $this->element->view($field); } - /** - * {@inheritdoc} - */ + #[Override] public function submitButton(): ?ButtonInterface { return null; } - /** - * {@inheritdoc} - */ + #[Override] public function button(string $name): ButtonInterface { throw new OutOfBoundsException('A leaf element do not have any buttons'); } - /** - * {@inheritdoc} - */ + #[Override] public function getValidator(): ValidatorInterface { - return (new ValidatorBuilder())->getValidator(); + return new ValidatorBuilder()->getValidator(); } - /** - * {@inheritdoc} - */ + #[Override] public function getPropertyAccessor(): PropertyAccessorInterface { return new PropertyAccessor(); } - /** - * {@inheritdoc} - */ + #[Override] public function constraintGroups(): array { return [Constraint::DEFAULT_GROUP]; diff --git a/src/Leaf/NumberElementBuilder.php b/src/Leaf/NumberElementBuilder.php index 63930f0..c10dd50 100644 --- a/src/Leaf/NumberElementBuilder.php +++ b/src/Leaf/NumberElementBuilder.php @@ -7,15 +7,11 @@ use Bdf\Form\Registry\RegistryInterface; use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\TransformerExceptionConstraint; -use ReflectionClass; +use Override; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; use Symfony\Component\Validator\Constraints\LessThanOrEqual; use Symfony\Component\Validator\Constraints\Positive; -use function is_array; -use function sprintf; -use function trigger_error; - /** * Base builder for an number element * @@ -26,10 +22,7 @@ abstract class NumberElementBuilder extends AbstractElementBuilder { use ChoiceBuilderTrait; - /** - * @var bool - */ - private $raw = false; + private bool $raw = false; /** @@ -41,7 +34,7 @@ public function __construct(?RegistryInterface $registry = null) { parent::__construct($registry); - $this->addTransformerProvider([$this, 'provideNumberTransformer']); + $this->addTransformerProvider($this->provideNumberTransformer(...)); } /** @@ -52,19 +45,9 @@ public function __construct(?RegistryInterface $registry = null) * * @return $this */ - public function min($min, ?string $message = null): self + public function min(int|float $min, ?string $message = null): self { - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(GreaterThanOrEqual::class))->getConstructor()->getNumberOfParameters() === 1; - } - - $this->satisfy($isSf4 - ? new GreaterThanOrEqual(['value' => $min, 'message' => $message]) - : new GreaterThanOrEqual($min, null, $message) - ); + $this->satisfy(new GreaterThanOrEqual(value: $min, message: $message)); return $this; } @@ -77,19 +60,9 @@ public function min($min, ?string $message = null): self * * @return $this */ - public function max($max, ?string $message = null): self + public function max(int|float $max, ?string $message = null): self { - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(LessThanOrEqual::class))->getConstructor()->getNumberOfParameters() === 1; - } - - $this->satisfy($isSf4 - ? new LessThanOrEqual(['value' => $max, 'message' => $message]) - : new LessThanOrEqual($max, null, $message) - ); + $this->satisfy(new LessThanOrEqual(value: $max, message: $message)); return $this; } @@ -97,29 +70,14 @@ public function max($max, ?string $message = null): self /** * The number must be positive * - * @param array|string|null $message The error message + * @param string|null $message The error message * * @return $this * @see Positive */ - public function positive($message = null): self + public function positive(?string $message = null): self { - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(Positive::class))->getConstructor()->getNumberOfParameters() === 1; - } - - if (is_array($message)) { - @trigger_error(sprintf('Passing an array of options on %s is deprecated since 1.7 and will be removed on 2.0, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - $message = $message['message'] ?? null; - } - - return $this->satisfy($isSf4 - ? new Positive(['message' => $message]) - : new Positive(null, $message) - ); + return $this->satisfy(new Positive(message: $message)); } /** @@ -139,26 +97,12 @@ public function raw(bool $flag = true): self return $this; } - /** - * {@inheritdoc} - */ - protected function defaultTransformerExceptionConstraintOptions(): array - { - return [ - 'message' => 'The value is not a valid number.', - 'code' => 'INVALID_NUMBER_ERROR', - ]; - } - - /** - * {@inheritdoc} - */ + #[Override] protected function defaultTransformerExceptionConstraint(): TransformerExceptionConstraint { return new TransformerExceptionConstraint( - null, - /*message:*/ 'The value is not a valid number.', - /*code:*/ 'INVALID_NUMBER_ERROR', + message: 'The value is not a valid number.', + code: 'INVALID_NUMBER_ERROR', ); } diff --git a/src/Leaf/StringElement.php b/src/Leaf/StringElement.php index 457d2f9..bd76794 100644 --- a/src/Leaf/StringElement.php +++ b/src/Leaf/StringElement.php @@ -2,8 +2,12 @@ namespace Bdf\Form\Leaf; +use Override; use TypeError; -use function Webmozart\Assert\Tests\StaticAnalysis\string; + +use function is_object; +use function is_scalar; +use function method_exists; /** * Element for a simple string field @@ -14,12 +18,8 @@ */ class StringElement extends LeafElement { - /** - * {@inheritdoc} - * - * @return string|null - */ - protected function toPhp($httpValue): ?string + #[Override] + protected function toPhp(mixed $httpValue): ?string { if (!is_scalar($httpValue)) { return null; @@ -28,20 +28,14 @@ protected function toPhp($httpValue): ?string return (string) $httpValue; } - /** - * {@inheritdoc} - */ - protected function toHttp($phpValue): ?string + #[Override] + protected function toHttp(mixed $phpValue): ?string { return $phpValue; } - /** - * {@inheritdoc} - * - * @return string|null - */ - protected function tryCast($value): ?string + #[Override] + protected function tryCast(mixed $value): ?string { if ($value === null) { return null; diff --git a/src/Leaf/StringElementBuilder.php b/src/Leaf/StringElementBuilder.php index 8c244b1..14f3976 100644 --- a/src/Leaf/StringElementBuilder.php +++ b/src/Leaf/StringElementBuilder.php @@ -8,24 +8,17 @@ use Bdf\Form\ElementInterface; use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ValueValidatorInterface; -use InvalidArgumentException; +use Override; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\Regex; -use function get_debug_type; -use function is_array; -use function is_string; -use function property_exists; -use function sprintf; -use function trigger_error; - /** * Builder for string element * * * $builder->string('username') * ->required() - * ->length(['min' => 6, 'max' => 32]) + * ->length(min: 6, max: 32) * ->regex('/[a-z_-]+/i') * ; * @@ -42,65 +35,36 @@ class StringElementBuilder extends AbstractElementBuilder /** * Add a string length constraint * - * Options are keys are : min, max, minMessage, maxMessage - * * Usage: * - * $builder->length(['max' => 256]); + * $builder->length(max: 256); * * - * @param array|int|null $exactly + * @param positive-int|null $exactly + * @param non-negative-int|null $min + * @param positive-int|null $max + * @param value-of|null $countUnit * * @return $this * * @see Length For options */ - public function length($exactly = null, ?int $min = null, ?int $max = null, ?string $charset = null, ?callable $normalizer = null, ?string $countUnit = null, ?string $exactMessage = null, ?string $minMessage = null, ?string $maxMessage = null, ?string $charsetMessage = null): self + public function length(?int $exactly = null, ?int $min = null, ?int $max = null, ?string $charset = null, ?callable $normalizer = null, ?string $countUnit = null, ?string $exactMessage = null, ?string $minMessage = null, ?string $maxMessage = null, ?string $charsetMessage = null): static { - static $sfVersion = null; - - if ($sfVersion === null) { - /** @psalm-suppress PossiblyNullReference */ - if ((new \ReflectionClass(Length::class))->getConstructor()->getNumberOfParameters() === 1) { - $sfVersion = 43; - } elseif (!property_exists(Length::class, 'countUnit')) { - // Count unit parameter has been added in SF 6.3 - $sfVersion = 53; - } else { - $sfVersion = 63; - } - } - - if (is_array($exactly)) { - @trigger_error(sprintf('Passing an array of options on %s is deprecated since 1.7 and will be removed on 2.0, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - - $min ??= $exactly['min'] ?? null; - $max ??= $exactly['max'] ?? null; - $charset ??= $exactly['charset'] ?? null; - $normalizer ??= $exactly['normalizer'] ?? null; - $countUnit ??= $exactly['countUnit'] ?? null; - $exactMessage ??= $exactly['exactMessage'] ?? null; - $minMessage ??= $exactly['minMessage'] ?? null; - $maxMessage ??= $exactly['maxMessage'] ?? null; - $charsetMessage ??= $exactly['charsetMessage'] ?? null; - $exactly = $exactly['exactly'] ?? null; - } - - switch ($sfVersion) { - case 43: - $constraint = new Length(['value' => $exactly, 'min' => $min, 'max' => $max, 'charset' => $charset, 'normalizer' => $normalizer, 'exactMessage' => $exactMessage, 'minMessage' => $minMessage, 'maxMessage' => $maxMessage, 'charsetMessage' => $charsetMessage]); - break; - - case 53: - $constraint = new Length($exactly, $min, $max, $charset, $normalizer, $exactMessage, $minMessage, $maxMessage, $charsetMessage); - break; - - default: - /** @psalm-suppress PossiblyInvalidArgument */ - $constraint = new Length($exactly, $min, $max, $charset, $normalizer, $countUnit, $exactMessage, $minMessage, $maxMessage, $charsetMessage); - } - - return $this->satisfy($constraint); + return $this->satisfy( + new Length( + exactly: $exactly, + min: $min, + max: $max, + charset: $charset, + normalizer: $normalizer, + countUnit: $countUnit, + exactMessage: $exactMessage, + minMessage: $minMessage, + maxMessage: $maxMessage, + charsetMessage: $charsetMessage, + ) + ); } /** @@ -111,45 +75,27 @@ public function length($exactly = null, ?int $min = null, ?int $max = null, ?str * $builder->regex('/[a-z_-]+/', message: 'Invalid value'); // With custom options * * - * @param string|array $pattern + * @param string $pattern * * @return $this * * @see Regex */ - public function regex($pattern, ?string $message = null, ?string $htmlPattern = null, ?bool $match = null, ?callable $normalizer = null): self + public function regex(string $pattern, ?string $message = null, ?string $htmlPattern = null, ?bool $match = null, ?callable $normalizer = null): static { - if (is_array($pattern)) { - @trigger_error(sprintf('Passing an array of options on %s is deprecated since 1.7 and will be removed on 2.0, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - - $message ??= $pattern['message'] ?? null; - $htmlPattern ??= $pattern['htmlPattern'] ?? null; - $match ??= $pattern['match'] ?? null; - $normalizer ??= $pattern['normalizer'] ?? null; - $pattern = $pattern['pattern'] ?? null; - } - - static $ifSf4 = null; - - if ($ifSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $ifSf4 = (new \ReflectionClass(Regex::class))->getConstructor()->getNumberOfParameters() === 1; - } - - if (!is_string($pattern)) { - throw new InvalidArgumentException(sprintf('The "pattern" option of the regex constraint must be a string, "%s" given.', get_debug_type($pattern))); - } - - return $this->satisfy($ifSf4 - ? new Regex(['pattern' => $pattern, 'message' => $message, 'htmlPattern' => $htmlPattern, 'match' => $match, 'normalizer' => $normalizer]) - : new Regex($pattern, $message, $htmlPattern, $match, $normalizer) + return $this->satisfy( + new Regex( + pattern: $pattern, + message: $message, + htmlPattern: $htmlPattern, + match: $match, + normalizer: $normalizer, + ) ); } - /** - * {@inheritdoc} - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): StringElement { return new StringElement($validator, $transformer, $this->getChoices()); } diff --git a/src/Leaf/Transformer/LocalizedIntegerTransformer.php b/src/Leaf/Transformer/LocalizedIntegerTransformer.php index ccbf4fd..7977a7a 100644 --- a/src/Leaf/Transformer/LocalizedIntegerTransformer.php +++ b/src/Leaf/Transformer/LocalizedIntegerTransformer.php @@ -4,6 +4,7 @@ use Attribute; use NumberFormatter; +use Override; /** * Localized number transformer for integer value @@ -25,11 +26,9 @@ public function __construct(bool $grouping = false, int $roundingMode = NumberFo parent::__construct(0, $grouping, $roundingMode, $locale); } - /** - * {@inheritdoc} - */ - protected function cast($value): int + #[Override] + protected function cast(float|int|string $value): int { - return $value; + return (int) $value; } } diff --git a/src/Leaf/Transformer/LocalizedNumberTransformer.php b/src/Leaf/Transformer/LocalizedNumberTransformer.php index f3700e3..2880e74 100644 --- a/src/Leaf/Transformer/LocalizedNumberTransformer.php +++ b/src/Leaf/Transformer/LocalizedNumberTransformer.php @@ -9,6 +9,9 @@ use Locale; use NumberFormatter; +use Override; + +use function is_int; use function is_string; /** @@ -26,20 +29,20 @@ class LocalizedNumberTransformer implements TransformerInterface * * @var int|null */ - private $scale; + private readonly ?int $scale; /** * @var int * @psalm-var NumberFormatter::ROUND_* */ - private $roundingMode; + private readonly int $roundingMode; /** * Group by thousand or not * * @var bool */ - private $grouping; + private readonly bool $grouping; /** * The locale to use @@ -47,7 +50,7 @@ class LocalizedNumberTransformer implements TransformerInterface * * @var string|null */ - private $locale; + private readonly ?string $locale; /** * LocalizedNumberTransformer constructor. @@ -70,6 +73,7 @@ public function __construct(?int $scale = null, bool $grouping = false, int $rou * * @throws InvalidArgumentException If the given value is not numeric or cannot be formatted */ + #[Override] final public function transformToHttp($value, ElementInterface $input): ?string { if ($value === null) { @@ -102,7 +106,8 @@ final public function transformToHttp($value, ElementInterface $input): ?string * * @throws InvalidArgumentException If the given value is not scalar or cannot be parsed */ - final public function transformFromHttp($value, ElementInterface $input) + #[Override] + final public function transformFromHttp(mixed $value, ElementInterface $input): float|int|string|null { if ($value !== null && !is_int($value) && !is_float($value) && !is_string($value)) { throw new InvalidArgumentException('Expected a scalar or null.'); @@ -158,8 +163,9 @@ private function getNumberFormatter(): NumberFormatter * * @return T */ - protected function cast($value) + protected function cast(float|int|string $value): float|int|string { + /** @var T */ return $value; } @@ -170,7 +176,7 @@ protected function cast($value) * * @return int|float The rounded number */ - private function round($number) + private function round(int|float $number): int|float { if (is_int($number) || $this->scale === null) { return $number; @@ -185,7 +191,7 @@ private function round($number) return round($number, $this->scale, PHP_ROUND_HALF_DOWN); } - $coef = 10 ** $this->scale; + $coef = (float) (10 ** $this->scale); $number *= $coef; switch ($this->roundingMode) { diff --git a/src/Leaf/View/BooleanElementView.php b/src/Leaf/View/BooleanElementView.php index c46f5d03..413a6d7 100644 --- a/src/Leaf/View/BooleanElementView.php +++ b/src/Leaf/View/BooleanElementView.php @@ -7,6 +7,7 @@ use Bdf\Form\View\FieldViewInterface; use Bdf\Form\View\FieldViewRendererInterface; use Bdf\Form\View\FieldViewTrait; +use Override; /** * Element view for boolean / checkbox @@ -18,15 +19,8 @@ final class BooleanElementView implements FieldViewInterface use ElementViewTrait; use FieldViewTrait; - /** - * @var string - */ - private $httpValue; - - /** - * @var bool - */ - private $checked; + public readonly string $httpValue; + public private(set) bool $checked; /** * BooleanElementView constructor. @@ -38,7 +32,7 @@ final class BooleanElementView implements FieldViewInterface * @param bool $checked * @param string|null $error */ - public function __construct(string $type, string $name, $value, string $httpValue, bool $checked, ?string $error) + public function __construct(string $type, string $name, mixed $value, string $httpValue, bool $checked, ?string $error) { $this->type = $type; $this->name = $name; @@ -70,9 +64,7 @@ public function checked(): bool return $this->checked; } - /** - * {@inheritdoc} - */ + #[Override] protected function defaultRenderer(): FieldViewRendererInterface { return CheckboxHtmlRenderer::instance(); diff --git a/src/Leaf/View/CheckboxHtmlRenderer.php b/src/Leaf/View/CheckboxHtmlRenderer.php index 473e216..948117a 100644 --- a/src/Leaf/View/CheckboxHtmlRenderer.php +++ b/src/Leaf/View/CheckboxHtmlRenderer.php @@ -5,6 +5,7 @@ use Bdf\Form\View\FieldViewInterface; use Bdf\Form\View\FieldViewRendererInterface; use Bdf\Form\View\HtmlRenderer; +use Override; /** * Default renderer for @see BooleanElementView @@ -13,16 +14,9 @@ */ final class CheckboxHtmlRenderer implements FieldViewRendererInterface { - /** - * @var CheckboxHtmlRenderer|null - */ - private static $instance; + private static ?self $instance = null; - /** - * {@inheritdoc} - * - * @param BooleanElementView $view - */ + #[Override] public function render(FieldViewInterface $view, array $attributes): string { if (!isset($attributes['type'])) { @@ -43,10 +37,6 @@ public function render(FieldViewInterface $view, array $attributes): string */ public static function instance(): self { - if (self::$instance) { - return self::$instance; - } - - return self::$instance = new self; + return self::$instance ??= new self; } } diff --git a/src/Leaf/View/SelectHtmlRenderer.php b/src/Leaf/View/SelectHtmlRenderer.php index 72b493a..16657fc 100644 --- a/src/Leaf/View/SelectHtmlRenderer.php +++ b/src/Leaf/View/SelectHtmlRenderer.php @@ -6,6 +6,7 @@ use Bdf\Form\View\FieldViewRendererInterface; use Bdf\Form\View\HtmlRenderer; use InvalidArgumentException; +use Override; /** * Renderer for select element @@ -15,17 +16,14 @@ */ final class SelectHtmlRenderer implements FieldViewRendererInterface { - /** - * @var SelectHtmlRenderer|null - */ - private static $instance; + private static ?self $instance = null; - /** - * {@inheritdoc} - */ + #[Override] public function render(FieldViewInterface $view, array $attributes): string { - if (!$choices = $view->choices()) { + $choices = $view->choices(); + + if ($choices === null || $choices === []) { throw new InvalidArgumentException('Choices must be provided for render a select element.'); } @@ -52,10 +50,6 @@ public function render(FieldViewInterface $view, array $attributes): string */ public static function instance(): self { - if (self::$instance) { - return self::$instance; - } - - return self::$instance = new self; + return self::$instance ??= new self; } } diff --git a/src/Leaf/View/SimpleElementView.php b/src/Leaf/View/SimpleElementView.php index 00c821f..9cae6c4 100644 --- a/src/Leaf/View/SimpleElementView.php +++ b/src/Leaf/View/SimpleElementView.php @@ -8,6 +8,7 @@ use Bdf\Form\View\FieldViewInterface; use Bdf\Form\View\FieldViewRendererInterface; use Bdf\Form\View\FieldViewTrait; +use Override; /** * View for simple input fields @@ -30,7 +31,7 @@ final class SimpleElementView implements FieldViewInterface * @param array $constraints * @param ChoiceView[]|null $choices */ - public function __construct(string $type, string $name, $value, ?string $error, bool $required, array $constraints, ?array $choices = null) + public function __construct(string $type, string $name, mixed $value, ?string $error, bool $required, array $constraints, ?array $choices = null) { $this->type = $type; $this->name = $name; @@ -41,12 +42,10 @@ public function __construct(string $type, string $name, $value, ?string $error, $this->choices = $choices; } - /** - * {@inheritdoc} - */ + #[Override] protected function defaultRenderer(): FieldViewRendererInterface { - return $this->choices ? SelectHtmlRenderer::instance() : SimpleFieldHtmlRenderer::instance(); + return $this->choices !== null ? SelectHtmlRenderer::instance() : SimpleFieldHtmlRenderer::instance(); } /** diff --git a/src/Leaf/View/SimpleFieldHtmlRenderer.php b/src/Leaf/View/SimpleFieldHtmlRenderer.php index d630fa5..c33e2c5 100644 --- a/src/Leaf/View/SimpleFieldHtmlRenderer.php +++ b/src/Leaf/View/SimpleFieldHtmlRenderer.php @@ -9,6 +9,7 @@ use Bdf\Form\View\FieldViewInterface; use Bdf\Form\View\FieldViewRendererInterface; use Bdf\Form\View\HtmlRenderer; +use Override; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\LessThanOrEqual; @@ -21,10 +22,7 @@ */ final class SimpleFieldHtmlRenderer implements FieldViewRendererInterface { - /** - * @var SimpleFieldHtmlRenderer|null - */ - private static $instance; + private static ?self $instance = null; /** * Map constraint class name to mapped attributes in form : @@ -32,7 +30,7 @@ final class SimpleFieldHtmlRenderer implements FieldViewRendererInterface * * @var string[][] */ - private $constraintMapping = [ + private array $constraintMapping = [ Length::class => ['min' => 'minlength', 'max' => 'maxlength'], LessThanOrEqual::class => ['value' => 'max'], GreaterThanOrEqual::class => ['value' => 'min'], @@ -44,16 +42,14 @@ final class SimpleFieldHtmlRenderer implements FieldViewRendererInterface * * @var string[] */ - private $typesMapping = [ + private array $typesMapping = [ IntegerElement::class => 'number', PhoneElement::class => 'tel', CsrfElement::class => 'hidden', EmailElement::class => 'email', ]; - /** - * {@inheritdoc} - */ + #[Override] public function render(FieldViewInterface $view, array $attributes): string { if (!isset($attributes['type'])) { @@ -98,10 +94,6 @@ private function constraintsToAttributes(array $constraints): array */ public static function instance(): self { - if (self::$instance) { - return self::$instance; - } - - return self::$instance = new self; + return self::$instance ??= new self; } } diff --git a/src/Phone/NotEmptyPhoneNumberValidator.php b/src/Phone/NotEmptyPhoneNumberValidator.php index 885b9ac..26e132f 100644 --- a/src/Phone/NotEmptyPhoneNumberValidator.php +++ b/src/Phone/NotEmptyPhoneNumberValidator.php @@ -4,18 +4,18 @@ use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumberUtil; +use Override; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\NotBlankValidator; /** * NotBlank implementation for PhoneNumber value + * + * @psalm-suppress PropertyNotSetInConstructor */ -class NotEmptyPhoneNumberValidator extends NotBlankValidator +final class NotEmptyPhoneNumberValidator extends NotBlankValidator { - /** - * @var PhoneNumberUtil - */ - private $formatter; + private readonly PhoneNumberUtil $formatter; /** @@ -27,10 +27,8 @@ public function __construct(?PhoneNumberUtil $formatter = null) $this->formatter = $formatter ?? PhoneNumberUtil::getInstance(); } - /** - * {@inheritdoc} - */ - public function validate($value, Constraint $constraint): void + #[Override] + public function validate(mixed $value, Constraint $constraint): void { if ($value instanceof PhoneNumber) { if ($value->hasRawInput()) { diff --git a/src/Phone/PhoneChildBuilder.php b/src/Phone/PhoneChildBuilder.php index d86a2fd..1549d30 100644 --- a/src/Phone/PhoneChildBuilder.php +++ b/src/Phone/PhoneChildBuilder.php @@ -16,12 +16,8 @@ class PhoneChildBuilder extends ChildBuilder /** * @var PhoneNumberFormat::*|null */ - private $saveFormat; - - /** - * @var bool - */ - private $formatIfInvalid = false; + private PhoneNumberFormat|int|null $saveFormat = null; + private bool $formatIfInvalid = false; /** * {@inheritdoc} @@ -32,7 +28,7 @@ public function __construct(string $name, ElementBuilderInterface $elementBuilde { parent::__construct($name, $elementBuilder, $registry); - $this->addTransformerProvider([$this, 'provideModelTransformer']); + $this->addTransformerProvider($this->provideModelTransformer(...)); } /** @@ -47,7 +43,7 @@ public function __construct(string $name, ElementBuilderInterface $elementBuilde * * @see PhoneChildBuilder::saveAsString() To enable string formating when filling the entity */ - public function formatIfInvalid(bool $formatIfInvalid = true): self + public function formatIfInvalid(bool $formatIfInvalid = true): static { $this->formatIfInvalid = $formatIfInvalid; @@ -79,7 +75,7 @@ public function formatIfInvalid(bool $formatIfInvalid = true): self * * @see PhoneNumberToStringTransformer */ - public function saveAsString($format = PhoneNumberFormat::E164): self + public function saveAsString(PhoneNumberFormat|int|null $format = PhoneNumberFormat::E164): static { $this->saveFormat = $format; diff --git a/src/Phone/PhoneElement.php b/src/Phone/PhoneElement.php index 3887da3..1318411 100644 --- a/src/Phone/PhoneElement.php +++ b/src/Phone/PhoneElement.php @@ -9,6 +9,7 @@ use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumberFormat; use libphonenumber\PhoneNumberUtil; +use Override; use TypeError; use function strtoupper; @@ -25,14 +26,10 @@ final class PhoneElement extends LeafElement { /** - * @var callable(PhoneElement):string + * @var callable(PhoneElement):(string|null) */ - private $regionResolver; - - /** - * @var PhoneNumberUtil - */ - private $formatter; + private readonly mixed $regionResolver; + private readonly PhoneNumberUtil $formatter; /** @@ -51,10 +48,8 @@ public function __construct(?ValueValidatorInterface $validator = null, ?Transfo $this->formatter = $formatter ?? PhoneNumberUtil::getInstance(); } - /** - * {@inheritdoc} - */ - protected function toPhp($httpValue) + #[Override] + protected function toPhp(mixed $httpValue): ?PhoneNumber { if ($httpValue === null || $httpValue === '') { return null; @@ -63,10 +58,8 @@ protected function toPhp($httpValue) return $this->parseValue($httpValue); } - /** - * {@inheritdoc} - */ - protected function toHttp($phpValue) + #[Override] + protected function toHttp(mixed $phpValue): ?string { if (!$phpValue) { return null; @@ -75,11 +68,7 @@ protected function toHttp($phpValue) return $phpValue->getRawInput() ?? $this->formatter->format($phpValue, PhoneNumberFormat::E164); } - /** - * {@inheritdoc} - * - * @return PhoneNumber|null - */ + #[Override] protected function tryCast($value): ?PhoneNumber { if ($value === null) { @@ -130,13 +119,11 @@ public function parseValue(string $rawPhoneNumber): PhoneNumber try { return $this->formatter->parse($rawPhoneNumber, $this->resolveRegion(), null, true); } catch (NumberParseException $e) { - return (new PhoneNumber())->setRawInput($rawPhoneNumber); + return new PhoneNumber()->setRawInput($rawPhoneNumber); } } - /** - * {@inheritdoc} - */ + #[Override] protected function parseConstraints(ValueValidatorInterface $validator): array { $result = parent::parseConstraints($validator); diff --git a/src/Phone/PhoneElementBuilder.php b/src/Phone/PhoneElementBuilder.php index ddf7792..c8693dc 100644 --- a/src/Phone/PhoneElementBuilder.php +++ b/src/Phone/PhoneElementBuilder.php @@ -12,17 +12,8 @@ use Bdf\Form\Validator\ValueValidatorInterface; use libphonenumber\PhoneNumberUtil; use libphonenumber\RegionCode; -use ReflectionClass; +use Override; use Symfony\Component\Validator\Constraint; -use TypeError; - -use function func_get_arg; -use function func_num_args; -use function is_array; -use function is_bool; -use function is_callable; -use function sprintf; -use function trigger_error; /** * Builder for a phone element @@ -45,27 +36,19 @@ class PhoneElementBuilder extends AbstractElementBuilder /** * @var callable(ElementInterface):string|null */ - private $regionResolver; - - /** - * @var PhoneNumberUtil|null - */ - private $formatter; + private mixed $regionResolver = null; + private ?PhoneNumberUtil $formatter = null; /** * Invalid phone number are allowed ? * (i.e. number value is not validated) - * - * @var bool */ - private $allowInvalidNumber = false; + private bool $allowInvalidNumber = false; /** * The error message or options for the ValidPhoneNumber constraint if the phone number is invalid - * - * @var string|null */ - private $invalidPhoneErrorMessage = null; + private ?string $invalidPhoneErrorMessage = null; /** @@ -77,56 +60,24 @@ public function __construct(?RegistryInterface $registry = null) { parent::__construct($registry); - $this->addConstraintsProvider([$this, 'providePhoneConstraint']); + $this->addConstraintsProvider($this->providePhoneConstraint(...)); } /** - * {@inheritdoc} - * - * @return $this + * @psalm-suppress MethodSignatureMismatch */ - public function required($options = null/*, ?bool $allowNull = null, ?callable $normalizer = null*/) + #[Override] + public function required(string|Constraint|null $message = null, ?bool $allowNull = null, ?callable $normalizer = null): static { - // @todo rename $options to $message on bdf-form 2.0 - if (is_array($options)) { - @trigger_error('Passing an array of options to required() is deprecated since 1.7. Pass the options as individual parameters instead.', E_USER_DEPRECATED); - } - - // @todo declare allowNull and normalizer as actual parameters on bdf-form 2.0 - $allowNull = func_num_args() > 1 ? func_get_arg(1) : null; - $normalizer = func_num_args() > 2 ? func_get_arg(2) : null; - - if ($allowNull !== null && !is_bool($allowNull)) { - throw new TypeError(sprintf('The "allowNull" option of required() must be a boolean or null, "%s" given.', get_debug_type($allowNull))); + if (!$message instanceof Constraint) { + $message = new NotEmptyPhoneNumber( + message: $message, + allowNull: $allowNull, + normalizer: $normalizer, + ); } - if ($normalizer !== null && !is_callable($normalizer)) { - throw new TypeError(sprintf('The "normalizer" option of required() must be a valid callable or null, "%s" given.', get_debug_type($normalizer))); - } - - if (!$options instanceof Constraint) { - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(NotEmptyPhoneNumber::class))->getConstructor()->getNumberOfParameters() === 1; - } - - if (is_array($options)) { - $message = $options['message'] ?? null; - $allowNull ??= $options['allowNull'] ?? null; - $normalizer ??= $options['normalizer'] ?? null; - } else { - $message = $options; - } - - $options = $isSf4 - ? new NotEmptyPhoneNumber(['message' => $message, 'allowNull' => $allowNull, 'normalizer' => $normalizer]) - : new NotEmptyPhoneNumber(null, $message, $allowNull, $normalizer) // The constructor is consistent from sf 5 to 8, so we can safely use ordered parameters. - ; - } - - return $this->satisfy($options); + return $this->satisfy($message); } /** @@ -142,7 +93,7 @@ public function required($options = null/*, ?bool $allowNull = null, ?callable $ * * @return $this */ - public function regionResolver(callable $regionResolver): self + public function regionResolver(callable $regionResolver): static { $this->regionResolver = $regionResolver; @@ -158,9 +109,9 @@ public function regionResolver(callable $regionResolver): self * * @see RegionCode */ - public function region(string $region): self + public function region(string $region): static { - return $this->regionResolver(function () use($region) { return $region; }); + return $this->regionResolver(static fn() => $region); } /** @@ -185,7 +136,7 @@ public function region(string $region): self * @see FieldPath::parse() For the path syntax * @see ChildBuilderInterface::depends() For declare the dependency to the other field */ - public function regionInput(string $inputPath): self + public function regionInput(string $inputPath): static { return $this->regionResolver(function (ElementInterface $element) use($inputPath) { return FieldPath::parse($inputPath)->value($element); @@ -199,7 +150,7 @@ public function regionInput(string $inputPath): self * * @return $this */ - public function formatter(PhoneNumberUtil $formatter): self + public function formatter(PhoneNumberUtil $formatter): static { $this->formatter = $formatter; @@ -214,7 +165,7 @@ public function formatter(PhoneNumberUtil $formatter): self * * @return $this */ - public function allowInvalidNumber(bool $allowInvalidNumber = true): self + public function allowInvalidNumber(bool $allowInvalidNumber = true): static { $this->allowInvalidNumber = $allowInvalidNumber; @@ -232,19 +183,13 @@ public function allowInvalidNumber(bool $allowInvalidNumber = true): self * $builder->validateNumber(['message' => 'My error']); // Also accept array of options * * - * @param array|string|null $message The error message or options for the ValidPhoneNumber constraint if the phone number is invalid + * @param string|null $message The error message if the phone number is invalid * * @return $this * @see ValidPhoneNumber */ - public function validateNumber($message = null): self + public function validateNumber(?string $message = null): static { - if (is_array($message)) { - @trigger_error(sprintf('Passing an array of options on %s is deprecated since 1.7 and will be removed on 2.0, use named arguments instead.', __METHOD__), E_USER_DEPRECATED); - - $message = $message['message'] ?? null; - } - $this->allowInvalidNumber = false; $this->invalidPhoneErrorMessage = $message; @@ -259,17 +204,15 @@ public function validateNumber($message = null): self * @return $this * @see ValidPhoneNumber::$message */ - public function errorMessage(string $message): self + public function errorMessage(string $message): static { $this->invalidPhoneErrorMessage = $message; return $this; } - /** - * {@inheritdoc} - */ - protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface + #[Override] + protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): PhoneElement { return new PhoneElement($validator, $transformer, $this->regionResolver, $this->formatter); } diff --git a/src/Phone/Transformer/PhoneNumberToStringTransformer.php b/src/Phone/Transformer/PhoneNumberToStringTransformer.php index fe97eef..80ec473 100644 --- a/src/Phone/Transformer/PhoneNumberToStringTransformer.php +++ b/src/Phone/Transformer/PhoneNumberToStringTransformer.php @@ -10,27 +10,20 @@ use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumberFormat; use libphonenumber\PhoneNumberUtil; +use Override; /** * Transformer PhoneNumber instance to string with a format */ #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] -final class PhoneNumberToStringTransformer implements TransformerInterface +final readonly class PhoneNumberToStringTransformer implements TransformerInterface { /** * @var PhoneNumberFormat::* */ - private $format; - - /** - * @var bool - */ - private $formatIfInvalid; - - /** - * @var PhoneNumberUtil|null - */ - private $formatter; + private PhoneNumberFormat|int $format; + private bool $formatIfInvalid; + private ?PhoneNumberUtil $formatter; /** * PhoneNumberToStringTransformer constructor. @@ -39,17 +32,15 @@ final class PhoneNumberToStringTransformer implements TransformerInterface * @param bool $formatIfInvalid * @param PhoneNumberUtil|null $formatter */ - public function __construct($format = PhoneNumberFormat::E164, bool $formatIfInvalid = false, ?PhoneNumberUtil $formatter = null) + public function __construct(PhoneNumberFormat|int $format = PhoneNumberFormat::E164, bool $formatIfInvalid = false, ?PhoneNumberUtil $formatter = null) { $this->format = $format; $this->formatIfInvalid = $formatIfInvalid; $this->formatter = $formatter; } - /** - * {@inheritdoc} - */ - public function transformToHttp($value, ElementInterface $input): ?PhoneNumber + #[Override] + public function transformToHttp(mixed $value, ElementInterface $input): ?PhoneNumber { if ($value === null) { return null; @@ -64,10 +55,8 @@ public function transformToHttp($value, ElementInterface $input): ?PhoneNumber return $formatter->parse($value, null, null, true); } - /** - * {@inheritdoc} - */ - public function transformFromHttp($value, ElementInterface $input): ?string + #[Override] + public function transformFromHttp(mixed $value, ElementInterface $input): ?string { if (!$value instanceof PhoneNumber) { return null; @@ -75,7 +64,7 @@ public function transformFromHttp($value, ElementInterface $input): ?string $formatter = $this->formatter ?? ($input instanceof PhoneElement ? $input->getFormatter() : PhoneNumberUtil::getInstance()); - if ((!$this->formatIfInvalid && !$formatter->isValidNumber($value)) || !$value->getNationalNumber()) { + if ((!$this->formatIfInvalid && !$formatter->isValidNumber($value)) || !$value->hasNationalNumber()) { return $value->getRawInput(); } diff --git a/src/Phone/ValidPhoneNumber.php b/src/Phone/ValidPhoneNumber.php index f18f3cd..d1207da 100644 --- a/src/Phone/ValidPhoneNumber.php +++ b/src/Phone/ValidPhoneNumber.php @@ -4,37 +4,25 @@ use Symfony\Component\Validator\Constraint; -use function is_array; -use function sprintf; -use function trigger_error; - /** * Check if the phone number is valid */ class ValidPhoneNumber extends Constraint { - const INVALID_PHONE_NUMBER_ERROR = '5169f03c-ec96-4e62-8651-9ee6766e0b5a'; + public const string INVALID_PHONE_NUMBER_ERROR = '5169f03c-ec96-4e62-8651-9ee6766e0b5a'; - protected const ERROR_NAMES = [self::INVALID_PHONE_NUMBER_ERROR => 'INVALID_PHONE_NUMBER_ERROR']; - protected static $errorNames = self::ERROR_NAMES; + protected const array ERROR_NAMES = [self::INVALID_PHONE_NUMBER_ERROR => 'INVALID_PHONE_NUMBER_ERROR']; /** * The error message * * @var string */ - public $message = 'The phone number is not valid.'; + public string $message = 'The phone number is not valid.'; - public function __construct($message = null) + public function __construct(?string $message = null) { - if (is_array($message)) { - @trigger_error(sprintf('Passing an array of options to the "%s" constraint is deprecated since version 1.7 and support for it will be removed in 2.0. Use named arguments instead.', __CLASS__), E_USER_DEPRECATED); - - $options = $message; - $message = $options['message'] ?? null; - } - - parent::__construct($options ?? null); + parent::__construct(); $this->message = $message ?? $this->message; } diff --git a/src/Phone/ValidPhoneNumberValidator.php b/src/Phone/ValidPhoneNumberValidator.php index c7a8fea..9b6b881 100644 --- a/src/Phone/ValidPhoneNumberValidator.php +++ b/src/Phone/ValidPhoneNumberValidator.php @@ -4,19 +4,19 @@ use libphonenumber\PhoneNumber as PhoneNumberValue; use libphonenumber\PhoneNumberUtil; +use Override; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; /** * Validator for @see ValidPhoneNumber + * + * @psalm-suppress PropertyNotSetInConstructor */ -class ValidPhoneNumberValidator extends ConstraintValidator +final class ValidPhoneNumberValidator extends ConstraintValidator { - /** - * @var PhoneNumberUtil - */ - private $formatter; + private readonly PhoneNumberUtil $formatter; /** @@ -28,10 +28,8 @@ public function __construct(?PhoneNumberUtil $formatter = null) $this->formatter = $formatter ?? PhoneNumberUtil::getInstance(); } - /** - * {@inheritdoc} - */ - public function validate($value, Constraint $constraint): void + #[Override] + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof ValidPhoneNumber) { throw new UnexpectedTypeException($constraint, ValidPhoneNumber::class); diff --git a/src/PropertyAccess/AbstractAccessor.php b/src/PropertyAccess/AbstractAccessor.php index bc196d6..bfdde65 100644 --- a/src/PropertyAccess/AbstractAccessor.php +++ b/src/PropertyAccess/AbstractAccessor.php @@ -3,49 +3,38 @@ namespace Bdf\Form\PropertyAccess; use Bdf\Form\Child\ChildInterface; +use Override; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use function is_string; + /** * Base access implementation */ abstract class AbstractAccessor implements AccessorInterface { - /** - * @var string|null - */ - private $propertyName; + private ?string $propertyName = null; /** * @var callable|null */ - protected $transformer; + protected mixed $transformer = null; /** * @var callable|null */ - protected $customAccessor; - - /** - * @var PropertyAccessorInterface - */ - protected $propertyAccessor; + protected mixed $customAccessor = null; + protected ?PropertyAccessorInterface $propertyAccessor = null; + protected ?ChildInterface $input = null; /** - * @var ChildInterface|null - */ - protected $input; - - - /** - * Getter constructor. - * - * @param string|callable $propertyName + * @param string|callable|null $propertyName * @param callable|null $transformer * @param callable|null $customAccessor */ - public function __construct($propertyName = null, ?callable $transformer = null, ?callable $customAccessor = null) + public function __construct(string|callable|null $propertyName = null, ?callable $transformer = null, ?callable $customAccessor = null) { - if (is_callable($propertyName)) { + if ($propertyName !== null && !is_string($propertyName)) { $customAccessor = $transformer; $transformer = $propertyName; $propertyName = null; @@ -56,17 +45,13 @@ public function __construct($propertyName = null, ?callable $transformer = null, $this->customAccessor = $customAccessor; } - /** - * {@inheritdoc} - */ + #[Override] final public function setPropertyAccessor(PropertyAccessorInterface $propertyAccessor): void { $this->propertyAccessor = $propertyAccessor; } - /** - * {@inheritdoc} - */ + #[Override] final public function setFormElement(?ChildInterface $formElement): void { $this->input = $formElement; @@ -78,7 +63,7 @@ final public function setFormElement(?ChildInterface $formElement): void * @param array|object $target * @return string */ - final protected function prepareAccessorPath($target): string + final protected function prepareAccessorPath(array|object $target): string { $propertyName = $this->getPropertyName(); @@ -100,7 +85,7 @@ final protected function prepareAccessorPath($target): string * * @return string */ - final protected function getPropertyName() + final protected function getPropertyName(): string { if ($this->propertyName === null && $this->input) { $this->propertyName = $this->input->name(); diff --git a/src/PropertyAccess/ExtractorInterface.php b/src/PropertyAccess/ExtractorInterface.php index 27f1808..b54087e 100755 --- a/src/PropertyAccess/ExtractorInterface.php +++ b/src/PropertyAccess/ExtractorInterface.php @@ -7,7 +7,7 @@ */ interface ExtractorInterface extends AccessorInterface { - const EXTRACTION = 'extraction'; + public const string EXTRACTION = 'extraction'; /** * Extract the target property value @@ -16,5 +16,5 @@ interface ExtractorInterface extends AccessorInterface * * @return mixed The extracted value */ - public function extract($source); + public function extract(array|object $source)/*: mixed*/; } diff --git a/src/PropertyAccess/Getter.php b/src/PropertyAccess/Getter.php index 302c7a4..3f2836e 100644 --- a/src/PropertyAccess/Getter.php +++ b/src/PropertyAccess/Getter.php @@ -3,6 +3,9 @@ namespace Bdf\Form\PropertyAccess; use Attribute; +use Override; + +use function assert; /** * Extract a property value and import it into the form element @@ -35,18 +38,17 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class Getter extends AbstractAccessor implements ExtractorInterface { - /** - * {@inheritdoc} - */ - public function extract($source) + #[Override] + public function extract(array|object $source): mixed { if ($this->customAccessor !== null) { $value = ($this->customAccessor)($source, null, self::EXTRACTION, $this); } else { + assert($this->propertyAccessor !== null); $value = $this->propertyAccessor->getValue($source, $this->prepareAccessorPath($source)); } - if ($this->transformer) { + if ($this->transformer !== null) { $value = ($this->transformer)($value, $this->input); } diff --git a/src/PropertyAccess/HydratorInterface.php b/src/PropertyAccess/HydratorInterface.php index 3fcb8f1..6a70c07 100755 --- a/src/PropertyAccess/HydratorInterface.php +++ b/src/PropertyAccess/HydratorInterface.php @@ -7,7 +7,7 @@ */ interface HydratorInterface extends AccessorInterface { - const HYDRATION = 'hydration'; + public const string HYDRATION = 'hydration'; /** * Hydrate the target @@ -17,5 +17,5 @@ interface HydratorInterface extends AccessorInterface * * @return void */ - public function hydrate(&$target, $value)/*: void*/; + public function hydrate(array|object &$target, mixed $value)/*: void*/; } diff --git a/src/PropertyAccess/Setter.php b/src/PropertyAccess/Setter.php index 5d11130..772c316 100644 --- a/src/PropertyAccess/Setter.php +++ b/src/PropertyAccess/Setter.php @@ -3,6 +3,9 @@ namespace Bdf\Form\PropertyAccess; use Attribute; +use Override; + +use function assert; /** * Set the property value using the element value @@ -35,18 +38,17 @@ #[Attribute(Attribute::TARGET_PROPERTY)] final class Setter extends AbstractAccessor implements HydratorInterface { - /** - * {@inheritdoc} - */ - public function hydrate(&$target, $value): void + #[Override] + public function hydrate(array|object &$target, mixed $value): void { - if ($this->transformer) { + if ($this->transformer !== null) { $value = ($this->transformer)($value, $this->input); } if ($this->customAccessor !== null) { ($this->customAccessor)($target, $value, self::HYDRATION, $this); } else { + assert($this->propertyAccessor !== null); $this->propertyAccessor->setValue($target, $this->prepareAccessorPath($target), $value); } } diff --git a/src/Registry/Registry.php b/src/Registry/Registry.php index 4b68f6e..0b7aa04 100755 --- a/src/Registry/Registry.php +++ b/src/Registry/Registry.php @@ -11,14 +11,11 @@ use Bdf\Form\Button\SubmitButtonBuilder; use Bdf\Form\Child\ChildBuilder; use Bdf\Form\Child\ChildBuilderInterface; -use Bdf\Form\Constraint\Closure; use Bdf\Form\Csrf\CsrfElement; use Bdf\Form\Csrf\CsrfElementBuilder; use Bdf\Form\Custom\CustomForm; use Bdf\Form\Custom\CustomFormBuilder; use Bdf\Form\ElementBuilderInterface; -use Bdf\Form\Filter\ClosureFilter; -use Bdf\Form\Filter\FilterInterface; use Bdf\Form\Leaf\AnyElement; use Bdf\Form\Leaf\AnyElementBuilder; use Bdf\Form\Leaf\BooleanElement; @@ -39,25 +36,21 @@ use Bdf\Form\Phone\PhoneChildBuilder; use Bdf\Form\Phone\PhoneElement; use Bdf\Form\Phone\PhoneElementBuilder; -use Bdf\Form\Transformer\ClosureTransformer; -use Bdf\Form\Transformer\DataTransformerAdapter; -use Bdf\Form\Transformer\TransformerInterface; use InvalidArgumentException; -use LogicException; -use Symfony\Component\Form\DataTransformerInterface; -use Symfony\Component\Validator\Constraint; +use Override; -use function trigger_error; +use function is_string; +use function is_subclass_of; /** * Base registry interface */ -class Registry implements RegistryInterface +final class Registry implements RegistryInterface { /** * @var class-string[]|callable[] */ - private $elementBuilderFactories = [ + private array $elementBuilderFactories = [ StringElement::class => StringElementBuilder::class, IntegerElement::class => IntegerElementBuilder::class, FloatElement::class => FloatElementBuilder::class, @@ -79,7 +72,7 @@ class Registry implements RegistryInterface /** * @var class-string[]|callable[] */ - private $childBuilderFactories = [ + private array $childBuilderFactories = [ DateTimeElement::class => DateTimeChildBuilder::class, PhoneElement::class => PhoneChildBuilder::class, ArrayElement::class => ArrayChildBuilder::class, @@ -96,82 +89,7 @@ public function __construct() }); } - /** - * {@inheritdoc} - */ - public function filter($filter): FilterInterface - { - if ($filter instanceof FilterInterface) { - return $filter; - } - - @trigger_error('Using the registry to create filters is deprecated since 1.7. Instantiate the filter directly instead of using the registry.', E_USER_DEPRECATED); - - if (is_callable($filter)) { - return new ClosureFilter($filter); - } - - // @todo container ? - /** @var class-string $filter */ - return new $filter(); - } - - /** - * {@inheritdoc} - */ - public function constraint($constraint): Constraint - { - if ($constraint instanceof Constraint) { - return $constraint; - } - - @trigger_error('Using the registry to create constraints is deprecated since 1.7. Instantiate the constraint directly instead of using the registry.', E_USER_DEPRECATED); - - if (is_callable($constraint)) { - return new Closure(['callback' => $constraint]); - } - - if (is_array($constraint)) { - $options = $constraint[1]; - $constraint = $constraint[0]; - - if (is_string($options)) { - $options = ['message' => $options]; - } - - /** @var class-string $constraint */ - return new $constraint($options); - } - - /** @var class-string $constraint */ - return new $constraint(); - } - - /** - * {@inheritdoc} - */ - public function transformer($transformer): TransformerInterface - { - if ($transformer instanceof TransformerInterface) { - return $transformer; - } - - @trigger_error('Using the registry to create transformers is deprecated since 1.7. Instantiate the transformer directly instead of using the registry.', E_USER_DEPRECATED); - - if ($transformer instanceof DataTransformerInterface) { - return new DataTransformerAdapter($transformer); - } - - if (is_callable($transformer)) { - return new ClosureTransformer($transformer); - } - - throw new LogicException('Invalid view transformer given for input '.var_export($transformer, true)); - } - - /** - * {@inheritdoc} - */ + #[Override] public function childBuilder(string $element, string $name): ChildBuilderInterface { $elementBuilder = $this->elementBuilder($element); @@ -193,6 +111,7 @@ public function childBuilder(string $element, string $name): ChildBuilderInterfa * @psalm-template E as \Bdf\Form\ElementInterface * @psalm-return ElementBuilderInterface */ + #[Override] public function elementBuilder(string $element): ElementBuilderInterface { $builderFactory = null; @@ -208,7 +127,7 @@ public function elementBuilder(string $element): ElementBuilderInterface } } - if (!$builderFactory) { + if ($builderFactory === null) { throw new InvalidArgumentException('The element '.$element.' is not registered'); } @@ -220,9 +139,7 @@ public function elementBuilder(string $element): ElementBuilderInterface return ($builderFactory)($this, $element); } - /** - * {@inheritdoc} - */ + #[Override] public function buttonBuilder(string $name): ButtonBuilderInterface { return new SubmitButtonBuilder($name); @@ -252,7 +169,7 @@ public function buttonBuilder(string $name): ButtonBuilderInterface * * @see Registry::elementBuilder() */ - public function register(string $elementType, $builderFactory, $childBuilderFactory = null): void + public function register(string $elementType, string|callable $builderFactory, string|callable|null $childBuilderFactory = null): void { $this->elementBuilderFactories[$elementType] = $builderFactory; diff --git a/src/Registry/RegistryInterface.php b/src/Registry/RegistryInterface.php index d5b22b3..ab46583 100644 --- a/src/Registry/RegistryInterface.php +++ b/src/Registry/RegistryInterface.php @@ -5,49 +5,12 @@ use Bdf\Form\Button\ButtonBuilderInterface; use Bdf\Form\Child\ChildBuilderInterface; use Bdf\Form\ElementBuilderInterface; -use Bdf\Form\Filter\FilterInterface; -use Bdf\Form\Transformer\TransformerInterface; -use LogicException; -use Symfony\Component\Form\DataTransformerInterface; -use Symfony\Component\Validator\Constraint; /** * Registry for form components */ interface RegistryInterface { - /** - * Create the filter - * - * @param FilterInterface|callable|string $filter - * - * @return FilterInterface - * @deprecated Since 1.7. Instantiate the filter directly instead of using the registry. - */ - public function filter($filter): FilterInterface; - - /** - * Create the constraint - * - * @param Constraint|callable|array|string $constraint - * - * @return Constraint - * @deprecated Since 1.7. Instantiate the constraint directly instead of using the registry. - */ - public function constraint($constraint): Constraint; - - /** - * Create a view transformer - * - * @param callable|TransformerInterface|DataTransformerInterface $transformer - * - * @return TransformerInterface - * - * @throws LogicException If the transformer is invalid - * @deprecated Since 1.7. Instantiate the transformer directly instead of using the registry. - */ - public function transformer($transformer): TransformerInterface; - /** * Create the child builder * diff --git a/src/RootElementInterface.php b/src/RootElementInterface.php index 2469ab2..2a64abc 100644 --- a/src/RootElementInterface.php +++ b/src/RootElementInterface.php @@ -12,9 +12,6 @@ * * @see ElementInterface::root() To get the root instance * - * @method void set(string $flag, mixed $value) Define a flag value - * @method bool is(string $flag) Check if a flag is defined - * * @extends ElementInterface */ interface RootElementInterface extends ElementInterface @@ -94,7 +91,7 @@ public function constraintGroups(): array; * * @return void */ - //public function set(string $flag, bool $value): void; + public function set(string $flag, bool $value): void; /** * Check a flag value @@ -104,5 +101,5 @@ public function constraintGroups(): array; * * @return bool Flag value */ - //public function is(string $flag): bool; + public function is(string $flag): bool; } diff --git a/src/Transformer/ClosureTransformer.php b/src/Transformer/ClosureTransformer.php index 42eba03..7be10a2 100755 --- a/src/Transformer/ClosureTransformer.php +++ b/src/Transformer/ClosureTransformer.php @@ -5,6 +5,7 @@ use Bdf\Form\ElementBuilderInterface; use Bdf\Form\ElementInterface; use Bdf\Form\Registry\RegistryInterface; +use Override; /** * Wrap a closure into a Transformer @@ -18,13 +19,12 @@ * @see RegistryInterface::transformer() With callbable should return a ClosureTransformer * @see ElementBuilderInterface::transformer() For register a transformer on an element */ -final class ClosureTransformer implements TransformerInterface +final readonly class ClosureTransformer implements TransformerInterface { /** * @var callable */ - private $callback; - + private mixed $callback; /** * @param callable $callback @@ -34,18 +34,14 @@ public function __construct(callable $callback) $this->callback = $callback; } - /** - * {@inheritdoc} - */ - public function transformToHttp($value, ElementInterface $input) + #[Override] + public function transformToHttp(mixed $value, ElementInterface $input): mixed { return ($this->callback)($value, $input, false); } - /** - * {@inheritdoc} - */ - public function transformFromHttp($value, ElementInterface $input) + #[Override] + public function transformFromHttp(mixed $value, ElementInterface $input): mixed { return ($this->callback)($value, $input, true); } diff --git a/src/Transformer/DataTransformerAdapter.php b/src/Transformer/DataTransformerAdapter.php index 259310f..6db4f74 100755 --- a/src/Transformer/DataTransformerAdapter.php +++ b/src/Transformer/DataTransformerAdapter.php @@ -3,43 +3,29 @@ namespace Bdf\Form\Transformer; use Bdf\Form\ElementInterface; +use Override; use Symfony\Component\Form\DataTransformerInterface; /** * Adapter for Symfony data transformer to bdf transformer */ -final class DataTransformerAdapter implements TransformerInterface +final readonly class DataTransformerAdapter implements TransformerInterface { - /** - * The symfony data transformer - * - * @var DataTransformerInterface - */ - private $transformer; + public function __construct( + /** + * The symfony data transformer + */ + private DataTransformerInterface $transformer, + ) {} - - /** - * Set the symfony data transformer - * - * @param DataTransformerInterface $transformer - */ - public function __construct(DataTransformerInterface $transformer) - { - $this->transformer = $transformer; - } - - /** - * {@inheritdoc} - */ - public function transformToHttp($value, ElementInterface $input) + #[Override] + public function transformToHttp(mixed $value, ElementInterface $input): mixed { return $this->transformer->transform($value); } - /** - * {@inheritdoc} - */ - public function transformFromHttp($value, ElementInterface $input) + #[Override] + public function transformFromHttp(mixed $value, ElementInterface $input): mixed { return $this->transformer->reverseTransform($value); } diff --git a/src/Transformer/NullTransformer.php b/src/Transformer/NullTransformer.php index 1aeeaf0..9a28635 100644 --- a/src/Transformer/NullTransformer.php +++ b/src/Transformer/NullTransformer.php @@ -3,29 +3,21 @@ namespace Bdf\Form\Transformer; use Bdf\Form\ElementInterface; +use Override; /** * Null object for form transformer */ -final class NullTransformer implements TransformerInterface +final readonly class NullTransformer implements TransformerInterface { - /** - * @var NullTransformer|null - */ - private static $instance; - - /** - * {@inheritdoc} - */ - public function transformToHttp($value, ElementInterface $input) + #[Override] + public function transformToHttp(mixed $value, ElementInterface $input): mixed { return $value; } - /** - * {@inheritdoc} - */ - public function transformFromHttp($value, ElementInterface $input) + #[Override] + public function transformFromHttp(mixed $value, ElementInterface $input): mixed { return $value; } @@ -37,10 +29,8 @@ public function transformFromHttp($value, ElementInterface $input) */ public static function instance(): self { - if (self::$instance) { - return self::$instance; - } + static $instance = new self(); - return self::$instance = new self(); + return $instance; } } diff --git a/src/Transformer/TransformerAggregate.php b/src/Transformer/TransformerAggregate.php index 2e95c01..559bbe1 100644 --- a/src/Transformer/TransformerAggregate.php +++ b/src/Transformer/TransformerAggregate.php @@ -3,6 +3,12 @@ namespace Bdf\Form\Transformer; use Bdf\Form\ElementInterface; +use Override; + +use function array_unshift; +use function count; +use function sprintf; +use function trigger_error; /** * Aggregation of transformers @@ -10,28 +16,17 @@ * - The transformers are applied in order for transform from PHP to HTTP value * - For transform from HTTP to PHP, the transformers are applied in reverse order */ -final class TransformerAggregate implements TransformerInterface +final /*readonly*/ class TransformerAggregate implements TransformerInterface { - /** - * @var TransformerInterface[] - */ - private $transformers; - - - /** - * DataTransformerChain constructor. - * - * @param TransformerInterface[] $transformers - */ - public function __construct(array $transformers) - { - $this->transformers = $transformers; - } + public function __construct( + /** + * @var TransformerInterface[] + */ + private array $transformers, + ) {} - /** - * {@inheritdoc} - */ - public function transformToHttp($value, ElementInterface $input) + #[Override] + public function transformToHttp(mixed $value, ElementInterface $input): mixed { foreach ($this->transformers as $transformer) { $value = $transformer->transformToHttp($value, $input); @@ -40,10 +35,8 @@ public function transformToHttp($value, ElementInterface $input) return $value; } - /** - * {@inheritdoc} - */ - public function transformFromHttp($value, ElementInterface $input) + #[Override] + public function transformFromHttp(mixed $value, ElementInterface $input): mixed { for ($i = count($this->transformers) - 1; $i >= 0; --$i) { $value = $this->transformers[$i]->transformFromHttp($value, $input); @@ -56,9 +49,11 @@ public function transformFromHttp($value, ElementInterface $input) * Add a transformer at the head of the transformer list * * @param TransformerInterface $transformer + * @deprecated since 2.0, the class will be marked as readonly in 3.0 */ public function prepend(TransformerInterface $transformer): void { + @trigger_error(sprintf('Modifying "%s" is deprecated since 2.0, the class will be marked as readonly in 3.0.', self::class), E_USER_DEPRECATED); array_unshift($this->transformers, $transformer); } @@ -66,9 +61,11 @@ public function prepend(TransformerInterface $transformer): void * Add a transformer at the end of the transformer list * * @param TransformerInterface $transformer + * @deprecated since 2.0, the class will be marked as readonly in 3.0 */ public function append(TransformerInterface $transformer): void { + @trigger_error(sprintf('Modifying "%s" is deprecated since 2.0, the class will be marked as readonly in 3.0.', self::class), E_USER_DEPRECATED); $this->transformers[] = $transformer; } } diff --git a/src/Transformer/TransformerInterface.php b/src/Transformer/TransformerInterface.php index b7fbd90..a7936b9 100755 --- a/src/Transformer/TransformerInterface.php +++ b/src/Transformer/TransformerInterface.php @@ -17,7 +17,7 @@ interface TransformerInterface * * @return mixed The view value */ - public function transformToHttp($value, ElementInterface $input); + public function transformToHttp(mixed $value, ElementInterface $input)/*: mixed*/; /** * Http to model transformation @@ -27,5 +27,5 @@ public function transformToHttp($value, ElementInterface $input); * * @return mixed The model value */ - public function transformFromHttp($value, ElementInterface $input); + public function transformFromHttp(mixed $value, ElementInterface $input)/*: mixed*/; } diff --git a/src/Util/ContainerTrait.php b/src/Util/ContainerTrait.php index 48703fa..a64b2a7 100644 --- a/src/Util/ContainerTrait.php +++ b/src/Util/ContainerTrait.php @@ -14,7 +14,7 @@ trait ContainerTrait /** * @var WeakReference|null */ - private $container; + private ?WeakReference $container = null; /** @@ -22,7 +22,7 @@ trait ContainerTrait */ final public function container(): ?ChildInterface { - return $this->container ? $this->container->get() : null; + return $this->container?->get(); } /** diff --git a/src/Util/DelegateElementBuilderTrait.php b/src/Util/DelegateElementBuilderTrait.php index f738d07..d05b0d3 100644 --- a/src/Util/DelegateElementBuilderTrait.php +++ b/src/Util/DelegateElementBuilderTrait.php @@ -3,6 +3,8 @@ namespace Bdf\Form\Util; use Bdf\Form\ElementInterface; +use Bdf\Form\Transformer\TransformerInterface; +use Symfony\Component\Validator\Constraint; /** * Simple implementation of delegated element builder @@ -16,9 +18,9 @@ trait DelegateElementBuilderTrait /** * {@inheritdoc} */ - final public function satisfy($constraint, $options = null, bool $append = true) + final public function satisfy(Constraint|callable $constraint, ?string $message = null, bool $append = true): static { - $this->getElementBuilder()->satisfy($constraint, $options, $append); + $this->getElementBuilder()->satisfy($constraint, $message, $append); return $this; } @@ -26,7 +28,7 @@ final public function satisfy($constraint, $options = null, bool $append = true) /** * {@inheritdoc} */ - final public function transformer($transformer, bool $append = true) + final public function transformer(callable|TransformerInterface $transformer, bool $append = true): static { $this->getElementBuilder()->transformer($transformer, $append); @@ -36,7 +38,7 @@ final public function transformer($transformer, bool $append = true) /** * {@inheritdoc} */ - final public function value($value) + final public function value(mixed $value): static { $this->getElementBuilder()->value($value); diff --git a/src/Util/FieldFinderTrait.php b/src/Util/FieldFinderTrait.php index 4a50771..5df08d4 100644 --- a/src/Util/FieldFinderTrait.php +++ b/src/Util/FieldFinderTrait.php @@ -68,7 +68,7 @@ public function findField(string $path): ?ElementInterface * * @see FieldPath::parse() For the path syntax */ - public function findFieldValue(string $path) + public function findFieldValue(string $path): mixed { return $this->fieldPath($path)->value($this); } diff --git a/src/Util/FieldPath.php b/src/Util/FieldPath.php index e545d28..e2cdd29 100644 --- a/src/Util/FieldPath.php +++ b/src/Util/FieldPath.php @@ -14,26 +14,22 @@ */ final class FieldPath { - const SELF_ELEMENT = '.'; - const PARENT_ELEMENT = '..'; - const SEPARATOR = '/'; + public const string SELF_ELEMENT = '.'; + public const string PARENT_ELEMENT = '..'; + public const string SEPARATOR = '/'; /** * Paths cache * * @var FieldPath[] */ - private static $cache = []; + private static array $cache = []; /** * @var string[] */ - private $path; - - /** - * @var bool - */ - private $absolute; + private readonly array $path; + private readonly bool $absolute; /** @@ -57,7 +53,7 @@ public function __construct(array $path, bool $absolute) * * @return ElementInterface|null The resolved element, or null if cannot be found */ - public function resolve($currentElement): ?ElementInterface + public function resolve(ElementInterface|ChildInterface $currentElement): ?ElementInterface { if ($currentElement instanceof ChildInterface) { $currentElement = $currentElement->element(); diff --git a/src/Util/HttpValue.php b/src/Util/HttpValue.php index 9bfe60e..20c4768 100644 --- a/src/Util/HttpValue.php +++ b/src/Util/HttpValue.php @@ -15,7 +15,7 @@ final class HttpValue * * @return bool true if the value is empty */ - public static function isEmpty($value): bool + public static function isEmpty(mixed $value): bool { return $value === null || $value === '' || $value === []; } @@ -29,7 +29,7 @@ public static function isEmpty($value): bool * * @return mixed The value or the default */ - public static function orDefault($value, $default) + public static function orDefault(mixed $value, mixed $default): mixed { if ($default === null || !self::isEmpty($value)) { return $value; diff --git a/src/Util/MagicCallForwarding.php b/src/Util/MagicCallForwarding.php index 938d100..b46cb49 100644 --- a/src/Util/MagicCallForwarding.php +++ b/src/Util/MagicCallForwarding.php @@ -17,7 +17,7 @@ trait MagicCallForwarding * * @return $this|mixed */ - final public function __call(string $name, array $arguments) + final public function __call(string $name, array $arguments): mixed { $builder = $this->getElementBuilder(); $return = $builder->$name(...$arguments); diff --git a/src/Util/RootFlagsTrait.php b/src/Util/RootFlagsTrait.php index 87919df..cf19886 100644 --- a/src/Util/RootFlagsTrait.php +++ b/src/Util/RootFlagsTrait.php @@ -16,7 +16,7 @@ trait RootFlagsTrait * * @var array */ - private $flags = []; + private array $flags = []; /** * {@inheritdoc} diff --git a/src/Util/TransformerBuilderTrait.php b/src/Util/TransformerBuilderTrait.php index 3669f2b..8e3f9a1 100644 --- a/src/Util/TransformerBuilderTrait.php +++ b/src/Util/TransformerBuilderTrait.php @@ -9,8 +9,9 @@ use Bdf\Form\Transformer\TransformerAggregate; use Bdf\Form\Transformer\TransformerInterface; +use function array_unshift; +use function count; use function is_callable; -use function trigger_error; /** * Trait for implements builder of transformer @@ -18,30 +19,26 @@ trait TransformerBuilderTrait { /** - * @var array + * @var list */ - private $transformers = []; + private array $transformers = []; /** - * @var callable[] + * @var array */ - private $transformerProviders = []; + private array $transformerProviders = []; /** * {@inheritdoc} * * @see ElementBuilderInterface::transformer() */ - final public function transformer($transformer, bool $append = true) + final public function transformer(callable|TransformerInterface $transformer, bool $append = true): static { if (is_callable($transformer)) { $transformer = new ClosureTransformer($transformer); } - if (!$transformer instanceof TransformerInterface) { - @trigger_error('Passing a non transformer to transformer() is deprecated since 1.7. Pass a transformer instance, or a callback, instead of a class name.', E_USER_DEPRECATED); - } - if ($append === true) { $this->transformers[] = $transformer; } else { @@ -67,7 +64,7 @@ final public function transformer($transformer, bool $append = true) * }); * * - * @param callable(RegistryInterface):TransformerInterface[] $provider + * @param callable(RegistryInterface):(TransformerInterface[]) $provider */ final protected function addTransformerProvider(callable $provider): void { @@ -91,24 +88,19 @@ private function buildTransformer(): TransformerInterface $providedTransformers = []; foreach ($this->transformerProviders as $provider) { - $providedTransformers = array_merge($providedTransformers, $provider($this->registry())); + $providedTransformers = [...$providedTransformers, ...$provider($this->registry())]; } - $transformers = array_map([$this->registry(), 'transformer'], $this->transformers); + $transformers = $this->transformers; if (!empty($providedTransformers)) { - $transformers = array_merge($providedTransformers, $transformers); + $transformers = [...$providedTransformers, ...$transformers]; } - switch (count($transformers)) { - case 0: - return NullTransformer::instance(); - - case 1: - return $transformers[0]; - - default: - return new TransformerAggregate($transformers); - } + return match (count($transformers)) { + 0 => NullTransformer::instance(), + 1 => $transformers[0], + default => new TransformerAggregate($transformers), + }; } } diff --git a/src/Util/ValidatorBuilderTrait.php b/src/Util/ValidatorBuilderTrait.php index a99a7be..412b193 100644 --- a/src/Util/ValidatorBuilderTrait.php +++ b/src/Util/ValidatorBuilderTrait.php @@ -8,17 +8,11 @@ use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\TransformerExceptionConstraint; use Bdf\Form\Validator\ValueValidatorInterface; -use ReflectionClass; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\NotBlank; -use TypeError; -use function get_debug_type; -use function is_array; -use function is_bool; +use function array_unshift; use function is_callable; -use function sprintf; -use function trigger_error; /** * Trait for implements build of constraint validator @@ -28,19 +22,15 @@ trait ValidatorBuilderTrait { /** - * @var array + * @var array */ - private $constraints = []; + private array $constraints = []; + private ?TransformerExceptionConstraint $transformerExceptionConstraint = null; /** - * @var TransformerExceptionConstraint|null + * @var array */ - private $transformerExceptionConstraint; - - /** - * @var callable[] - */ - private $constraintsProviders = []; + private array $constraintsProviders = []; /** * Mark this input as required @@ -56,7 +46,7 @@ trait ValidatorBuilderTrait * $builder->required(['allowNull' => true]); // With custom options * * - * @param array|string|null $options The constraint option. Is a string is given, it will be used as error message + * @param string|Constraint|null $message The error message. A custom constraint can be passed. * @param bool|null $allowNull Allow null value (default false). * @param callable|null $normalizer A normalizer callback to apply on the value before validation. * @@ -64,48 +54,13 @@ trait ValidatorBuilderTrait * * @see NotBlank The used constraint */ - public function required($options = null/*, ?bool $allowNull = null, ?callable $normalizer = null*/) + public function required(string|Constraint|null $message = null, ?bool $allowNull = null, ?callable $normalizer = null): static { - // @todo rename $options to $message on bdf-form 2.0 - if (is_array($options)) { - @trigger_error('Passing an array of options to required() is deprecated since 1.7. Pass the options as individual parameters instead.', E_USER_DEPRECATED); - } - - // @todo declare allowNull and normalizer as actual parameters on bdf-form 2.0 - $allowNull = func_num_args() > 1 ? func_get_arg(1) : null; - $normalizer = func_num_args() > 2 ? func_get_arg(2) : null; - - if ($allowNull !== null && !is_bool($allowNull)) { - throw new TypeError(sprintf('The "allowNull" option of required() must be a boolean or null, "%s" given.', get_debug_type($allowNull))); - } - - if ($normalizer !== null && !is_callable($normalizer)) { - throw new TypeError(sprintf('The "normalizer" option of required() must be a valid callable or null, "%s" given.', get_debug_type($normalizer))); + if (!$message instanceof Constraint) { + $message = new NotBlank(message: $message, allowNull: $allowNull, normalizer: $normalizer); } - if (!$options instanceof Constraint) { - static $isSf4 = null; - - if ($isSf4 === null) { - /** @psalm-suppress PossiblyNullReference */ - $isSf4 = (new ReflectionClass(NotBlank::class))->getConstructor()->getNumberOfParameters() === 1; - } - - if (is_array($options)) { - $message = $options['message'] ?? null; - $allowNull ??= $options['allowNull'] ?? null; - $normalizer ??= $options['normalizer'] ?? null; - } else { - $message = $options; - } - - $options = $isSf4 - ? new NotBlank(['message' => $message, 'allowNull' => $allowNull, 'normalizer' => $normalizer]) - : new NotBlank(null, $message, $allowNull, $normalizer) // The constructor is consistent from sf 5 to 8, so we can safely use ordered parameters. - ; - } - - return $this->satisfy($options); + return $this->satisfy($message); } /** @@ -113,19 +68,10 @@ public function required($options = null/*, ?bool $allowNull = null, ?callable $ * * @see ElementBuilderInterface::satisfy() */ - final public function satisfy($constraint, $options = null, bool $append = true) + final public function satisfy(Constraint|callable $constraint, ?string $message = null, bool $append = true): static { - // @todo rename $options to $message in bdf-form 2.0 if (is_callable($constraint)) { - $constraint = new Closure($constraint, $options); - } - - if (!$constraint instanceof Constraint) { - @trigger_error('Passing a non constraint to satisfy() is deprecated since 1.7. Pass a constraint instance, or a callback, instead of a class name.', E_USER_DEPRECATED); - } - - if ($options !== null) { - $constraint = [$constraint, $options]; + $constraint = new Closure($constraint, $message); } if ($append === true) { @@ -147,7 +93,7 @@ final public function satisfy($constraint, $options = null, bool $append = true) * * @see TransformerExceptionConstraint::$ignoreException */ - final public function ignoreTransformerException(bool $flag = true) + final public function ignoreTransformerException(bool $flag = true): static { $this->getTransformerExceptionConstraint()->ignoreException = $flag; @@ -162,7 +108,7 @@ final public function ignoreTransformerException(bool $flag = true) * * @see TransformerExceptionConstraint::$message */ - final public function transformerErrorMessage(string $message) + final public function transformerErrorMessage(string $message): static { $this->getTransformerExceptionConstraint()->message = $message; @@ -177,7 +123,7 @@ final public function transformerErrorMessage(string $message) * * @see TransformerExceptionConstraint::$code */ - final public function transformerErrorCode(string $code) + final public function transformerErrorCode(string $code): static { $this->getTransformerExceptionConstraint()->code = $code; @@ -215,7 +161,7 @@ final public function transformerErrorCode(string $code) * * @see TransformerExceptionConstraint::$code */ - final public function transformerExceptionValidation(callable $validationCallback) + final public function transformerExceptionValidation(callable $validationCallback): static { $this->getTransformerExceptionConstraint()->validationCallback = $validationCallback; @@ -236,7 +182,7 @@ final public function transformerExceptionValidation(callable $validationCallbac * }); * * - * @param callable(RegistryInterface):Constraint[] $constraintsProvider + * @param callable(RegistryInterface):(Constraint[]) $constraintsProvider */ final protected function addConstraintsProvider(callable $constraintsProvider): void { @@ -257,37 +203,13 @@ private function getTransformerExceptionConstraint(): TransformerExceptionConstr return $this->transformerExceptionConstraint = $this->defaultTransformerExceptionConstraint(); } - /** - * Define the default constraints options for the TransformerExceptionConstraint - * This method should be overridden for define options - * - * @return array - * @deprecated Use {@see ValidatorBuilderTrait::defaultTransformerExceptionConstraint()} instead to define the default constraint with options. Will be removed in bdf-form 2.0 - */ - protected function defaultTransformerExceptionConstraintOptions(): array - { - return []; - } - /** * Define the default TransformerExceptionConstraint * This method should be overridden to define options */ protected function defaultTransformerExceptionConstraint(): TransformerExceptionConstraint { - $options = $this->defaultTransformerExceptionConstraintOptions(); - - if ($options !== []) { - @trigger_error(sprintf('The %s::defaultTransformerExceptionConstraintOptions() method is deprecated since 1.7. Override %s::defaultTransformerExceptionConstraint() instead to define the default constraint with options.', static::class, static::class), E_USER_DEPRECATED); - } - - return new TransformerExceptionConstraint( - $options['exception'] ?? null, - $options['message'] ?? null, - $options['code'] ?? null, - $options['validationCallback'] ?? null, - $options['ignoreException'] ?? null, - ); + return new TransformerExceptionConstraint(); } /** @@ -308,12 +230,10 @@ private function buildValidator(): ValueValidatorInterface $constraints = []; foreach ($this->constraintsProviders as $provider) { - $constraints = array_merge($constraints, $provider($registry)); + $constraints = [...$constraints, ...$provider($registry)]; } - foreach ($this->constraints as $constraint) { - $constraints[] = $registry->constraint($constraint); - } + $constraints = [...$constraints, ...$this->constraints]; return new ConstraintValueValidator($constraints, $this->getTransformerExceptionConstraint()); } diff --git a/src/Validator/ConstraintValueValidator.php b/src/Validator/ConstraintValueValidator.php index 64dc28f..32b00fa 100644 --- a/src/Validator/ConstraintValueValidator.php +++ b/src/Validator/ConstraintValueValidator.php @@ -5,6 +5,7 @@ use Bdf\Form\ElementInterface; use Bdf\Form\Error\FormError; use Exception; +use Override; use Symfony\Component\Validator\Constraint; /** @@ -14,22 +15,17 @@ * @template T * @implements ValueValidatorInterface */ -final class ConstraintValueValidator implements ValueValidatorInterface +final readonly class ConstraintValueValidator implements ValueValidatorInterface { - /** - * @var self - */ - private static $emptyInstance; - /** * @var Constraint[] */ - private $constraints; + private array $constraints; /** * @var TransformerExceptionConstraint */ - private $transformerExceptionConstraint; + private TransformerExceptionConstraint $transformerExceptionConstraint; /** @@ -44,9 +40,7 @@ public function __construct(array $constraints = [], ?TransformerExceptionConstr $this->transformerExceptionConstraint = $transformerExceptionConstraint ?? new TransformerExceptionConstraint(); } - /** - * {@inheritdoc} - */ + #[Override] public function validate($value, ElementInterface $element): FormError { if (!$this->constraints) { @@ -71,9 +65,7 @@ public function validate($value, ElementInterface $element): FormError return FormError::null(); } - /** - * {@inheritdoc} - */ + #[Override] public function onTransformerException(Exception $exception, $value, ElementInterface $element): FormError { if ($this->transformerExceptionConstraint->ignoreException) { @@ -95,20 +87,16 @@ public function onTransformerException(Exception $exception, $value, ElementInte return FormError::null(); } - /** - * {@inheritdoc} - */ + #[Override] public function constraints(): array { return $this->constraints; } - /** - * {@inheritdoc} - */ + #[Override] public function hasConstraints(): bool { - return !empty($this->constraints); + return $this->constraints !== []; } /** @@ -118,10 +106,8 @@ public function hasConstraints(): bool */ public static function empty(): self { - if (self::$emptyInstance) { - return self::$emptyInstance; - } + static $instance = new self(); - return self::$emptyInstance = new self(); + return $instance; } } diff --git a/src/Validator/TransformerExceptionConstraint.php b/src/Validator/TransformerExceptionConstraint.php index f0a4cdf..9e503e7 100644 --- a/src/Validator/TransformerExceptionConstraint.php +++ b/src/Validator/TransformerExceptionConstraint.php @@ -5,10 +5,6 @@ use Exception; use Symfony\Component\Validator\Constraint; -use function is_array; -use function sprintf; -use function trigger_error; - /** * @internal */ @@ -17,28 +13,27 @@ final class TransformerExceptionConstraint extends Constraint const TRANSFORM_ERROR = 'b5acab45-80b0-4808-8784-6577e37ac869'; protected const ERROR_NAMES = [self::TRANSFORM_ERROR => 'TRANSFORM_ERROR']; - protected static $errorNames = self::ERROR_NAMES; /** * The error message. If null, the exception's message will be taken * * @var string|null */ - public $message = null; + public ?string $message = null; /** * The error code * * @var string */ - public $code = self::TRANSFORM_ERROR; + public string $code = self::TRANSFORM_ERROR; /** * The transformer exception * - * @var Exception + * @var Exception|null */ - public $exception; + public ?Exception $exception = null; /** * Use to validate the exception @@ -48,7 +43,7 @@ final class TransformerExceptionConstraint extends Constraint * * @var callable(mixed,TransformerExceptionConstraint,\Bdf\Form\ElementInterface):bool|null */ - public $validationCallback; + public mixed $validationCallback = null; /** * Does the transformation error should be ignored ? @@ -56,17 +51,11 @@ final class TransformerExceptionConstraint extends Constraint * * @var bool */ - public $ignoreException = false; + public bool $ignoreException = false; - public function __construct($exception = null, ?string $message = null, ?string $code = null, ?callable $validationCallback = null, ?bool $ignoreException = null) + public function __construct(?Exception $exception = null, ?string $message = null, ?string $code = null, ?callable $validationCallback = null, ?bool $ignoreException = null) { - if (is_array($exception)) { - @trigger_error(sprintf('Passing an array of options to %s is deprecated since 1.7 and will not be supported in 2.0. Use named parameters instead.', __METHOD__), E_USER_DEPRECATED); - - $options = $exception; - } - - parent::__construct($options ?? null); + parent::__construct(); $this->exception = $exception ?? $this->exception; $this->message = $message ?? $this->message; @@ -75,14 +64,6 @@ public function __construct($exception = null, ?string $message = null, ?string $this->ignoreException = $ignoreException ?? $this->ignoreException; } - /** - * {@inheritdoc} - */ - public function getDefaultOption(): ?string - { - return 'exception'; - } - /** * Set the exception on the constraint * diff --git a/src/Validator/TransformerExceptionConstraintValidator.php b/src/Validator/TransformerExceptionConstraintValidator.php index 36eb211..69341b7 100644 --- a/src/Validator/TransformerExceptionConstraintValidator.php +++ b/src/Validator/TransformerExceptionConstraintValidator.php @@ -2,6 +2,8 @@ namespace Bdf\Form\Validator; +use LogicException; +use Override; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -11,22 +13,20 @@ */ final class TransformerExceptionConstraintValidator extends ConstraintValidator { - /** - * {@inheritdoc} - */ - public function validate($value, Constraint $constraint): void + #[Override] + public function validate(mixed $value, Constraint $constraint): void { if (!$constraint instanceof TransformerExceptionConstraint) { throw new UnexpectedTypeException($constraint, TransformerExceptionConstraint::class); } - if ($constraint->validationCallback) { + if ($constraint->validationCallback !== null) { if (!($constraint->validationCallback)($value, $constraint, $this->context->getRoot())) { return; } } - $this->context->buildViolation($constraint->message ?: $constraint->exception->getMessage()) + $this->context->buildViolation($constraint->message ?? $constraint->exception?->getMessage() ?? throw new LogicException('TransformerExceptionConstraint must have a message or an exception')) ->setCode($constraint->code) ->setParameter('{{ value }}', $this->formatValue($value)) ->addViolation() diff --git a/src/Validator/ValueValidatorInterface.php b/src/Validator/ValueValidatorInterface.php index 567a9c4..912eb10 100644 --- a/src/Validator/ValueValidatorInterface.php +++ b/src/Validator/ValueValidatorInterface.php @@ -24,7 +24,7 @@ interface ValueValidatorInterface * * @return FormError The error. Return an empty error if the value is valid */ - public function validate($value, ElementInterface $element): FormError; + public function validate(mixed $value, ElementInterface $element): FormError; /** * Handle a transformer exception @@ -35,7 +35,7 @@ public function validate($value, ElementInterface $element): FormError; * * @return FormError The real error */ - public function onTransformerException(Exception $exception, $value, ElementInterface $element): FormError; + public function onTransformerException(Exception $exception, mixed $value, ElementInterface $element): FormError; /** * Get validator constraints diff --git a/src/View/ConstraintsNormalizer.php b/src/View/ConstraintsNormalizer.php index 455555c..0037d6b 100644 --- a/src/View/ConstraintsNormalizer.php +++ b/src/View/ConstraintsNormalizer.php @@ -10,6 +10,7 @@ use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; +use function array_intersect_key; use function assert; use function get_object_vars; @@ -22,7 +23,7 @@ final class ConstraintsNormalizer /** * @var array, array> */ - static private $constraints = [ + static private array $constraints = [ NotBlank::class => [], Length::class => ['min' => null, 'max' => null], Count::class => ['min' => null, 'max' => null], @@ -46,7 +47,7 @@ public static function normalize(ValueValidatorInterface $validator): array $normalizedConstraints = []; foreach ($validator->constraints() as $constraint) { - $className = get_class($constraint); + $className = $constraint::class; if (isset(self::$constraints[$className])) { $normalizedConstraints[$className] = array_intersect_key(get_object_vars($constraint), self::$constraints[$className]); @@ -69,16 +70,13 @@ public static function normalize(ValueValidatorInterface $validator): array */ private static function getDefaultOption(Constraint $constraint): ?array { - $ctor = (new ReflectionClass($constraint))->getConstructor(); + $ctor = new ReflectionClass($constraint)->getConstructor(); assert($ctor !== null); $firstParam = $ctor->getParameters()[0] ?? null; - $firstParamName = $firstParam ? $firstParam->getName() : null; + $firstParamName = $firstParam?->getName(); - // Sf < 5.3 - if ($ctor->getNumberOfParameters() === 1 && $firstParamName === 'options') { - $option = $constraint->getDefaultOption(); - } elseif ($firstParamName !== 'options') { + if ($firstParamName !== 'options' && $firstParamName !== 'message') { $option = $firstParamName; } else { return null; @@ -89,7 +87,7 @@ private static function getDefaultOption(Constraint $constraint): ?array } // Cache the option name for the constraint class, to avoid reflection on next calls - self::$constraints[get_class($constraint)][$option] = null; + self::$constraints[$constraint::class][$option] = null; $value = $constraint->{$option} ?? null; diff --git a/src/View/ElementViewInterface.php b/src/View/ElementViewInterface.php index d4bded2..c131460 100644 --- a/src/View/ElementViewInterface.php +++ b/src/View/ElementViewInterface.php @@ -4,8 +4,6 @@ /** * Base form element view type - * - * @method self setError(?string $error) */ interface ElementViewInterface { @@ -35,9 +33,8 @@ public function error(): ?string; * * @return $this * @since 1.5 - * @todo uncomment in 2.0 */ - //public function setError(?string $error): self; + public function setError(?string $error): static; /** * Check if the current element is on error @@ -74,5 +71,5 @@ public function hasError(): bool; * * @see ElementViewInterface::hasError() To check if the element has an error */ - public function onError($action): ?string; + public function onError(string|callable $action): ?string; } diff --git a/src/View/ElementViewTrait.php b/src/View/ElementViewTrait.php index 4f29b15..7278624 100644 --- a/src/View/ElementViewTrait.php +++ b/src/View/ElementViewTrait.php @@ -14,12 +14,12 @@ trait ElementViewTrait /** * @var string */ - private $type; + private string $type; /** * @var string|null */ - private $error; + private ?string $error = null; /** * {@inheritdoc} @@ -40,7 +40,7 @@ public function error(): ?string /** * {@inheritdoc} */ - public function setError(?string $error): ElementViewInterface + public function setError(?string $error): static { $this->error = $error; @@ -58,7 +58,7 @@ public function hasError(): bool /** * {@inheritdoc} */ - public function onError($action): ?string + public function onError(string|callable $action): ?string { if (!$this->hasError()) { return null; diff --git a/src/View/FieldSetViewInterface.php b/src/View/FieldSetViewInterface.php index ba688e8..a9accaf 100644 --- a/src/View/FieldSetViewInterface.php +++ b/src/View/FieldSetViewInterface.php @@ -28,8 +28,6 @@ * * @extends ArrayAccess * @extends Traversable - * - * @method array errors() */ interface FieldSetViewInterface extends ElementViewInterface, ArrayAccess, Traversable { @@ -42,7 +40,6 @@ interface FieldSetViewInterface extends ElementViewInterface, ArrayAccess, Trave * * @return array * @since 1.5 - * @todo uncomment in 2.0 */ - //public function errors(): array; + public function errors(): array; } diff --git a/src/View/FieldSetViewTrait.php b/src/View/FieldSetViewTrait.php index e3af4c8..532d41f 100644 --- a/src/View/FieldSetViewTrait.php +++ b/src/View/FieldSetViewTrait.php @@ -18,12 +18,12 @@ trait FieldSetViewTrait /** * @var array */ - private $elements = []; + private array $elements = []; /** * {@inheritdoc} */ - public function offsetExists($offset): bool + public function offsetExists(mixed $offset): bool { return isset($this->elements[$offset]); } @@ -31,7 +31,7 @@ public function offsetExists($offset): bool /** * {@inheritdoc} */ - public function offsetGet($offset): ElementViewInterface + public function offsetGet(mixed $offset): ElementViewInterface { return $this->elements[$offset]; } @@ -39,7 +39,7 @@ public function offsetGet($offset): ElementViewInterface /** * {@inheritdoc} */ - public function offsetSet($offset, $value): void + public function offsetSet(mixed $offset, mixed $value): void { throw new BadMethodCallException('FormView is read only'); } @@ -47,7 +47,7 @@ public function offsetSet($offset, $value): void /** * {@inheritdoc} */ - public function offsetUnset($offset): void + public function offsetUnset(mixed $offset): void { throw new BadMethodCallException('FormView is read only'); } @@ -72,6 +72,8 @@ public function hasError(): bool /** * {@inheritdoc} + * + * @return array */ public function errors(): array { @@ -82,9 +84,9 @@ public function errors(): array continue; } - if ($element instanceof FieldSetViewInterface && method_exists($element, 'errors')) { + if ($element instanceof FieldSetViewInterface) { $errors[$name] = $element->errors(); - } elseif ($error = $element->error()) { + } elseif (($error = $element->error()) !== null) { $errors[$name] = $error; } } diff --git a/src/View/FieldViewInterface.php b/src/View/FieldViewInterface.php index e8f5383..6dedb34 100644 --- a/src/View/FieldViewInterface.php +++ b/src/View/FieldViewInterface.php @@ -5,12 +5,11 @@ use Bdf\Form\Choice\Choiceable; use Bdf\Form\Choice\ChoiceInterface; use Bdf\Form\Choice\ChoiceView; +use Override; /** * Base type for HTTP input / field * The implementations must be renderable - * - * @method self setValue($value) Override the value */ interface FieldViewInterface extends ElementViewInterface, Renderable { @@ -26,7 +25,7 @@ public function name(): string; * * @return mixed */ - public function value(); + public function value(): mixed; /** * Override the value @@ -36,9 +35,8 @@ public function value(); * * @return $this Return the current instance * @since 1.5 - * @todo uncomment in 2.0 */ - //public function setValue($value): self; + public function setValue(mixed $value): static; /** * Does the current field is required (i.e. the value must not be empty) @@ -94,5 +92,6 @@ public function choices(): ?array; * * @return string */ + #[Override] public function render(?FieldViewRendererInterface $renderer = null): string; } diff --git a/src/View/FieldViewTrait.php b/src/View/FieldViewTrait.php index ede5ae9..d267799 100644 --- a/src/View/FieldViewTrait.php +++ b/src/View/FieldViewTrait.php @@ -13,30 +13,19 @@ trait FieldViewTrait { use RenderableTrait; - /** - * @var string - */ - private $name; - - /** - * @var mixed - */ - private $value; - - /** - * @var bool - */ - private $required = false; + public private(set) string $name; + public private(set) mixed $value; + public private(set) bool $required = false; /** * @var array */ - private $constraints = []; + private array $constraints = []; /** * @var ChoiceView[]|null */ - private $choices; + public private(set) ?array $choices = null; /** * {@inheritdoc} @@ -49,7 +38,7 @@ public function name(): string /** * {@inheritdoc} */ - public function value() + public function value(): mixed { return $this->value; } @@ -57,7 +46,7 @@ public function value() /** * {@inheritdoc} */ - public function setValue($value): FieldViewInterface + public function setValue(mixed $value): static { $this->value = $value; diff --git a/src/View/Renderable.php b/src/View/Renderable.php index 071d9cf..51b6b31 100644 --- a/src/View/Renderable.php +++ b/src/View/Renderable.php @@ -7,8 +7,6 @@ /** * Base type for a renderable element of the form view tree - * - * @method self with(array $attributes) */ interface Renderable extends Stringable { @@ -20,17 +18,17 @@ interface Renderable extends Stringable * * @return $this */ - public function __call(string $name, array $arguments); + public function __call(string $name, array $arguments): static; /** * Define an attribute value * * @param string $name The attribute name - * @param string|bool $value The attribute value. Use true for a simple flag attribute + * @param scalar|bool $value The attribute value. Use true for a simple flag attribute * * @return $this */ - public function set(string $name, $value); + public function set(string $name, string|int|float|bool $value): static; /** * Define multiple attributes @@ -44,9 +42,8 @@ public function set(string $name, $value); * @param array $attributes * @return $this * @since 1.5 - * @todo uncomment in 2.0 */ - //public function with(array $attributes); + public function with(array $attributes): static; /** * Remove an attribute @@ -55,7 +52,7 @@ public function set(string $name, $value); * * @return $this */ - public function unset(string $name); + public function unset(string $name): static; /** * Get all defined attributes diff --git a/src/View/RenderableTrait.php b/src/View/RenderableTrait.php index 1ca124a..96b7dd3 100644 --- a/src/View/RenderableTrait.php +++ b/src/View/RenderableTrait.php @@ -16,14 +16,14 @@ trait RenderableTrait { /** - * @var array + * @var array */ - private $attributes = []; + private array $attributes = []; /** * {@inheritdoc} */ - public function __call(string $name, array $arguments) + public function __call(string $name, array $arguments): static { if (empty($arguments)) { throw new ArgumentCountError('Missing the attribute value.'); @@ -37,12 +37,8 @@ public function __call(string $name, array $arguments) /** * {@inheritdoc} */ - public function set(string $name, $value) + public function set(string $name, int|string|float|bool $value): static { - if (!is_scalar($value)) { - throw new TypeError('The attribute value must be a scalar value.'); - } - $this->attributes[$name] = $value; return $this; @@ -51,7 +47,7 @@ public function set(string $name, $value) /** * {@inheritdoc} */ - public function with(array $attributes) + public function with(array $attributes): static { foreach ($attributes as $name => $value) { if (!is_scalar($value)) { @@ -71,7 +67,7 @@ public function with(array $attributes) /** * {@inheritdoc} */ - public function unset(string $name) + public function unset(string $name): static { unset($this->attributes[$name]); diff --git a/tests/Aggregate/ArrayElementBuilderTest.php b/tests/Aggregate/ArrayElementBuilderTest.php index 51b7b98..2593d89 100644 --- a/tests/Aggregate/ArrayElementBuilderTest.php +++ b/tests/Aggregate/ArrayElementBuilderTest.php @@ -57,7 +57,7 @@ public function test_defaults() */ public function test_calling_undefined_method_should_be_forwarded_to_inner_element_builder() { - $element = $this->builder->length(['min' => 2])->buildElement(); + $element = $this->builder->length(min: 2)->buildElement(); $this->assertFalse($element->submit(['a', 'bb'])->valid()); $this->assertEquals(['This value is too short. It should have 2 characters or more.'], $element->error()->toArray()); @@ -167,7 +167,7 @@ public function test_phone_with_configurator() $phones = $element->submit(['0451236585', '0241578932'])->value(); - $this->assertContainsOnly(PhoneNumber::class, $phones); + $this->assertContainsOnlyInstancesOf(PhoneNumber::class, $phones); $this->assertCount(2, $phones); $this->assertEquals('+33451236585', PhoneNumberUtil::getInstance()->format($phones[0], PhoneNumberFormat::E164)); $this->assertEquals('+33241578932', PhoneNumberUtil::getInstance()->format($phones[1], PhoneNumberFormat::E164)); @@ -214,17 +214,6 @@ public function test_arrayTransformer() $this->assertSame(['bar' => 'foo'], $element->submit(['foo' => 'bar'])->value()); } - /** - * - */ - public function test_count_legacy() - { - $element = $this->builder->count(['min' => 3])->buildElement(); - - $this->assertFalse($element->submit(['foo', 'bar'])->valid()); - $this->assertTrue($element->submit(['foo', 'bar', 'baz'])->valid()); - } - /** * */ @@ -326,7 +315,7 @@ public function test_required_with_custom_message() */ public function test_required_with_custom_constraint() { - $element = $this->builder->required(new Count(['min' => 2]))->buildElement(); + $element = $this->builder->required(new Count(min: 2))->buildElement(); $element->submit([]); $this->assertEquals('This collection should contain 2 elements or more.', $element->error()->global()); diff --git a/tests/Aggregate/ArrayElementTest.php b/tests/Aggregate/ArrayElementTest.php index 0f45ea8..d511636 100644 --- a/tests/Aggregate/ArrayElementTest.php +++ b/tests/Aggregate/ArrayElementTest.php @@ -15,6 +15,7 @@ use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\TransformerExceptionConstraint; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Count; use Symfony\Component\Validator\Constraints\Length; @@ -154,7 +155,7 @@ public function test_submit_with_transformer_error_ignored() $element = new ArrayElement( new StringElement(), new ClosureTransformer(function () { throw new Exception('My error'); }), - new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])) + new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)) ); $this->assertTrue($element->submit(['foo', 'bar'])->valid()); @@ -169,7 +170,7 @@ public function test_submit_with_transformer_error_ignored_should_apply_other_co $element = new ArrayElement( new StringElement(), new ClosureTransformer(function () { throw new Exception('My error'); }), - new ConstraintValueValidator([new Closure(function () {return 'error';})], new TransformerExceptionConstraint(['ignoreException' => true])) + new ConstraintValueValidator([new Closure(function () {return 'error';})], new TransformerExceptionConstraint(ignoreException: true)) ); $this->assertFalse($element->submit(['foo', 'bar'])->valid()); @@ -182,7 +183,7 @@ public function test_submit_with_transformer_error_ignored_should_apply_other_co */ public function test_submit_with_array_error() { - $element = new ArrayElement(new StringElement(), null, new ConstraintValueValidator([new Count(['min' => 3])])); + $element = new ArrayElement(new StringElement(), null, new ConstraintValueValidator([new Count(min: 3)])); $this->assertFalse($element->submit(['foo', 'bar'])->valid()); $this->assertEquals('This collection should contain 3 elements or more.', $element->error()->global()); @@ -225,7 +226,7 @@ public function test_import_and_patch_null_will_keep_global_error() */ public function test_import_and_patch_null_will_keep_element_error() { - $element = (new ArrayElementBuilder())->satisfy(new Length(['min' => 3]))->buildElement(); + $element = (new ArrayElementBuilder())->satisfy(new Length(min: 3))->buildElement(); $element->submit(['a', 'bar']); @@ -291,7 +292,7 @@ public function test_patch_with_transformer_error() */ public function test_patch_with_array_error() { - $element = new ArrayElement(new StringElement(), null, new ConstraintValueValidator([new Count(['min' => 3])])); + $element = new ArrayElement(new StringElement(), null, new ConstraintValueValidator([new Count(min: 3)])); $this->assertFalse($element->patch(['foo', 'bar'])->valid()); $this->assertEquals('This collection should contain 3 elements or more.', $element->error()->global()); @@ -341,9 +342,7 @@ public function test_import_null() $this->assertSame([], $element->import(null)->value()); } - /** - * @dataProvider provideInvalidValue - */ + #[DataProvider('provideInvalidValue')] public function test_import_invalid_values($value) { $this->expectException(\TypeError::class); @@ -356,7 +355,7 @@ public function test_import_invalid_values($value) /** * */ - public function provideInvalidValue() + public static function provideInvalidValue() { return [ [''], @@ -501,7 +500,7 @@ public function test_view_simple() $this->assertFalse($view->hasError()); $this->assertEquals(['foo', 'bar', 'baz'], $view->value()); $this->assertCount(3, $view); - $this->assertContainsOnly(SimpleElementView::class, $view); + $this->assertContainsOnlyInstancesOf(SimpleElementView::class, $view); $this->assertEquals('foo', $view[0]->value()); $this->assertEquals('arr[0]', $view[0]->name()); $this->assertEquals('bar', $view[1]->value()); @@ -516,7 +515,7 @@ public function test_view_simple() $this->assertFalse($view->hasError()); $this->assertEquals(['foo' => 'bar', 'baz' => 'rab'], $view->value()); $this->assertCount(2, $view); - $this->assertContainsOnly(SimpleElementView::class, $view); + $this->assertContainsOnlyInstancesOf(SimpleElementView::class, $view); $this->assertEquals('bar', $view['foo']->value()); $this->assertEquals('arr[foo]', $view['foo']->name()); $this->assertEquals('rab', $view['baz']->value()); @@ -540,7 +539,7 @@ public function test_view_with_element_transformer_should_be_applied() $this->assertEquals('arr', $view->name()); $this->assertFalse($view->hasError()); $this->assertEquals(['Zm9v', 'YmFy'], $view->value()); - $this->assertContainsOnly(SimpleElementView::class, $view); + $this->assertContainsOnlyInstancesOf(SimpleElementView::class, $view); $this->assertEquals('Zm9v', $view[0]->value()); $this->assertEquals('arr[0]', $view[0]->name()); $this->assertEquals('YmFy', $view[1]->value()); diff --git a/tests/Aggregate/FormBuilderTest.php b/tests/Aggregate/FormBuilderTest.php index fdd9599..51464bc 100644 --- a/tests/Aggregate/FormBuilderTest.php +++ b/tests/Aggregate/FormBuilderTest.php @@ -309,7 +309,7 @@ public function test_required_with_custom_message() */ public function test_required_with_custom_constraint() { - $element = $this->builder->required(new Count(['min' => 2]))->buildElement(); + $element = $this->builder->required(new Count(min: 2))->buildElement(); $element->submit([]); $this->assertEquals('This collection should contain 2 elements or more.', $element->error()->global()); diff --git a/tests/Aggregate/FormTest.php b/tests/Aggregate/FormTest.php index 7398a7d..b45835b 100644 --- a/tests/Aggregate/FormTest.php +++ b/tests/Aggregate/FormTest.php @@ -218,7 +218,7 @@ public function test_submit_with_transformer_exception_ignored() $this->registry->childBuilder(StringElement::class, 'firstName')->getter()->length(null, /*min:*/ 2)->buildChild(), $this->registry->childBuilder(StringElement::class, 'lastName')->getter()->length(null, /*min:*/ 2)->buildChild(), $this->registry->childBuilder(IntegerElement::class, 'id')->getter()->buildChild(), - ]), new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])), new ClosureTransformer(function () { throw new \Exception('my error'); })); + ]), new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)), new ClosureTransformer(function () { throw new \Exception('my error'); })); $form->import([ 'firstName' => 'John', @@ -244,7 +244,7 @@ public function test_submit_with_transformer_exception_ignored_should_validate_o $this->registry->childBuilder(StringElement::class, 'firstName')->getter()->required()->length(null, /*min:*/ 2)->buildChild(), $this->registry->childBuilder(StringElement::class, 'lastName')->getter()->required()->length(null, /*min:*/ 2)->buildChild(), $this->registry->childBuilder(IntegerElement::class, 'id')->getter()->required()->buildChild(), - ]), new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])), new ClosureTransformer(function () { throw new \Exception('my error'); })); + ]), new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)), new ClosureTransformer(function () { throw new \Exception('my error'); })); $form->import([ 'firstName' => 'John', @@ -540,6 +540,35 @@ public function test_import_with_array() $this->assertSame(42, $form['id']->element()->value()); } + /** + * + */ + public function test_import_null_should_reset_fields() + { + $form = new Form(new ChildrenCollection([ + $this->registry->childBuilder(IntegerElement::class, 'id')->getset()->buildChild(), + $this->registry->childBuilder(StringElement::class, 'firstName')->getset()->buildChild(), + $this->registry->childBuilder(StringElement::class, 'lastName')->getset()->buildChild(), + ])); + + $form->submit($data = [ + 'id' => 42, + 'firstName' => 'Mike', + 'lastName' => 'Smith', + ]); + $this->assertSame($data, $form->value()); + + $this->assertSame([ + 'id' => null, + 'firstName' => null, + 'lastName' => null, + ], $form->import(null)->value()); + + $this->assertNull($form['firstName']->element()->value()); + $this->assertNull($form['lastName']->element()->value()); + $this->assertNull($form['id']->element()->value()); + } + /** * */ diff --git a/tests/Aggregate/View/ArrayElementViewRendererTest.php b/tests/Aggregate/View/ArrayElementViewRendererTest.php index 10df0d5..19a3d32 100644 --- a/tests/Aggregate/View/ArrayElementViewRendererTest.php +++ b/tests/Aggregate/View/ArrayElementViewRendererTest.php @@ -4,6 +4,7 @@ use Bdf\Form\Aggregate\ArrayElement; use Bdf\Form\Choice\ChoiceView; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; use Symfony\Component\Validator\Constraints\Length; @@ -45,9 +46,7 @@ public function test_render_with_choice() ); } - /** - * @dataProvider provideConstraints - */ + #[DataProvider('provideConstraints')] public function test_render_csv_constraints($constraints, $attributes) { $view = new ArrayElementView(ArrayElement::class, 'foo', 'aaa,bbb,ccc', null, [], false, $constraints); @@ -56,7 +55,7 @@ public function test_render_csv_constraints($constraints, $attributes) $this->assertEquals('', $renderer->render($view, [])); } - public function provideConstraints() + public static function provideConstraints() { return [ [[], ''], diff --git a/tests/Child/ChildBuilderTest.php b/tests/Child/ChildBuilderTest.php index cb8357e..4a6b9fa 100644 --- a/tests/Child/ChildBuilderTest.php +++ b/tests/Child/ChildBuilderTest.php @@ -498,7 +498,7 @@ public function parent(): ChildAggregateInterface { } - public function setParent(ChildAggregateInterface $parent): ChildInterface + public function setParent(ChildAggregateInterface $parent): static { } diff --git a/tests/Child/ChildTest.php b/tests/Child/ChildTest.php index d9525c4..4f4184b 100644 --- a/tests/Child/ChildTest.php +++ b/tests/Child/ChildTest.php @@ -16,6 +16,7 @@ use Bdf\Form\PropertyAccess\Setter; use Bdf\Form\Transformer\ClosureTransformer; use Bdf\Form\Validator\ConstraintValueValidator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotEqualTo; @@ -62,7 +63,7 @@ public function test_parent() */ public function test_import_with_array() { - $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(['message' => 'required error']), null, new Getter()); + $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(message: 'required error'), null, new Getter()); $child->setParent($form = new Form(new ChildrenCollection())); $child->import(['child' => 'my value']); @@ -74,19 +75,31 @@ public function test_import_with_array() */ public function test_import_with_object() { - $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(['message' => 'required error']), null, new Getter()); + $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(message: 'required error'), null, new Getter()); $child->setParent($form = new Form(new ChildrenCollection())); $child->import((object) ['child' => 'my value']); $this->assertSame('my value', $child->element()->value()); } + /** + * + */ + public function test_import_null() + { + $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(message: 'required error'), null, new Getter()); + $child->setParent($form = new Form(new ChildrenCollection())); + + $child->import(null); + $this->assertNull($child->element()->value()); + } + /** * */ public function test_import_with_transformer() { - $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(['message' => 'required error']), null, new Getter(), [], new ClosureTransformer(function($value) { + $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(message: 'required error'), null, new Getter(), [], new ClosureTransformer(function($value) { return base64_encode($value); })); $child->setParent($form = new Form(new ChildrenCollection())); @@ -100,7 +113,7 @@ public function test_import_with_transformer() */ public function test_fill_with_array() { - $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(['message' => 'required error']), new Setter()); + $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(message: 'required error'), new Setter()); $child->setParent($form = new Form(new ChildrenCollection())); $child->element()->import('my value'); @@ -115,7 +128,7 @@ public function test_fill_with_array() */ public function test_fill_with_object() { - $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(['message' => 'required error']), new Setter()); + $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(message: 'required error'), new Setter()); $child->setParent($form = new Form(new ChildrenCollection())); $child->element()->import('my value'); @@ -130,7 +143,7 @@ public function test_fill_with_object() */ public function test_fill_with_transformer() { - $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(['message' => 'required error']), new Setter(), null, [], new ClosureTransformer(function($value) { + $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], new NotBlank(message: 'required error'), new Setter(), null, [], new ClosureTransformer(function($value) { return base64_encode($value); })); $child->setParent($form = new Form(new ChildrenCollection())); @@ -142,9 +155,7 @@ public function test_fill_with_transformer() $this->assertEquals(base64_encode('my value'), $target->child); } - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_submit_empty($value) { $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], null, new Setter()); @@ -154,9 +165,7 @@ public function test_submit_empty($value) $this->assertEmpty($child->element()->value()); } - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_submit_empty_with_default_value($value) { $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], 'default', new Setter()); @@ -166,9 +175,7 @@ public function test_submit_empty_with_default_value($value) $this->assertEquals('default', $child->element()->value()); } - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_submit_empty_with_default_value_on_array_element($value) { $child = new Child('child', new ArrayElement(new StringElement()), new ArrayOffsetHttpFields('child'), [], ['default'], new Setter()); @@ -178,9 +185,7 @@ public function test_submit_empty_with_default_value_on_array_element($value) $this->assertEquals(['default'], $child->element()->value()); } - /** - * @dataProvider notEmptyValues - */ + #[DataProvider('notEmptyValues')] public function test_submit_not_empty($value) { $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], null, new Setter()); @@ -322,7 +327,7 @@ public function test_view() /** * @return array */ - public function emptyValues() + public static function emptyValues() { return [ [null], @@ -336,7 +341,7 @@ public function emptyValues() /** * @return array */ - public function notEmptyValues() + public static function notEmptyValues() { return [ ['0'], diff --git a/tests/Child/Http/ArrayOffsetHttpFieldsTest.php b/tests/Child/Http/ArrayOffsetHttpFieldsTest.php index a5c53c4..5614880 100644 --- a/tests/Child/Http/ArrayOffsetHttpFieldsTest.php +++ b/tests/Child/Http/ArrayOffsetHttpFieldsTest.php @@ -10,6 +10,7 @@ use Bdf\Form\Leaf\StringElement; use Bdf\Form\PropertyAccess\Setter; use Bdf\Form\Validator\ConstraintValueValidator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\NotEqualTo; @@ -18,9 +19,7 @@ */ class ArrayOffsetHttpFieldsTest extends TestCase { - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_submit_empty($value) { $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], null, new Setter()); @@ -57,9 +56,7 @@ public function test_contains() $this->assertTrue($field->contains(['child' => 'xxx'])); } - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_submit_empty_with_default_value($value) { $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], 'default', new Setter()); @@ -69,9 +66,7 @@ public function test_submit_empty_with_default_value($value) $this->assertEquals('default', $child->element()->value()); } - /** - * @dataProvider notEmptyValues - */ + #[DataProvider('notEmptyValues')] public function test_submit_not_empty($value) { $child = new Child('child', new StringElement(), new ArrayOffsetHttpFields('child'), [], null, new Setter()); @@ -136,7 +131,7 @@ public function test_get() /** * @return array */ - public function emptyValues() + public static function emptyValues() { return [ [null], @@ -150,7 +145,7 @@ public function emptyValues() /** * @return array */ - public function notEmptyValues() + public static function notEmptyValues() { return [ ['0'], diff --git a/tests/Child/Http/PrefixedHttpFieldsTest.php b/tests/Child/Http/PrefixedHttpFieldsTest.php index 594db8b..b63d89f 100644 --- a/tests/Child/Http/PrefixedHttpFieldsTest.php +++ b/tests/Child/Http/PrefixedHttpFieldsTest.php @@ -11,6 +11,7 @@ use Bdf\Form\Leaf\StringElement; use Bdf\Form\PropertyAccess\Setter; use Bdf\Form\Validator\ConstraintValueValidator; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\Count; @@ -19,9 +20,7 @@ */ class PrefixedHttpFieldsTest extends TestCase { - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_submit_empty($value) { $child = new Child('child', new ArrayElement(new StringElement()), new PrefixedHttpFields('child_'), [], null, new Setter()); @@ -31,9 +30,7 @@ public function test_submit_empty($value) $this->assertSame([], $child->element()->value()); } - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_submit_empty_with_default_value($value) { $child = new Child('child', new ArrayElement(new StringElement()), new PrefixedHttpFields('child_'), [], ['default'], new Setter()); @@ -80,7 +77,7 @@ public function test_submit_not_empty_with_default() */ public function test_submit_element_constraint_error() { - $child = new Child('child', new ArrayElement(new StringElement(), null, new ConstraintValueValidator([new Count(['min' => 2])])), new PrefixedHttpFields('child_'), [], null, new Setter()); + $child = new Child('child', new ArrayElement(new StringElement(), null, new ConstraintValueValidator([new Count(min: 2)])), new PrefixedHttpFields('child_'), [], null, new Setter()); $child->setParent($form = new Form(new ChildrenCollection())); $this->assertFalse($child->submit(['child_0' => 'value'])); @@ -130,7 +127,7 @@ public function test_get() /** * @return array */ - public function emptyValues() + public static function emptyValues() { return [ [null], diff --git a/tests/Csrf/CsrfElementBuilderTest.php b/tests/Csrf/CsrfElementBuilderTest.php index e121b37..f0000ca 100644 --- a/tests/Csrf/CsrfElementBuilderTest.php +++ b/tests/Csrf/CsrfElementBuilderTest.php @@ -2,6 +2,7 @@ namespace Bdf\Form\Csrf; +use Bdf\Form\Transformer\TransformerInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -11,6 +12,7 @@ use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage; use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface; +use Symfony\Component\Validator\Constraints\NotBlank; /** * Class CsrfElementBuilderTest @@ -104,7 +106,7 @@ public function test_tokenManager() public function test_satisfy() { $this->expectException(\BadMethodCallException::class); - $this->builder->satisfy(null); + $this->builder->satisfy(new NotBlank()); } /** @@ -113,7 +115,7 @@ public function test_satisfy() public function test_transformer() { $this->expectException(\BadMethodCallException::class); - $this->builder->transformer(null); + $this->builder->transformer($this->createMock(TransformerInterface::class)); } /** diff --git a/tests/Custom/CustomFormTest.php b/tests/Custom/CustomFormTest.php index 6fe5bb4..82c3bc0 100644 --- a/tests/Custom/CustomFormTest.php +++ b/tests/Custom/CustomFormTest.php @@ -241,7 +241,7 @@ public function test_functional_array_of_custom_form() $this->assertFalse($array->failed()); $this->assertCount(2, $array); - $this->assertContainsOnly(Person::class, $array->value()); + $this->assertContainsOnlyInstancesOf(Person::class, $array->value()); $this->assertSame('Mickey', $array->value()[0]->firstName); $this->assertSame('Mouse', $array->value()[0]->lastName); $this->assertSame('Minnie', $array->value()[1]->firstName); @@ -301,7 +301,7 @@ public function child(string $name, FormError $error): void $error->print($this); } - public function print() + public function print(): mixed { if ($this->global || $this->code) { $this->errors[] = [ diff --git a/tests/CyclicReferenceTest.php b/tests/CyclicReferenceTest.php index 17b42e6..935d94c 100644 --- a/tests/CyclicReferenceTest.php +++ b/tests/CyclicReferenceTest.php @@ -8,11 +8,6 @@ use PHPUnit\Framework\TestCase; use WeakReference; -/** - * Class CyclicReferenceTest - * - * @requires PHP 7.4 - */ class CyclicReferenceTest extends TestCase { /** @@ -132,7 +127,7 @@ protected function configure(FormBuilderInterface $builder): void $builder ->string('foo') - ->length(['min' => 3]) + ->length(min: 3) ->setter()->getter() ; diff --git a/tests/Error/FormErrorTest.php b/tests/Error/FormErrorTest.php index 9200fd8..8850691 100644 --- a/tests/Error/FormErrorTest.php +++ b/tests/Error/FormErrorTest.php @@ -63,7 +63,7 @@ public function test_message_with_code() public function test_violation() { $validator = (new ValidatorBuilder)->getValidator(); - $violation = $validator->validate('foo', new Length(['min' => 5]))->get(0); + $violation = $validator->validate('foo', new Length(min: 5))->get(0); $error = FormError::violation($violation); $this->assertInstanceOf(FormError::class, $error); diff --git a/tests/Error/ImplodeErrorPrinterTest.php b/tests/Error/ImplodeErrorPrinterTest.php index 510179c..c013c93 100644 --- a/tests/Error/ImplodeErrorPrinterTest.php +++ b/tests/Error/ImplodeErrorPrinterTest.php @@ -31,7 +31,7 @@ public function test_functional_with_children_error() $builder->string('foo')->required(); $builder->integer('bar')->min(5); $builder->embedded('embedded', function ($builder) { - $builder->string('inner')->length(['min' => 5]); + $builder->string('inner')->length(min: 5); }); $element = $builder->buildElement(); @@ -51,7 +51,7 @@ public function test_functional_with_custom_separator() $builder->string('foo')->required(); $builder->integer('bar')->min(5); $builder->embedded('embedded', function ($builder) { - $builder->string('inner')->length(['min' => 5]); + $builder->string('inner')->length(min: 5); }); $element = $builder->buildElement(); diff --git a/tests/Error/StringErrorPrinterTest.php b/tests/Error/StringErrorPrinterTest.php index bfe9b86..c57a272 100644 --- a/tests/Error/StringErrorPrinterTest.php +++ b/tests/Error/StringErrorPrinterTest.php @@ -31,7 +31,7 @@ public function test_functional_with_children_error() $builder->string('foo')->required(); $builder->integer('bar')->min(5); $builder->embedded('embedded', function ($builder) { - $builder->string('inner')->length(['min' => 5]); + $builder->string('inner')->length(min: 5); }); $element = $builder->buildElement(); @@ -57,7 +57,7 @@ public function test_functional_lineSeparator() $builder->string('foo')->required(); $builder->integer('bar')->min(5); $builder->embedded('embedded', function ($builder) { - $builder->string('inner')->length(['min' => 5]); + $builder->string('inner')->length(min: 5); }); $element = $builder->buildElement(); @@ -80,7 +80,7 @@ public function test_functional_indentString() $builder->string('foo')->required(); $builder->integer('bar')->min(5); $builder->embedded('embedded', function ($builder) { - $builder->string('inner')->length(['min' => 5]); + $builder->string('inner')->length(min: 5); }); $element = $builder->buildElement(); @@ -106,7 +106,7 @@ public function test_functional_nameSeparator() $builder->string('foo')->required(); $builder->integer('bar')->min(5); $builder->embedded('embedded', function ($builder) { - $builder->string('inner')->length(['min' => 5]); + $builder->string('inner')->length(min: 5); }); $element = $builder->buildElement(); @@ -132,7 +132,7 @@ public function test_functional_maxDepth() $builder->string('foo')->required(); $builder->integer('bar')->min(5); $builder->embedded('embedded', function ($builder) { - $builder->string('inner')->length(['min' => 5]); + $builder->string('inner')->length(min: 5); }); $element = $builder->buildElement(); diff --git a/tests/Filter/FilterVarTest.php b/tests/Filter/FilterVarTest.php index ebb0265..e1ad44c 100644 --- a/tests/Filter/FilterVarTest.php +++ b/tests/Filter/FilterVarTest.php @@ -61,7 +61,7 @@ public function test_filter_array() public function test_functionnal() { $builder = new FormBuilder(); - $builder->string('foo')->filter(FilterVar::class); + $builder->string('foo')->filter(new FilterVar()); $form = $builder->buildElement(); $form->submit(['foo' => '"Test"']); diff --git a/tests/Leaf/AnyElementBuilderTest.php b/tests/Leaf/AnyElementBuilderTest.php index 666a4ce..00713f6 100644 --- a/tests/Leaf/AnyElementBuilderTest.php +++ b/tests/Leaf/AnyElementBuilderTest.php @@ -62,17 +62,6 @@ public function test_satisfy_order() $this->assertEquals('error 3', $element->error()->global()); } - /** - * - */ - public function test_satisfy_with_className_and_options() - { - $element = $this->builder->satisfy(NotEqualTo::class, ['value' => 'hello'])->buildElement(); - - $this->assertFalse($element->submit('hello')->valid()); - $this->assertTrue($element->submit('world')->valid()); - } - /** * */ diff --git a/tests/Leaf/AnyElementTest.php b/tests/Leaf/AnyElementTest.php index 515c9f9..472c24c 100644 --- a/tests/Leaf/AnyElementTest.php +++ b/tests/Leaf/AnyElementTest.php @@ -68,7 +68,7 @@ public function test_submit_null() */ public function test_submit_with_constraint() { - $element = new AnyElement(new ConstraintValueValidator([new Length(['max' => 2])])); + $element = new AnyElement(new ConstraintValueValidator([new Length(max: 2)])); $this->assertFalse($element->submit('hello')->valid()); $this->assertSame('hello', $element->value()); @@ -101,7 +101,7 @@ public function test_submit_with_transformer_exception_ignored() $transformer = $this->createMock(TransformerInterface::class); $transformer->expects($this->once())->method('transformFromHttp')->willThrowException(new TransformationFailedException('my error')); $element = new AnyElement( - new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])), + new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)), $transformer ); @@ -120,7 +120,7 @@ public function test_submit_with_transformer_exception_ignored_should_validate_o $element = new AnyElement( new ConstraintValueValidator( [new Closure(function () { return 'validation error'; })], - new TransformerExceptionConstraint(['ignoreException' => true]) + new TransformerExceptionConstraint(ignoreException: true) ), $transformer ); @@ -149,7 +149,7 @@ public function test_patch_null() */ public function test_patch_null_with_constraints_should_be_validated() { - $element = (new AnyElementBuilder())->satisfy(new Length(['min' => 5]))->buildElement(); + $element = (new AnyElementBuilder())->satisfy(new Length(min: 5))->buildElement(); $element->import('foo'); $this->assertSame($element, $element->patch(null)); @@ -164,7 +164,7 @@ public function test_patch_null_with_constraints_should_be_validated() */ public function test_patch_with_value() { - $element = (new AnyElementBuilder())->satisfy(new Length(['min' => 3]))->buildElement(); + $element = (new AnyElementBuilder())->satisfy(new Length(min: 3))->buildElement(); $this->assertFalse($element->patch('f')->valid()); $this->assertSame('f', $element->value()); @@ -313,7 +313,7 @@ public function test_view_with_choice() $view = $element->view(HttpFieldPath::named('val')); - $this->assertContainsOnly(ChoiceView::class, $view->choices()); + $this->assertContainsOnlyInstancesOf(ChoiceView::class, $view->choices()); $this->assertCount(2, $view->choices()); $this->assertSame('foo', $view->choices()[0]->value()); @@ -342,7 +342,7 @@ public function test_view_with_choice_and_transformer() $view = $element->view(HttpFieldPath::named('val')); - $this->assertContainsOnly(ChoiceView::class, $view->choices()); + $this->assertContainsOnlyInstancesOf(ChoiceView::class, $view->choices()); $this->assertCount(2, $view->choices()); $this->assertSame('Zm9v', $view->choices()[0]->value()); diff --git a/tests/Leaf/BooleanElementTest.php b/tests/Leaf/BooleanElementTest.php index b7c45c5..8f3cc4a 100644 --- a/tests/Leaf/BooleanElementTest.php +++ b/tests/Leaf/BooleanElementTest.php @@ -12,6 +12,7 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\TransformerExceptionConstraint; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Validator\Constraints\EqualTo; @@ -99,7 +100,7 @@ public function test_submit_with_transformer_exception_ignored() $transformer = $this->createMock(TransformerInterface::class); $transformer->expects($this->once())->method('transformFromHttp')->willThrowException(new TransformationFailedException('my error')); $element = new BooleanElement( - new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])), + new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)), $transformer ); @@ -117,7 +118,7 @@ public function test_submit_with_transformer_exception_ignored_should_validate_o $element = new BooleanElement( new ConstraintValueValidator( [new Closure(function () { return 'validation error'; })], - new TransformerExceptionConstraint(['ignoreException' => true]) + new TransformerExceptionConstraint(ignoreException: true) ), $transformer ); @@ -150,9 +151,7 @@ public function test_transformer() $this->assertSame('-', $element->httpValue()); } - /** - * @dataProvider provideValidValues - */ + #[DataProvider('provideValidValues')] public function test_import($value, $expected) { $element = new BooleanElement(); @@ -160,7 +159,7 @@ public function test_import($value, $expected) $this->assertSame($expected, $element->import($value)->value()); } - public function provideValidValues() + public static function provideValidValues() { return [ ['hello', true], @@ -174,9 +173,7 @@ public function provideValidValues() ]; } - /** - * @dataProvider provideInvalidValue - */ + #[DataProvider('provideInvalidValue')] public function test_import_invalid_type($value) { $this->expectException(\TypeError::class); @@ -189,7 +186,7 @@ public function test_import_invalid_type($value) /** * */ - public function provideInvalidValue() + public static function provideInvalidValue() { return [ [[]], diff --git a/tests/Leaf/BooleanStringElementTest.php b/tests/Leaf/BooleanStringElementTest.php index 8617663..601d8e1 100644 --- a/tests/Leaf/BooleanStringElementTest.php +++ b/tests/Leaf/BooleanStringElementTest.php @@ -12,6 +12,8 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\TransformerExceptionConstraint; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Validator\Constraints\EqualTo; @@ -33,15 +35,15 @@ public function test_default() $this->assertTrue($element->error()->empty()); } - /** - * @testWith [true] - * [1] - * ["1"] - * ["true"] - * ["on"] - * ["yes"] - * [" True "] - */ + #[ + TestWith([true]), + TestWith([1]), + TestWith(["1"]), + TestWith(["true"]), + TestWith(["on"]), + TestWith(["yes"]), + TestWith([" True "]), + ] public function test_submit_true($value) { $element = new BooleanStringElement(); @@ -51,15 +53,15 @@ public function test_submit_true($value) $this->assertTrue($element->error()->empty()); } - /** - * @testWith [false] - * [0] - * ["0"] - * ["false"] - * ["off"] - * ["no"] - * [" False "] - */ + #[ + TestWith([false]), + TestWith([0]), + TestWith(["0"]), + TestWith(["false"]), + TestWith(["off"]), + TestWith(["no"]), + TestWith([" False "]), + ] public function test_submit_false($value) { $element = new BooleanStringElement(); @@ -145,7 +147,7 @@ public function test_submit_with_transformer_exception_ignored() $transformer = $this->createMock(TransformerInterface::class); $transformer->expects($this->once())->method('transformFromHttp')->willThrowException(new TransformationFailedException('my error')); $element = new BooleanStringElement( - new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])), + new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)), $transformer ); @@ -163,7 +165,7 @@ public function test_submit_with_transformer_exception_ignored_should_validate_o $element = new BooleanStringElement( new ConstraintValueValidator( [new Closure(function () { return 'validation error'; })], - new TransformerExceptionConstraint(['ignoreException' => true]) + new TransformerExceptionConstraint(ignoreException: true) ), $transformer ); @@ -196,9 +198,7 @@ public function test_transformer() $this->assertSame('-', $element->httpValue()); } - /** - * @dataProvider provideValidValues - */ + #[DataProvider('provideValidValues')] public function test_import($value, $expected) { $element = new BooleanStringElement(); @@ -206,7 +206,7 @@ public function test_import($value, $expected) $this->assertSame($expected, $element->import($value)->value()); } - public function provideValidValues() + public static function provideValidValues() { return [ ['hello', true], @@ -220,9 +220,7 @@ public function provideValidValues() ]; } - /** - * @dataProvider provideInvalidValue - */ + #[DataProvider('provideInvalidValue')] public function test_import_invalid_type($value) { $this->expectException(\TypeError::class); @@ -235,7 +233,7 @@ public function test_import_invalid_type($value) /** * */ - public function provideInvalidValue() + public static function provideInvalidValue() { return [ [[]], diff --git a/tests/Leaf/Date/DateTimeElementTest.php b/tests/Leaf/Date/DateTimeElementTest.php index aa5920e..a95bef9 100644 --- a/tests/Leaf/Date/DateTimeElementTest.php +++ b/tests/Leaf/Date/DateTimeElementTest.php @@ -18,6 +18,7 @@ use DateTime; use DateTimeImmutable; use DateTimeZone; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Validator\Constraints\LessThan; @@ -178,7 +179,7 @@ public function test_submit_with_transformer_exception_ignored_should_validate_o $element = new DateTimeElement( new ConstraintValueValidator( [new Closure(function () { return 'validation error'; })], - new TransformerExceptionConstraint(['ignoreException' => true]) + new TransformerExceptionConstraint(ignoreException: true) ), $transformer ); @@ -346,9 +347,7 @@ public function test_import_invalid_class() $element->import(new DateTime('2000-01-05 15:00:00')); } - /** - * @dataProvider provideInvalidValue - */ + #[DataProvider('provideInvalidValue')] public function test_import_invalid($value) { $this->expectException(\TypeError::class); @@ -361,7 +360,7 @@ public function test_import_invalid($value) /** * */ - public function provideInvalidValue() + public static function provideInvalidValue() { return [ [[]], diff --git a/tests/Leaf/FloatElementTest.php b/tests/Leaf/FloatElementTest.php index 7a0e3f9..2e95d68 100644 --- a/tests/Leaf/FloatElementTest.php +++ b/tests/Leaf/FloatElementTest.php @@ -14,6 +14,7 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\TransformerExceptionConstraint; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; @@ -102,7 +103,7 @@ public function test_submit_with_transformer_exception_ignored() $transformer = $this->createMock(TransformerInterface::class); $transformer->expects($this->once())->method('transformFromHttp')->willThrowException(new TransformationFailedException('my error')); $element = new FloatElement( - new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])), + new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)), $transformer ); @@ -120,7 +121,7 @@ public function test_submit_with_transformer_exception_ignored_should_validate_o $element = new FloatElement( new ConstraintValueValidator( [new Closure(function () { return 'validation error'; })], - new TransformerExceptionConstraint(['ignoreException' => true]) + new TransformerExceptionConstraint(ignoreException: true) ), $transformer ); @@ -188,9 +189,7 @@ public function test_transformer() $this->assertEqualsWithDelta(1.2, $element->httpValue(), 0.00001); } - /** - * @dataProvider provideValidValues - */ + #[DataProvider('provideValidValues')] public function test_import($value, $expected) { $element = new FloatElement(); @@ -198,7 +197,7 @@ public function test_import($value, $expected) $this->assertSame($expected, $element->import($value)->value()); } - public function provideValidValues() + public static function provideValidValues() { return [ [15, 15.0], @@ -209,9 +208,7 @@ public function provideValidValues() ]; } - /** - * @dataProvider provideInvalidValue - */ + #[DataProvider('provideInvalidValue')] public function test_import_invalid_type($value) { $this->expectException(\TypeError::class); @@ -224,7 +221,7 @@ public function test_import_invalid_type($value) /** * */ - public function provideInvalidValue() + public static function provideInvalidValue() { return [ [[]], @@ -376,7 +373,7 @@ public function test_view_with_choice() $view = $element->view(HttpFieldPath::named('val')); - $this->assertContainsOnly(ChoiceView::class, $view->choices()); + $this->assertContainsOnlyInstancesOf(ChoiceView::class, $view->choices()); $this->assertCount(3, $view->choices()); $this->assertSame('1.2', $view->choices()[0]->value()); diff --git a/tests/Leaf/Helper/EmailElementBuilderTest.php b/tests/Leaf/Helper/EmailElementBuilderTest.php index 084c890..26e3aa8 100644 --- a/tests/Leaf/Helper/EmailElementBuilderTest.php +++ b/tests/Leaf/Helper/EmailElementBuilderTest.php @@ -85,23 +85,6 @@ public function test_disableConstraint() $this->assertTrue($element->submit('foo')->valid()); } - /** - * - */ - public function test_useConstraint_legacy() - { - $element = $this->builder - ->disableConstraint() - ->useConstraint(['message' => 'my error']) - ->buildElement() - ; - - $this->assertInstanceOf(EmailElement::class, $element); - - $this->assertFalse($element->submit('foo')->valid()); - $this->assertEquals('my error', $element->error()->global()); - } - /** * */ diff --git a/tests/Leaf/Helper/UrlElementBuilderTest.php b/tests/Leaf/Helper/UrlElementBuilderTest.php index d65a247..d0d4d58 100644 --- a/tests/Leaf/Helper/UrlElementBuilderTest.php +++ b/tests/Leaf/Helper/UrlElementBuilderTest.php @@ -103,7 +103,7 @@ public function test_useConstraint_legacy() { $element = $this->builder ->disableConstraint() - ->useConstraint(['message' => 'my error']) + ->useConstraint(message: 'my error') ->buildElement() ; diff --git a/tests/Leaf/IntegerElementTest.php b/tests/Leaf/IntegerElementTest.php index e59773b..c5d4cd9 100644 --- a/tests/Leaf/IntegerElementTest.php +++ b/tests/Leaf/IntegerElementTest.php @@ -14,6 +14,7 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\TransformerExceptionConstraint; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; @@ -102,7 +103,7 @@ public function test_submit_with_transformer_exception_ignored() $transformer = $this->createMock(TransformerInterface::class); $transformer->expects($this->once())->method('transformFromHttp')->willThrowException(new TransformationFailedException('my error')); $element = new IntegerElement( - new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])), + new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)), $transformer ); @@ -120,7 +121,7 @@ public function test_submit_with_transformer_exception_ignored_should_validate_o $element = new IntegerElement( new ConstraintValueValidator( [new Closure(function () { return 'validation error'; })], - new TransformerExceptionConstraint(['ignoreException' => true]) + new TransformerExceptionConstraint(ignoreException: true) ), $transformer ); @@ -188,9 +189,7 @@ public function test_transformer() $this->assertEquals(1, $element->httpValue()); } - /** - * @dataProvider provideValidValues - */ + #[DataProvider('provideValidValues')] public function test_import($value, $expected) { $element = new IntegerElement(); @@ -198,7 +197,7 @@ public function test_import($value, $expected) $this->assertSame($expected, $element->import($value)->value()); } - public function provideValidValues() + public static function provideValidValues() { return [ [15, 15], @@ -209,9 +208,7 @@ public function provideValidValues() ]; } - /** - * @dataProvider provideInvalidValue - */ + #[DataProvider('provideInvalidValue')] public function test_import_invalid_type($value) { $this->expectException(\TypeError::class); @@ -224,7 +221,7 @@ public function test_import_invalid_type($value) /** * */ - public function provideInvalidValue() + public static function provideInvalidValue() { return [ [[]], @@ -376,7 +373,7 @@ public function test_view_with_choice() $view = $element->view(HttpFieldPath::named('val')); - $this->assertContainsOnly(ChoiceView::class, $view->choices()); + $this->assertContainsOnlyInstancesOf(ChoiceView::class, $view->choices()); $this->assertCount(3, $view->choices()); $this->assertSame('12', $view->choices()[0]->value()); @@ -409,7 +406,7 @@ public function test_view_with_choice_and_transformer() $view = $element->view(HttpFieldPath::named('val')); - $this->assertContainsOnly(ChoiceView::class, $view->choices()); + $this->assertContainsOnlyInstancesOf(ChoiceView::class, $view->choices()); $this->assertCount(3, $view->choices()); $this->assertSame('c', $view->choices()[0]->value()); diff --git a/tests/Leaf/LeafElementTest.php b/tests/Leaf/LeafElementTest.php index 3a6d32a..a36e301 100644 --- a/tests/Leaf/LeafElementTest.php +++ b/tests/Leaf/LeafElementTest.php @@ -4,6 +4,7 @@ use Bdf\Form\Child\Http\HttpFieldPath; use Bdf\Form\View\ElementViewInterface; +use Bdf\Form\View\FieldViewInterface; use PHPUnit\Framework\TestCase; class LeafElementTest extends TestCase @@ -34,7 +35,7 @@ protected function choiceView(): ?array return []; } - public function view(?HttpFieldPath $field = null): ElementViewInterface + public function view(?HttpFieldPath $field = null): FieldViewInterface { $view = parent::view($field); diff --git a/tests/Leaf/StringElementBuilderTest.php b/tests/Leaf/StringElementBuilderTest.php index 312af14..8463906 100644 --- a/tests/Leaf/StringElementBuilderTest.php +++ b/tests/Leaf/StringElementBuilderTest.php @@ -62,17 +62,6 @@ public function test_satisfy_order() $this->assertEquals('error 3', $element->error()->global()); } - /** - * - */ - public function test_satisfy_with_className_and_options() - { - $element = $this->builder->satisfy(NotEqualTo::class, ['value' => 'hello'])->buildElement(); - - $this->assertFalse($element->submit('hello')->valid()); - $this->assertTrue($element->submit('world')->valid()); - } - /** * */ @@ -111,17 +100,6 @@ public function test_value() $this->assertSame('default', $element->value()); } - /** - * - */ - public function test_length_legacy() - { - $element = $this->builder->length(['max' => 3])->buildElement(); - - $this->assertFalse($element->submit('aaaa')->valid()); - $this->assertTrue($element->submit('aaa')->valid()); - } - /** * */ diff --git a/tests/Leaf/StringElementTest.php b/tests/Leaf/StringElementTest.php index f48028f..20569bc 100644 --- a/tests/Leaf/StringElementTest.php +++ b/tests/Leaf/StringElementTest.php @@ -13,6 +13,7 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ConstraintValueValidator; use Bdf\Form\Validator\TransformerExceptionConstraint; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Validator\Constraints\Length; @@ -66,7 +67,7 @@ public function test_submit_null() */ public function test_submit_with_constraint() { - $element = new StringElement(new ConstraintValueValidator([new Length(['max' => 2])])); + $element = new StringElement(new ConstraintValueValidator([new Length(max: 2)])); $this->assertFalse($element->submit('hello')->valid()); $this->assertTrue($element->failed()); @@ -100,7 +101,7 @@ public function test_submit_with_transformer_exception_ignored() $transformer = $this->createMock(TransformerInterface::class); $transformer->expects($this->once())->method('transformFromHttp')->willThrowException(new TransformationFailedException('my error')); $element = new StringElement( - new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])), + new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)), $transformer ); @@ -118,7 +119,7 @@ public function test_submit_with_transformer_exception_ignored_should_validate_o $element = new StringElement( new ConstraintValueValidator( [new Closure(function () { return 'validation error'; })], - new TransformerExceptionConstraint(['ignoreException' => true]) + new TransformerExceptionConstraint(ignoreException: true) ), $transformer ); @@ -185,9 +186,7 @@ public function test_transformer() $this->assertEquals('hello', $element->httpValue()); } - /** - * @dataProvider provideValidValues - */ + #[DataProvider('provideValidValues')] public function test_import($value, $expected) { $element = new StringElement(); @@ -195,7 +194,7 @@ public function test_import($value, $expected) $this->assertSame($expected, $element->import($value)->value()); } - public function provideValidValues() + public static function provideValidValues() { return [ ['hello', 'hello'], @@ -208,9 +207,7 @@ public function provideValidValues() ]; } - /** - * @dataProvider provideInvalidValue - */ + #[DataProvider('provideInvalidValue')] public function test_import_invalid_type($value) { $this->expectException(\TypeError::class); @@ -223,7 +220,7 @@ public function test_import_invalid_type($value) /** * */ - public function provideInvalidValue() + public static function provideInvalidValue() { return [ [[]], @@ -370,7 +367,7 @@ public function test_view_with_choice() $view = $element->view(HttpFieldPath::named('val')); - $this->assertContainsOnly(ChoiceView::class, $view->choices()); + $this->assertContainsOnlyInstancesOf(ChoiceView::class, $view->choices()); $this->assertCount(2, $view->choices()); $this->assertSame('foo', $view->choices()[0]->value()); @@ -399,7 +396,7 @@ public function test_view_with_choice_and_transformer() $view = $element->view(HttpFieldPath::named('val')); - $this->assertContainsOnly(ChoiceView::class, $view->choices()); + $this->assertContainsOnlyInstancesOf(ChoiceView::class, $view->choices()); $this->assertCount(2, $view->choices()); $this->assertSame('Zm9v', $view->choices()[0]->value()); diff --git a/tests/Leaf/Transformer/LocalizedIntegerTransformerTest.php b/tests/Leaf/Transformer/LocalizedIntegerTransformerTest.php index 71018b5..c7140bc 100644 --- a/tests/Leaf/Transformer/LocalizedIntegerTransformerTest.php +++ b/tests/Leaf/Transformer/LocalizedIntegerTransformerTest.php @@ -4,13 +4,12 @@ use Bdf\Form\ElementInterface; use Locale; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class LocalizedIntegerTransformerTest extends TestCase { - /** - * @dataProvider provideFromHttpRoundingMode - */ + #[DataProvider('provideFromHttpRoundingMode')] public function test_fromHttp_scale_and_roundingMode($mode, $httpValue, $phpValue) { $transformer = new LocalizedIntegerTransformer(false, $mode); @@ -21,7 +20,7 @@ public function test_fromHttp_scale_and_roundingMode($mode, $httpValue, $phpValu /** * */ - public function provideFromHttpRoundingMode() + public static function provideFromHttpRoundingMode() { return [ [\NumberFormatter::ROUND_CEILING, '12.3456789', 13], @@ -34,9 +33,7 @@ public function provideFromHttpRoundingMode() ]; } - /** - * @dataProvider provideToHttpRoundingMode - */ + #[DataProvider('provideToHttpRoundingMode')] public function test_toHttp_roundingMode($mode, $phpValue, $httpValue) { $transformer = new LocalizedIntegerTransformer(false, $mode); @@ -47,7 +44,7 @@ public function test_toHttp_roundingMode($mode, $phpValue, $httpValue) /** * */ - public function provideToHttpRoundingMode() + public static function provideToHttpRoundingMode() { return [ [\NumberFormatter::ROUND_CEILING, 12.3456789, '13'], @@ -128,9 +125,7 @@ public function test_empty() $this->assertNull($transformer->transformToHttp(null, $element)); } - /** - * @dataProvider invalidValue - */ + #[DataProvider('invalidValue')] public function test_transformFromHttp_invalid_number($value, $error) { $this->expectException(\InvalidArgumentException::class); @@ -142,7 +137,7 @@ public function test_transformFromHttp_invalid_number($value, $error) $transformer->transformFromHttp($value, $element); } - public function invalidValue() + public static function invalidValue() { return [ ['foo', 'Number parsing failed: U_PARSE_ERROR'], @@ -153,9 +148,7 @@ public function invalidValue() ]; } - /** - * @dataProvider invalidPhpValue - */ + #[DataProvider('invalidPhpValue')] public function test_transformToHttp_not_a_number($value) { $this->expectException(\InvalidArgumentException::class); @@ -167,7 +160,7 @@ public function test_transformToHttp_not_a_number($value) $transformer->transformToHttp($value, $element); } - public function invalidPhpValue() + public static function invalidPhpValue() { return [ ['foo'], diff --git a/tests/Leaf/Transformer/LocalizedNumberTransformerTest.php b/tests/Leaf/Transformer/LocalizedNumberTransformerTest.php index 085d79e..652406f 100644 --- a/tests/Leaf/Transformer/LocalizedNumberTransformerTest.php +++ b/tests/Leaf/Transformer/LocalizedNumberTransformerTest.php @@ -4,13 +4,12 @@ use Bdf\Form\ElementInterface; use Locale; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class LocalizedNumberTransformerTest extends TestCase { - /** - * @dataProvider provideFromHttpScaleAndRoundingMode - */ + #[DataProvider('provideFromHttpScaleAndRoundingMode')] public function test_fromHttp_scale_and_roundingMode($scale, $mode, $httpValue, $phpValue) { $transformer = new LocalizedNumberTransformer($scale, false, $mode); @@ -21,7 +20,7 @@ public function test_fromHttp_scale_and_roundingMode($scale, $mode, $httpValue, /** * */ - public function provideFromHttpScaleAndRoundingMode() + public static function provideFromHttpScaleAndRoundingMode() { return [ [null, \NumberFormatter::ROUND_CEILING, '12.3456789', 12.3456789], @@ -51,9 +50,7 @@ public function provideFromHttpScaleAndRoundingMode() [1, \NumberFormatter::ROUND_HALFEVEN, '-3.65', -3.6], ]; } - /** - * @dataProvider provideToHttpScaleAndRoundingMode - */ + #[DataProvider('provideToHttpScaleAndRoundingMode')] public function test_toHttp_scale_and_roundingMode($scale, $mode, $phpValue, $httpValue) { $transformer = new LocalizedNumberTransformer($scale, false, $mode); @@ -64,7 +61,7 @@ public function test_toHttp_scale_and_roundingMode($scale, $mode, $phpValue, $ht /** * */ - public function provideToHttpScaleAndRoundingMode() + public static function provideToHttpScaleAndRoundingMode() { return [ [null, \NumberFormatter::ROUND_CEILING, 12.3456789, '12.346'], @@ -163,9 +160,7 @@ public function test_empty() $this->assertNull($transformer->transformToHttp(null, $element)); } - /** - * @dataProvider invalidValue - */ + #[DataProvider('invalidValue')] public function test_transformFromHttp_invalid_number($value, $error) { $this->expectException(\InvalidArgumentException::class); @@ -177,7 +172,7 @@ public function test_transformFromHttp_invalid_number($value, $error) $transformer->transformFromHttp($value, $element); } - public function invalidValue() + public static function invalidValue() { return [ ['foo', 'Number parsing failed: U_PARSE_ERROR'], @@ -188,9 +183,7 @@ public function invalidValue() ]; } - /** - * @dataProvider invalidPhpValue - */ + #[DataProvider('invalidPhpValue')] public function test_transformToHttp_not_a_number($value) { $this->expectException(\InvalidArgumentException::class); @@ -202,7 +195,7 @@ public function test_transformToHttp_not_a_number($value) $transformer->transformToHttp($value, $element); } - public function invalidPhpValue() + public static function invalidPhpValue() { return [ ['foo'], @@ -212,9 +205,7 @@ public function invalidPhpValue() ]; } - /** - * @dataProvider validPhpValue - */ + #[DataProvider('validPhpValue')] public function test_transformToHttp_valid($httpValue, $expected) { $transformer = new LocalizedNumberTransformer(); @@ -223,7 +214,7 @@ public function test_transformToHttp_valid($httpValue, $expected) $this->assertSame($expected, $transformer->transformToHttp($httpValue, $element)); } - public function validPhpValue() + public static function validPhpValue() { return [ [1.23, '1.23'], diff --git a/tests/Leaf/View/SimpleFieldHtmlRendererTest.php b/tests/Leaf/View/SimpleFieldHtmlRendererTest.php index 9fc8cdf..b150f07 100644 --- a/tests/Leaf/View/SimpleFieldHtmlRendererTest.php +++ b/tests/Leaf/View/SimpleFieldHtmlRendererTest.php @@ -8,6 +8,7 @@ use Bdf\Form\Leaf\StringElement; use Bdf\Form\Phone\PhoneElement; use Bdf\Form\Constraint\Closure; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; use Symfony\Component\Validator\Constraints\Length; @@ -42,9 +43,7 @@ public function test_render_allow_override_required() $this->assertEquals('', $renderer->render($view, ['type' => 'email', 'required' => false])); } - /** - * @dataProvider provideType - */ + #[DataProvider('provideType')] public function test_render_type($elementType, $htmlType) { $view = new SimpleElementView($elementType, 'foo', 'bar', null, false, []); @@ -53,7 +52,7 @@ public function test_render_type($elementType, $htmlType) $this->assertEquals('', $renderer->render($view, [])); } - public function provideType() + public static function provideType() { return [ [StringElement::class, 'text'], @@ -64,9 +63,7 @@ public function provideType() ]; } - /** - * @dataProvider provideConstraints - */ + #[DataProvider('provideConstraints')] public function test_render_constraints($constraints, $attributes) { $view = new SimpleElementView(StringElement::class, 'foo', 'bar', null, false, $constraints); @@ -75,7 +72,7 @@ public function test_render_constraints($constraints, $attributes) $this->assertEquals('', $renderer->render($view, [])); } - public function provideConstraints() + public static function provideConstraints() { return [ [[], ''], diff --git a/tests/Phone/NotEmptyPhoneNumberTest.php b/tests/Phone/NotEmptyPhoneNumberTest.php index 699afd9..bc3f1b0 100644 --- a/tests/Phone/NotEmptyPhoneNumberTest.php +++ b/tests/Phone/NotEmptyPhoneNumberTest.php @@ -35,23 +35,12 @@ public function test_not_empty() $this->assertEquals(FormError::null(), $validator->validate((new PhoneNumber())->setNationalNumber('123456'), $input)); } - /** - * - */ - public function test_custom_message_legacy() - { - $validator = new ConstraintValueValidator([new NotEmptyPhoneNumber(['message' => 'my error'])]); - $input = new PhoneElement(); - - $this->assertEquals(FormError::message('my error', 'IS_BLANK_ERROR'), $validator->validate(null, $input)); - } - /** * */ public function test_custom_message() { - $validator = new ConstraintValueValidator([new NotEmptyPhoneNumber(null, /*message:*/ 'my error')]); + $validator = new ConstraintValueValidator([new NotEmptyPhoneNumber(message: 'my error')]); $input = new PhoneElement(); $this->assertEquals(FormError::message('my error', 'IS_BLANK_ERROR'), $validator->validate(null, $input)); diff --git a/tests/Phone/PhoneChildBuilderTest.php b/tests/Phone/PhoneChildBuilderTest.php index 729656e..708c120 100644 --- a/tests/Phone/PhoneChildBuilderTest.php +++ b/tests/Phone/PhoneChildBuilderTest.php @@ -6,13 +6,12 @@ use Bdf\Form\Aggregate\Form; use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumberFormat; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class PhoneChildBuilderTest extends TestCase { - /** - * @dataProvider provideFormats - */ + #[DataProvider('provideFormats')] public function test_saveAsString($format, $formatted) { $builder = new PhoneChildBuilder('child', new PhoneElementBuilder()); @@ -82,7 +81,7 @@ public function test_formatIfInvalid() $this->assertSame(['child' => '+3314554'], $target); } - public function provideFormats() + public static function provideFormats() { return [ [PhoneNumberFormat::E164, '+33142563698'], diff --git a/tests/Phone/PhoneElementBuilderTest.php b/tests/Phone/PhoneElementBuilderTest.php index 8a49267..cdb9c8f 100644 --- a/tests/Phone/PhoneElementBuilderTest.php +++ b/tests/Phone/PhoneElementBuilderTest.php @@ -6,6 +6,7 @@ use Bdf\Form\Child\Child; use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumberUtil; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Constraints\NotBlank; @@ -165,9 +166,7 @@ public function test_value() $this->assertSame($phone, $element->value()); } - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_required($value) { $element = $this->builder->required()->allowInvalidNumber()->buildElement(); @@ -179,7 +178,7 @@ public function test_required($value) /** * @return array */ - public function emptyValues() + public static function emptyValues() { return [ [null], diff --git a/tests/Phone/PhoneElementTest.php b/tests/Phone/PhoneElementTest.php index 15a66b6..e2a642a 100644 --- a/tests/Phone/PhoneElementTest.php +++ b/tests/Phone/PhoneElementTest.php @@ -12,6 +12,7 @@ use Bdf\Form\Transformer\TransformerInterface; use Bdf\Form\Validator\ConstraintValueValidator; use libphonenumber\PhoneNumber; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Validator\Constraints\NotBlank; @@ -185,9 +186,7 @@ public function test_import_null() $this->assertNull($element->import(null)->value()); } - /** - * @dataProvider provideInvalidValue - */ + #[DataProvider('provideInvalidValue')] public function test_import_invalid_values($value) { $this->expectException(\TypeError::class); @@ -200,7 +199,7 @@ public function test_import_invalid_values($value) /** * */ - public function provideInvalidValue() + public static function provideInvalidValue() { return [ [[]], diff --git a/tests/Phone/Transformer/PhoneNumberToStringTransformerTest.php b/tests/Phone/Transformer/PhoneNumberToStringTransformerTest.php index 350f328..4e54df3 100644 --- a/tests/Phone/Transformer/PhoneNumberToStringTransformerTest.php +++ b/tests/Phone/Transformer/PhoneNumberToStringTransformerTest.php @@ -6,6 +6,7 @@ use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumberFormat; use libphonenumber\PhoneNumberUtil; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class PhoneNumberToStringTransformerTest extends TestCase @@ -82,9 +83,7 @@ public function test_with_invalid_force_format_the_value() $this->assertInstanceOf(PhoneNumber::class, $form['foo']->element()->value()); } - /** - * @dataProvider provideEmptyValue - */ + #[DataProvider('provideEmptyValue')] public function test_with_empty_value_should_return_null($empty) { $builder = new FormBuilder(); @@ -99,7 +98,7 @@ public function test_with_empty_value_should_return_null($empty) /** * */ - public function provideEmptyValue() + public static function provideEmptyValue() { return [ [null], diff --git a/tests/Registry/RegistryTest.php b/tests/Registry/RegistryTest.php index e736581..2715219 100644 --- a/tests/Registry/RegistryTest.php +++ b/tests/Registry/RegistryTest.php @@ -68,104 +68,6 @@ protected function setUp(): void $this->registry = new Registry(); } - /** - * - */ - public function test_filter_with_instance_of_filter() - { - $filter = new TrimFilter(); - - $this->assertSame($filter, $this->registry->filter($filter)); - } - - /** - * - */ - public function test_filter_with_classname() - { - $this->assertInstanceOf(TrimFilter::class, $this->registry->filter(TrimFilter::class)); - } - - /** - * - */ - public function test_filter_with_callback() - { - $this->assertInstanceOf(ClosureFilter::class, $this->registry->filter(function () {})); - } - - /** - * - */ - public function test_constraint_with_constraint_instance() - { - $constraint = new NotBlank(); - - $this->assertSame($constraint, $this->registry->constraint($constraint)); - } - - /** - * - */ - public function test_constraint_with_classname() - { - $this->assertInstanceOf(NotBlank::class, $this->registry->constraint(NotBlank::class)); - } - - /** - * - */ - public function test_constraint_with_classname_and_options() - { - $this->assertEquals(new NotBlank(['message' => 'error']), $this->registry->constraint([NotBlank::class, 'error'])); - $this->assertEquals(new NotBlank(['message' => 'error']), $this->registry->constraint([NotBlank::class, ['message' => 'error']])); - } - - /** - * - */ - public function test_constraint_with_callback() - { - $this->assertInstanceOf(Closure::class, $this->registry->constraint(function () {})); - } - - /** - * - */ - public function test_transformer_with_instance() - { - $transformer = new TransformerAggregate([]); - - $this->assertSame($transformer, $this->registry->transformer($transformer)); - } - - /** - * - */ - public function test_transformer_with_symfony_DataTransformer() - { - $this->assertInstanceOf(DataTransformerAdapter::class, $this->registry->transformer(new IntegerToLocalizedStringTransformer())); - } - - /** - * - */ - public function test_transformer_with_callable() - { - $this->assertInstanceOf(ClosureTransformer::class, $this->registry->transformer(function () {})); - } - - /** - * - */ - public function test_transformer_invalid() - { - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('Invalid view transformer given for input \'foo\''); - - $this->registry->transformer('foo'); - } - /** * */ diff --git a/tests/StaticAnalysis/ArrayElementConfigure.php b/tests/StaticAnalysis/ArrayElementConfigure.php index b2a7195..2f55eb8 100644 --- a/tests/StaticAnalysis/ArrayElementConfigure.php +++ b/tests/StaticAnalysis/ArrayElementConfigure.php @@ -15,6 +15,7 @@ class ArrayElementConfigure extends CustomForm /** * {@inheritdoc} */ + #[\Override] protected function configure(FormBuilderInterface $builder): void { $builder->array('values')->element( diff --git a/tests/StaticAnalysis/MyCustomForm.php b/tests/StaticAnalysis/MyCustomForm.php index 47d3270..73f8eea 100644 --- a/tests/StaticAnalysis/MyCustomForm.php +++ b/tests/StaticAnalysis/MyCustomForm.php @@ -13,15 +13,16 @@ class MyCustomForm extends CustomForm /** * {@inheritdoc} */ + #[\Override] protected function configure(FormBuilderInterface $builder): void { $builder->generates(MyGeneratedEntity::class); - $builder->string('foo')->length(['min' => 1])->getter()->setter(); + $builder->string('foo')->length(min: 1)->getter()->setter(); $builder->string('bar')->required()->getter()->setter(); } - public static function test() + public static function test(): void { $form = new MyCustomForm(); $form->submit(['foo' => 'a', 'bar' => 'b']); @@ -34,6 +35,9 @@ public static function check(?MyGeneratedEntity $entity): void } } +/** + * @psalm-suppress MissingConstructor + */ class MyGeneratedEntity { /** diff --git a/tests/StaticAnalysis/SubClassChildBuilderForm.php b/tests/StaticAnalysis/SubClassChildBuilderForm.php index 8d9796d..fe43b47 100644 --- a/tests/StaticAnalysis/SubClassChildBuilderForm.php +++ b/tests/StaticAnalysis/SubClassChildBuilderForm.php @@ -13,6 +13,7 @@ class SubClassChildBuilderForm extends CustomForm /** * {@inheritdoc} */ + #[\Override] protected function configure(FormBuilderInterface $builder): void { $builder->dateTime('foo') diff --git a/tests/Util/HttpValueTest.php b/tests/Util/HttpValueTest.php index 1bb7836..527849d 100644 --- a/tests/Util/HttpValueTest.php +++ b/tests/Util/HttpValueTest.php @@ -2,6 +2,7 @@ namespace Bdf\Form\Util; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class HttpValueTest extends TestCase @@ -23,18 +24,14 @@ public function test_isEmpty() $this->assertFalse(HttpValue::isEmpty(false)); } - /** - * @dataProvider emptyValues - */ + #[DataProvider('emptyValues')] public function test_orDefault_with_empty_value($value) { $this->assertSame($value, HttpValue::orDefault($value, null)); $this->assertSame('foo', HttpValue::orDefault($value, 'foo')); } - /** - * @dataProvider notEmptyValues - */ + #[DataProvider('notEmptyValues')] public function test_orDefault_with_not_empty_value($value) { $this->assertSame($value, HttpValue::orDefault($value, null)); @@ -44,7 +41,7 @@ public function test_orDefault_with_not_empty_value($value) /** * @return array */ - public function emptyValues() + public static function emptyValues() { return [ [null], @@ -56,7 +53,7 @@ public function emptyValues() /** * @return array */ - public function notEmptyValues() + public static function notEmptyValues() { return [ ['0'], diff --git a/tests/Util/ValidatorBuilderTraitTest.php b/tests/Util/ValidatorBuilderTraitTest.php index 7b6394c..3bc3005 100644 --- a/tests/Util/ValidatorBuilderTraitTest.php +++ b/tests/Util/ValidatorBuilderTraitTest.php @@ -57,17 +57,6 @@ public function test_satisfy_order() $this->assertEquals('error 3', $element->error()->global()); } - /** - * - */ - public function test_satisfy_with_className_and_options() - { - $element = $this->builder->satisfy(NotEqualTo::class, ['value' => 'hello'])->buildElement(); - - $this->assertFalse($element->submit('hello')->valid()); - $this->assertTrue($element->submit('world')->valid()); - } - /** * */ @@ -225,9 +214,9 @@ public function test_transformerExceptionValidation() public function test_defaultTransformerExceptionConstraintOptions() { $builder = new class extends StringElementBuilder { - protected function defaultTransformerExceptionConstraintOptions(): array + protected function defaultTransformerExceptionConstraint(): TransformerExceptionConstraint { - return ['message' => 'my error message', 'code' => 'MY_ERROR']; + return new TransformerExceptionConstraint(message: 'my error message', code: 'MY_ERROR'); } }; diff --git a/tests/Validator/ConstraintValueValidatorTest.php b/tests/Validator/ConstraintValueValidatorTest.php index dfdf142..cc13a96 100644 --- a/tests/Validator/ConstraintValueValidatorTest.php +++ b/tests/Validator/ConstraintValueValidatorTest.php @@ -105,7 +105,7 @@ public function test_onTransformerException() public function test_onTransformerException_ignoreException() { $element = new StringElement(); - $validator = new ConstraintValueValidator([], new TransformerExceptionConstraint(['ignoreException' => true])); + $validator = new ConstraintValueValidator([], new TransformerExceptionConstraint(ignoreException: true)); $error = $validator->onTransformerException(new \Exception('my error'), 'foo', $element); @@ -118,7 +118,7 @@ public function test_onTransformerException_ignoreException() public function test_onTransformerException_custom_message_and_code() { $element = new StringElement(); - $validator = new ConstraintValueValidator([], new TransformerExceptionConstraint(['message' => 'message', 'code' => 'CODE_ERROR'])); + $validator = new ConstraintValueValidator([], new TransformerExceptionConstraint(message: 'message', code: 'CODE_ERROR')); $error = $validator->onTransformerException(new \Exception('my error'), 'foo', $element); diff --git a/tests/View/ConstraintsNormalizerTest.php b/tests/View/ConstraintsNormalizerTest.php index eea7a97..af8abb4 100644 --- a/tests/View/ConstraintsNormalizerTest.php +++ b/tests/View/ConstraintsNormalizerTest.php @@ -27,6 +27,6 @@ public function test_normalize() Length::class => ['min' => 3, 'max' => 5], LessThanOrEqual::class => ['value' => 42], NotEqualTo::class => ['value' => 666], - ], ConstraintsNormalizer::normalize(new ConstraintValueValidator([new NotBlank(), new Length(['min' => 3, 'max' => 5]), new LessThanOrEqual(42), new NotEqualTo(666)]))); + ], ConstraintsNormalizer::normalize(new ConstraintValueValidator([new NotBlank(), new Length(min: 3, max: 5), new LessThanOrEqual(42), new NotEqualTo(666)]))); } }