From 2cf5a727802d227342b47207539512b897ca3a20 Mon Sep 17 00:00:00 2001 From: George Steel Date: Tue, 9 Jan 2024 12:33:35 +0000 Subject: [PATCH 1/5] Improve type inference for FormElementManager::get() Signed-off-by: George Steel --- src/FormElementManager.php | 5 +++-- test/StaticAnalysis/FormElementManagerType.php | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/FormElementManager.php b/src/FormElementManager.php index 41f75aa67..5af73c07f 100644 --- a/src/FormElementManager.php +++ b/src/FormElementManager.php @@ -321,9 +321,10 @@ public function configure(array $config) * createFromInvokable() will use these and pass them to the instance * constructor if not null and a non-empty array. * - * @param class-string|string $name Service name of plugin to retrieve. + * @template T of ElementInterface + * @param class-string|string $name Service name of plugin to retrieve. * @param null|array $options Options to use when creating the instance. - * @psalm-return ($name is class-string ? ElementInterface : mixed) + * @psalm-return ($name is class-string ? T : mixed) */ public function get($name, ?array $options = null): mixed { diff --git a/test/StaticAnalysis/FormElementManagerType.php b/test/StaticAnalysis/FormElementManagerType.php index faf203da7..a77e1aa65 100644 --- a/test/StaticAnalysis/FormElementManagerType.php +++ b/test/StaticAnalysis/FormElementManagerType.php @@ -7,6 +7,7 @@ use Laminas\Form\Element\Checkbox; use Laminas\Form\ElementInterface; use Laminas\Form\FormElementManager; +use LaminasTest\Form\TestAsset\NewProductForm; final class FormElementManagerType { @@ -23,4 +24,9 @@ public function getReturnsMixedWhenGivenAnAlias(): mixed { return $this->manager->get('foo'); } + + public function getReturnsObjectOfClassWhenGivenFQCN(): NewProductForm + { + return $this->manager->get(NewProductForm::class); + } } From f12cd91d82c4198f96074ed19fccbef20c56e338 Mon Sep 17 00:00:00 2001 From: George Steel Date: Wed, 10 Jan 2024 14:36:28 +0000 Subject: [PATCH 2/5] Form element manager always returns an element interface or throws an exception. Signed-off-by: George Steel --- src/FormElementManager.php | 2 +- test/FormElementManagerTest.php | 3 +++ test/StaticAnalysis/FormElementManagerType.php | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/FormElementManager.php b/src/FormElementManager.php index 5af73c07f..372a7af66 100644 --- a/src/FormElementManager.php +++ b/src/FormElementManager.php @@ -324,7 +324,7 @@ public function configure(array $config) * @template T of ElementInterface * @param class-string|string $name Service name of plugin to retrieve. * @param null|array $options Options to use when creating the instance. - * @psalm-return ($name is class-string ? T : mixed) + * @psalm-return ($name is class-string ? T : ElementInterface) */ public function get($name, ?array $options = null): mixed { diff --git a/test/FormElementManagerTest.php b/test/FormElementManagerTest.php index 9a5bdf49c..2b62eefa0 100644 --- a/test/FormElementManagerTest.php +++ b/test/FormElementManagerTest.php @@ -25,6 +25,7 @@ use function array_pop; use function array_shift; +use function assert; use function count; use function method_exists; use function strtoupper; @@ -44,6 +45,7 @@ protected function setUp(): void public function testInjectToFormFactoryAware(): void { $form = $this->manager->get('Form'); + assert($form instanceof Form); self::assertSame($this->manager, $form->getFormFactory()->getFormElementManager()); } @@ -59,6 +61,7 @@ public function testInjectsFormElementManagerToFormComposedByFormFactoryAwareEle return $form; }); $form = $this->manager->get('my-form'); + assert($form instanceof Form); self::assertSame($factory, $form->getFormFactory()); self::assertSame($this->manager, $form->getFormFactory()->getFormElementManager()); } diff --git a/test/StaticAnalysis/FormElementManagerType.php b/test/StaticAnalysis/FormElementManagerType.php index a77e1aa65..8061aba05 100644 --- a/test/StaticAnalysis/FormElementManagerType.php +++ b/test/StaticAnalysis/FormElementManagerType.php @@ -20,7 +20,7 @@ public function getReturnsAnElementInterfaceWhenGivenAClassString(): ElementInte return $this->manager->get(Checkbox::class); } - public function getReturnsMixedWhenGivenAnAlias(): mixed + public function getReturnsElementInterfaceWhenGivenAnAlias(): ElementInterface { return $this->manager->get('foo'); } From cb68f54bc1f1dce6f77f80d4e7f1d6d5c0f8baa9 Mon Sep 17 00:00:00 2001 From: George Steel Date: Wed, 10 Jan 2024 14:37:57 +0000 Subject: [PATCH 3/5] Baseline issue with `FormElementManager::get()` related to inheritance Signed-off-by: George Steel --- psalm-baseline.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 966d560a9..5a34f811a 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -241,6 +241,9 @@ Element\DateTime::class Element\DateTime::class + + parent::get($name, $options) + $aliases $factories From dcd82eea07d9c73bcb339d9cefb9f11f9c6ee26f Mon Sep 17 00:00:00 2001 From: George Steel Date: Wed, 10 Jan 2024 14:39:41 +0000 Subject: [PATCH 4/5] Prefer `@return` over `@psalm-return` Signed-off-by: George Steel --- src/FormElementManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FormElementManager.php b/src/FormElementManager.php index 372a7af66..cc7a99a22 100644 --- a/src/FormElementManager.php +++ b/src/FormElementManager.php @@ -324,7 +324,7 @@ public function configure(array $config) * @template T of ElementInterface * @param class-string|string $name Service name of plugin to retrieve. * @param null|array $options Options to use when creating the instance. - * @psalm-return ($name is class-string ? T : ElementInterface) + * @return ($name is class-string ? T : ElementInterface) */ public function get($name, ?array $options = null): mixed { From 60f1cea8d65e188bb6e9bc3b32a9158275f1faaf Mon Sep 17 00:00:00 2001 From: George Steel Date: Wed, 10 Jan 2024 14:49:48 +0000 Subject: [PATCH 5/5] Assert that a class string not implementing ElementInterface is treated the same as a string literal Signed-off-by: George Steel --- test/StaticAnalysis/FormElementManagerType.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/StaticAnalysis/FormElementManagerType.php b/test/StaticAnalysis/FormElementManagerType.php index 8061aba05..fc4426555 100644 --- a/test/StaticAnalysis/FormElementManagerType.php +++ b/test/StaticAnalysis/FormElementManagerType.php @@ -29,4 +29,9 @@ public function getReturnsObjectOfClassWhenGivenFQCN(): NewProductForm { return $this->manager->get(NewProductForm::class); } + + public function getReturnsElementInterfaceWhenGivenInvalidClassStringType(): ElementInterface + { + return $this->manager->get(self::class); + } }