diff --git a/psalm-baseline.xml b/psalm-baseline.xml index a8254f306..4e63f6778 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -73,6 +73,14 @@ $this $this $this + $this + $this + $this + $this + $this + $this + $this + $this @@ -158,6 +166,8 @@ $this $this $this + $this + $this $object @@ -187,6 +197,9 @@ $this $this $this + $this + $this + $this $cKey @@ -900,11 +913,6 @@ ])]]> - - - array - - Annotation\AllowEmpty() @@ -921,26 +929,6 @@ Annotation\ContinueIfEmpty(true) - - - array - - - - - array - - - - - array - - - - - array - - CustomCollection @@ -958,16 +946,6 @@ view]]> - - - array - - - - - array - - $name @@ -977,15 +955,7 @@ $dependency - - - array[] - - - - array[] - $name $options @@ -998,56 +968,23 @@ - - array[] - $name $options - - array[] - - - $name - $options - - - - - array[] - $name $options - - - array[] - - - - - array - - - - array[] - $name $options - - - array - - $storedValue diff --git a/src/Form.php b/src/Form.php index e0b63aaf7..1cdd7ec28 100644 --- a/src/Form.php +++ b/src/Form.php @@ -24,6 +24,10 @@ use function is_object; use function sprintf; +/** + * @template TFilteredValues + * @implements FormInterface + */ class Form extends Fieldset implements FormInterface { /** @var array */ @@ -487,7 +491,7 @@ public function isValid(): bool * By default, retrieves normalized values; pass one of the * FormInterface::VALUES_* constants to shape the behavior. * - * @return array|object + * @inheritDoc * @throws Exception\DomainException */ public function getData(int $flag = FormInterface::VALUES_NORMALIZED) @@ -582,6 +586,7 @@ protected function prepareValidationGroup(Fieldset $formOrFieldset, array $data, /** * Set the input filter used by this form * + * @param InputFilterInterface $inputFilter * @return $this */ public function setInputFilter(InputFilterInterface $inputFilter) @@ -599,6 +604,8 @@ public function setInputFilter(InputFilterInterface $inputFilter) /** * Retrieve input filter used by this form + * + * @return InputFilterInterface */ public function getInputFilter(): InputFilterInterface { diff --git a/src/FormInterface.php b/src/FormInterface.php index 4955bdb62..a2f8c7c6e 100644 --- a/src/FormInterface.php +++ b/src/FormInterface.php @@ -6,6 +6,9 @@ use Laminas\InputFilter\InputFilterInterface; +/** + * @template TFilteredValues + */ interface FormInterface extends FieldsetInterface { public const BIND_ON_VALIDATE = 0x00; @@ -42,12 +45,15 @@ public function setBindOnValidate(int $bindOnValidateFlag); /** * Set input filter * + * @param InputFilterInterface $inputFilter * @return $this */ public function setInputFilter(InputFilterInterface $inputFilter); /** * Retrieve input filter + * + * @return InputFilterInterface */ public function getInputFilter(): InputFilterInterface; @@ -64,7 +70,13 @@ public function isValid(): bool; * By default, retrieves normalized values; pass one of the VALUES_* * constants to shape the behavior. * - * @return array|object + * @param self::VALUES_* $flag + * @return TFilteredValues|object|array + * @psalm-return ( + * $flag is self::VALUES_NORMALIZED + * ? TFilteredValues|object + * : ($flag is self::VALUES_RAW ? array : TFilteredValues) + * ) */ public function getData(int $flag = FormInterface::VALUES_NORMALIZED); diff --git a/test/FieldsetTest.php b/test/FieldsetTest.php index 71c56d06f..5da8ccaa0 100644 --- a/test/FieldsetTest.php +++ b/test/FieldsetTest.php @@ -641,4 +641,24 @@ public function testSetHydratorByNameMethodShouldSetValidHydratorForForm(): void // Test self::assertSame($this->hydrator, $this->fieldset->getHydrator()); } + + public function testTheValueOfAFieldsetIsNullWhenComposedInAForm(): void + { + $fieldset = new Fieldset('set'); + $fieldset->add(new Element\Text('text')); + $fieldset->setUseAsBaseFieldset(true); + + $form = new Form(); + $form->add($fieldset); + $payload = [ + 'set' => [ + 'text' => 'Test Value', + ], + ]; + $form->setData($payload); + + self::assertTrue($form->isValid()); + self::assertNull($fieldset->getValue()); + self::assertSame($payload, $form->getData()); + } } diff --git a/test/Integration/TestAsset/Form.php b/test/Integration/TestAsset/Form.php index 4360eb2fd..e432879c8 100644 --- a/test/Integration/TestAsset/Form.php +++ b/test/Integration/TestAsset/Form.php @@ -7,6 +7,7 @@ use Laminas\Form\Form as BaseForm; use Laminas\Form\FormElementManager; +/** @extends BaseForm> */ final class Form extends BaseForm { /** @var null|FormElementManager */ diff --git a/test/StaticAnalysis/Asset/ExampleForm.php b/test/StaticAnalysis/Asset/ExampleForm.php new file mode 100644 index 000000000..e68f62d0e --- /dev/null +++ b/test/StaticAnalysis/Asset/ExampleForm.php @@ -0,0 +1,28 @@ + + */ +final class ExampleForm extends Form +{ + public function init(): void + { + $this->add([ + 'name' => 'string', + 'type' => Text::class, + ]); + $this->add([ + 'name' => 'number', + 'type' => Number::class, + ]); + } +} diff --git a/test/StaticAnalysis/Asset/ExampleInputFilter.php b/test/StaticAnalysis/Asset/ExampleInputFilter.php new file mode 100644 index 000000000..9f81f9895 --- /dev/null +++ b/test/StaticAnalysis/Asset/ExampleInputFilter.php @@ -0,0 +1,34 @@ + + */ +final class ExampleInputFilter extends InputFilter +{ + public function init(): void + { + $this->add([ + 'name' => 'string', + 'required' => true, + ]); + + $this->add([ + 'name' => 'number', + 'required' => true, + 'filters' => [ + ['name' => ToInt::class], + ], + ]); + } +} diff --git a/test/StaticAnalysis/FormTemplates.php b/test/StaticAnalysis/FormTemplates.php new file mode 100644 index 000000000..6b120abfe --- /dev/null +++ b/test/StaticAnalysis/FormTemplates.php @@ -0,0 +1,57 @@ +getInputFilter()->getValues()['string']; + } + + /** @return non-empty-string */ + public function getDataWithExplicitArray(): string + { + $form = new ExampleForm(); + $data = $form->getData(FormInterface::VALUES_AS_ARRAY); + + return $data['string']; + } + + /** @return non-empty-string */ + public function getDataWithValuesNormalized(): string + { + $form = new ExampleForm(); + $data = $form->getData(FormInterface::VALUES_NORMALIZED); + assert(is_array($data)); + + return $data['string']; + } + + /** @return non-empty-string */ + public function testThatFluidReturnTypesPreserveTemplatesForSetPreferFormInputFilter(): string + { + $form = new ExampleForm(); + return $form->setPreferFormInputFilter(true) + ->getData(FormInterface::VALUES_AS_ARRAY)['string']; + } + + /** @return non-empty-string */ + public function testThatFluidReturnTypesPreserveTemplatesForSetWrapElements(): string + { + $form = new ExampleForm(); + return $form->setWrapElements(true) + ->getData(FormInterface::VALUES_AS_ARRAY)['string']; + } +} diff --git a/test/TestAsset/AddressFieldset.php b/test/TestAsset/AddressFieldset.php index 1a5d18c20..8607d1ac1 100644 --- a/test/TestAsset/AddressFieldset.php +++ b/test/TestAsset/AddressFieldset.php @@ -38,12 +38,7 @@ public function __construct() $this->add($phones); } - /** - * Should return an array specification compatible with - * {@link Laminas\InputFilter\Factory::createInputFilter()}. - * - * @return array - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/Annotation/Form.php b/test/TestAsset/Annotation/Form.php index 0d6928cda..3ce1e5a52 100644 --- a/test/TestAsset/Annotation/Form.php +++ b/test/TestAsset/Annotation/Form.php @@ -6,6 +6,7 @@ use Laminas\Form\Form as BaseForm; +/** @extends BaseForm> */ class Form extends BaseForm { } diff --git a/test/TestAsset/BasicFieldset.php b/test/TestAsset/BasicFieldset.php index 3099fe930..18950a3ca 100644 --- a/test/TestAsset/BasicFieldset.php +++ b/test/TestAsset/BasicFieldset.php @@ -22,12 +22,7 @@ public function __construct() $this->add($nestedFieldset); } - /** - * Should return an array specification compatible with - * {@link Laminas\InputFilter\Factory::createInputFilter()}. - * - * @return array - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/CategoryFieldset.php b/test/TestAsset/CategoryFieldset.php index 93ff0c454..fdeb112a1 100644 --- a/test/TestAsset/CategoryFieldset.php +++ b/test/TestAsset/CategoryFieldset.php @@ -29,12 +29,7 @@ public function __construct() ]); } - /** - * Should return an array specification compatible with - * {@link Laminas\InputFilter\Factory::createInputFilter()}. - * - * @return array - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/CityFieldset.php b/test/TestAsset/CityFieldset.php index 765be3537..6402cf216 100644 --- a/test/TestAsset/CityFieldset.php +++ b/test/TestAsset/CityFieldset.php @@ -32,12 +32,7 @@ public function __construct() $this->add($country); } - /** - * Should return an array specification compatible with - * {@link Laminas\InputFilter\Factory::createInputFilter()}. - * - * @return array - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/CountryFieldset.php b/test/TestAsset/CountryFieldset.php index 130b6f911..28780cab9 100644 --- a/test/TestAsset/CountryFieldset.php +++ b/test/TestAsset/CountryFieldset.php @@ -28,12 +28,7 @@ public function __construct() $this->add($continent); } - /** - * Should return an array specification compatible with - * {@link Laminas\InputFilter\Factory::createInputFilter()}. - * - * @return array - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/CreateAddressForm.php b/test/TestAsset/CreateAddressForm.php index a4713a107..2af96fb32 100644 --- a/test/TestAsset/CreateAddressForm.php +++ b/test/TestAsset/CreateAddressForm.php @@ -7,6 +7,21 @@ use Laminas\Form\Form; use Laminas\Hydrator\ClassMethodsHydrator; +/** + * @psalm-type Payload = array{ + * street: non-empty-string, + * phones: list, + * city: array{ + * name: non-empty-string, + * zipCode: non-empty-string, + * country: array{ + * name: non-empty-string, + * continent: non-empty-string, + * }, + * }, + * } + * @extends Form + */ class CreateAddressForm extends Form { public function __construct() @@ -15,8 +30,7 @@ public function __construct() $this ->setAttribute('method', 'post') - ->setHydrator(new ClassMethodsHydrator(false)) - ->setInputFilter(new InputFilter()); + ->setHydrator(new ClassMethodsHydrator(false)); $address = new AddressFieldset(); $address->setUseAsBaseFieldset(true); diff --git a/test/TestAsset/CustomCreatedForm.php b/test/TestAsset/CustomCreatedForm.php index 2328bc78a..5b8ba3e82 100644 --- a/test/TestAsset/CustomCreatedForm.php +++ b/test/TestAsset/CustomCreatedForm.php @@ -7,6 +7,7 @@ use DateTime; use Laminas\Form\Form; +/** @extends Form> */ class CustomCreatedForm extends Form { public function __construct(private DateTime $created, ?string $name = null, array $options = []) diff --git a/test/TestAsset/CustomForm.php b/test/TestAsset/CustomForm.php deleted file mode 100644 index 4954f087e..000000000 --- a/test/TestAsset/CustomForm.php +++ /dev/null @@ -1,69 +0,0 @@ -setAttribute('method', 'post') - ->setHydrator(new ClassMethodsHydrator()); - - $field1 = new Element('name', ['label' => 'Name']); - $field1->setAttribute('type', 'text'); - $this->add($field1); - - $field2 = new Element('email', ['label' => 'Email']); - $field2->setAttribute('type', 'text'); - $this->add($field2); - - $this->add([ - 'name' => 'csrf', - 'type' => Csrf::class, - 'attributes' => [], - ]); - - $this->add([ - 'name' => 'submit', - 'attributes' => [ - 'type' => 'submit', - ], - ]); - } - - /** - * @return array[] - */ - public function getInputFilterSpecification() - { - return [ - 'name' => [ - 'required' => true, - 'filters' => [ - ['name' => StringTrim::class], - ], - ], - 'email' => [ - 'required' => true, - 'filters' => [ - ['name' => StringTrim::class], - ], - 'validators' => [ - new Validator\EmailAddress(), - ], - ], - ]; - } -} diff --git a/test/TestAsset/ElementWithFilter.php b/test/TestAsset/ElementWithFilter.php index ed496d983..48f1c5b34 100644 --- a/test/TestAsset/ElementWithFilter.php +++ b/test/TestAsset/ElementWithFilter.php @@ -12,13 +12,11 @@ class ElementWithFilter extends Element implements InputProviderInterface { - /** - * @return array - */ + /** @inheritDoc */ public function getInputSpecification() { return [ - 'name' => $this->getName(), + 'name' => (string) $this->getName(), 'required' => true, 'filters' => [ ['name' => StringTrim::class], diff --git a/test/TestAsset/ElementWithStringToArrayFilter.php b/test/TestAsset/ElementWithStringToArrayFilter.php index a0504351c..10a9ad685 100644 --- a/test/TestAsset/ElementWithStringToArrayFilter.php +++ b/test/TestAsset/ElementWithStringToArrayFilter.php @@ -10,13 +10,11 @@ class ElementWithStringToArrayFilter extends Element implements InputProviderInterface { - /** - * @return array - */ + /** @inheritDoc */ public function getInputSpecification() { return [ - 'name' => $this->getName(), + 'name' => (string) $this->getName(), 'required' => true, 'filters' => [ ['name' => StringToArrayFilter::class], diff --git a/test/TestAsset/FieldsetWithInputFilter.php b/test/TestAsset/FieldsetWithInputFilter.php index 2be219e6a..424b2724d 100644 --- a/test/TestAsset/FieldsetWithInputFilter.php +++ b/test/TestAsset/FieldsetWithInputFilter.php @@ -12,9 +12,7 @@ class FieldsetWithInputFilter extends Fieldset implements InputFilterProviderInterface { - /** - * @return array[] - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/FileInputFilterProviderFieldset.php b/test/TestAsset/FileInputFilterProviderFieldset.php index 6a9512f92..1d613e3bf 100644 --- a/test/TestAsset/FileInputFilterProviderFieldset.php +++ b/test/TestAsset/FileInputFilterProviderFieldset.php @@ -10,9 +10,7 @@ class FileInputFilterProviderFieldset extends Fieldset implements InputFilterProviderInterface { - /** - * @inheritDoc - */ + /** @inheritDoc */ public function __construct($name = null, $options = []) { parent::__construct($name, $options); @@ -26,9 +24,7 @@ public function __construct($name = null, $options = []) ]); } - /** - * @return array[] - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/FileInputFilterProviderForm.php b/test/TestAsset/FileInputFilterProviderForm.php index 6ad5418bc..da47e1c33 100644 --- a/test/TestAsset/FileInputFilterProviderForm.php +++ b/test/TestAsset/FileInputFilterProviderForm.php @@ -6,6 +6,7 @@ use Laminas\Form\Form; +/** @extends Form> */ class FileInputFilterProviderForm extends Form { /** @@ -16,7 +17,7 @@ public function __construct($name = null, $options = []) parent::__construct($name, $options); $this->add([ - 'type' => __NAMESPACE__ . '\FileInputFilterProviderFieldset', + 'type' => FileInputFilterProviderFieldset::class, 'name' => 'file_fieldset', ]); } diff --git a/test/TestAsset/FormCollection.php b/test/TestAsset/FormCollection.php index 62b8113ba..abdcefc8e 100644 --- a/test/TestAsset/FormCollection.php +++ b/test/TestAsset/FormCollection.php @@ -8,12 +8,12 @@ use Laminas\Form\Element\Color as ColorElement; use Laminas\Form\Form; +/** @extends Form> */ class FormCollection extends Form { public function __construct() { parent::__construct('collection'); - $this->setInputFilter(new InputFilter()); $element = new ColorElement('color'); $this->add([ diff --git a/test/TestAsset/InputFilterProvider.php b/test/TestAsset/InputFilterProvider.php index 25bb525fa..b5125d44a 100644 --- a/test/TestAsset/InputFilterProvider.php +++ b/test/TestAsset/InputFilterProvider.php @@ -7,6 +7,7 @@ use Laminas\Form\Form; use Laminas\InputFilter\InputFilterProviderInterface; +/** @extends Form */ class InputFilterProvider extends Form implements InputFilterProviderInterface { /** @@ -24,9 +25,7 @@ public function __construct($name = null, $options = []) ]); } - /** - * @return array[] - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/InputFilterProviderFieldset.php b/test/TestAsset/InputFilterProviderFieldset.php index f426e3c42..afc63a2e1 100644 --- a/test/TestAsset/InputFilterProviderFieldset.php +++ b/test/TestAsset/InputFilterProviderFieldset.php @@ -26,9 +26,7 @@ public function __construct($name = null, $options = []) $this->add(new BasicFieldset()); } - /** - * @return array[] - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/InputFilterProviderWithFieldset.php b/test/TestAsset/InputFilterProviderWithFieldset.php index 42c37badb..bdced50f6 100644 --- a/test/TestAsset/InputFilterProviderWithFieldset.php +++ b/test/TestAsset/InputFilterProviderWithFieldset.php @@ -7,12 +7,11 @@ use Laminas\Form\Form; use Laminas\InputFilter\InputFilterProviderInterface; +/** @extends Form */ class InputFilterProviderWithFieldset extends Form implements InputFilterProviderInterface { - /** - * @inheritDoc - */ - public function __construct($name = null, $options = []) + /** @inheritDoc */ + public function __construct(string|null $name = null, array $options = []) { parent::__construct($name, $options); @@ -26,9 +25,7 @@ public function __construct($name = null, $options = []) $this->add(new BasicFieldset()); } - /** - * @return array[] - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/InvokableForm.php b/test/TestAsset/InvokableForm.php index a51066505..7093ba373 100644 --- a/test/TestAsset/InvokableForm.php +++ b/test/TestAsset/InvokableForm.php @@ -6,6 +6,7 @@ use Laminas\Form\Form; +/** @extends Form> */ final class InvokableForm extends Form { } diff --git a/test/TestAsset/MyFieldset.php b/test/TestAsset/MyFieldset.php index 2dddec5bb..5dc9eebbc 100644 --- a/test/TestAsset/MyFieldset.php +++ b/test/TestAsset/MyFieldset.php @@ -18,9 +18,7 @@ public function __construct() ]); } - /** - * @return array[] - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/NestedCollectionsForm.php b/test/TestAsset/NestedCollectionsForm.php index e543285ec..3a45f852d 100644 --- a/test/TestAsset/NestedCollectionsForm.php +++ b/test/TestAsset/NestedCollectionsForm.php @@ -9,6 +9,7 @@ use Laminas\Form\Fieldset; use Laminas\Form\Form; +/** @extends Form> */ class NestedCollectionsForm extends Form { public function __construct() diff --git a/test/TestAsset/NestedFieldset.php b/test/TestAsset/NestedFieldset.php index eeb70c77b..fb5aa7e3f 100644 --- a/test/TestAsset/NestedFieldset.php +++ b/test/TestAsset/NestedFieldset.php @@ -20,12 +20,7 @@ public function __construct() $this->add($field); } - /** - * Should return an array specification compatible with - * {@link Laminas\InputFilter\Factory::createInputFilter()}. - * - * @return array - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/NewProductForm.php b/test/TestAsset/NewProductForm.php index 5e98c9391..e06734bef 100644 --- a/test/TestAsset/NewProductForm.php +++ b/test/TestAsset/NewProductForm.php @@ -7,6 +7,7 @@ use Laminas\Form\Form; use Laminas\Hydrator\ClassMethodsHydrator; +/** @extends Form> */ class NewProductForm extends Form { public function __construct() @@ -15,8 +16,7 @@ public function __construct() $this ->setAttribute('method', 'post') - ->setHydrator(new ClassMethodsHydrator()) - ->setInputFilter(new InputFilter()); + ->setHydrator(new ClassMethodsHydrator()); $fieldset = new ProductFieldset(); $fieldset->setUseAsBaseFieldset(true); diff --git a/test/TestAsset/OrphansFieldset.php b/test/TestAsset/OrphansFieldset.php index afe37e0f9..251c79d17 100644 --- a/test/TestAsset/OrphansFieldset.php +++ b/test/TestAsset/OrphansFieldset.php @@ -28,9 +28,7 @@ public function __construct($name = null, $options = []) ]); } - /** - * @return array[] - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [ diff --git a/test/TestAsset/OrphansForm.php b/test/TestAsset/OrphansForm.php index e8d03be31..aa7ac2caa 100644 --- a/test/TestAsset/OrphansForm.php +++ b/test/TestAsset/OrphansForm.php @@ -6,8 +6,8 @@ use Laminas\Form\Element\Collection; use Laminas\Form\Form; -use LaminasTest\Form\TestAsset\OrphansFieldset; +/** @extends Form> */ class OrphansForm extends Form { public function __construct() @@ -15,8 +15,7 @@ public function __construct() parent::__construct('orphans'); $this->setAttribute('method', 'post') - ->setBindOnValidate(self::BIND_ON_VALIDATE) - ->setInputFilter(new InputFilter()); + ->setBindOnValidate(self::BIND_ON_VALIDATE); //adds a collection of 2 $this->add( diff --git a/test/TestAsset/ProductFieldset.php b/test/TestAsset/ProductFieldset.php index f32eeffbd..8f03ad575 100644 --- a/test/TestAsset/ProductFieldset.php +++ b/test/TestAsset/ProductFieldset.php @@ -65,12 +65,7 @@ public function __construct() ]); } - /** - * Should return an array specification compatible with - * {@link Laminas\InputFilter\Factory::createInputFilter()}. - * - * @return array - */ + /** @inheritDoc */ public function getInputFilterSpecification() { return [