From 15e9e3df31e9c640380ebfecf9b4b238a4555f6c Mon Sep 17 00:00:00 2001 From: George Steel Date: Tue, 9 Jan 2024 14:25:36 +0000 Subject: [PATCH 1/6] Add an assertion to remove the possibility that the value is an `InputInterface` Signed-off-by: George Steel --- src/Factory.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Factory.php b/src/Factory.php index 9769c531..41074718 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -12,6 +12,7 @@ use Laminas\Validator\ValidatorInterface; use Traversable; +use function assert; use function class_exists; use function get_debug_type; use function gettype; @@ -317,6 +318,7 @@ public function createInputFilter($inputFilterSpecification) } $inputFilter = $this->getInputFilterManager()->get($type); + assert($inputFilter instanceof InputFilterInterface); // As opposed to InputInterface if ($inputFilter instanceof CollectionInputFilter) { $inputFilter->setFactory($this); From 774bb408067388e75352e3b6eadc6c2d7bb593b2 Mon Sep 17 00:00:00 2001 From: George Steel Date: Tue, 9 Jan 2024 14:26:16 +0000 Subject: [PATCH 2/6] Suppress `UnusedClass` in SA Tests in config instead of inline Signed-off-by: George Steel --- psalm.xml.dist | 8 ++++++++ test/StaticAnalysis/AddingInputsWithArraySpecs.php | 1 - .../InputFilterTemplateInfersFilterValueTypes.php | 1 - 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/psalm.xml.dist b/psalm.xml.dist index 62def1af..f495a985 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -22,6 +22,14 @@ + + + + + + + + diff --git a/test/StaticAnalysis/AddingInputsWithArraySpecs.php b/test/StaticAnalysis/AddingInputsWithArraySpecs.php index 53e701c8..bd03b298 100644 --- a/test/StaticAnalysis/AddingInputsWithArraySpecs.php +++ b/test/StaticAnalysis/AddingInputsWithArraySpecs.php @@ -10,7 +10,6 @@ /** * @extends InputFilter> - * @psalm-suppress UnusedClass */ final class AddingInputsWithArraySpecs extends InputFilter { diff --git a/test/StaticAnalysis/InputFilterTemplateInfersFilterValueTypes.php b/test/StaticAnalysis/InputFilterTemplateInfersFilterValueTypes.php index bd33fc08..a3564710 100644 --- a/test/StaticAnalysis/InputFilterTemplateInfersFilterValueTypes.php +++ b/test/StaticAnalysis/InputFilterTemplateInfersFilterValueTypes.php @@ -8,7 +8,6 @@ use function count; use function reset; -/** @psalm-suppress UnusedClass */ final class InputFilterTemplateInfersFilterValueTypes { public function __construct( From be8b82928996c732d060df3592d30f477e9712ba Mon Sep 17 00:00:00 2001 From: George Steel Date: Tue, 9 Jan 2024 14:29:37 +0000 Subject: [PATCH 3/6] Override `get()` so that we can properly type the return value. Signed-off-by: George Steel --- psalm-baseline.xml | 13 --------- src/InputFilterPluginManager.php | 14 +++++++++- .../InputFilterPluginManagerType.php | 27 +++++++++++++++++++ 3 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 test/StaticAnalysis/InputFilterPluginManagerType.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 8dab1c92..f9474070 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -104,12 +104,6 @@ - - $inputFilter - - - InputFilterInterface - @@ -142,10 +136,6 @@ $value $value - - add - add - DeprecatedMethod DocblockTypeContradiction @@ -288,9 +278,6 @@ InputFilterPluginManager InputFilterPluginManager - - Plu - $factories diff --git a/src/InputFilterPluginManager.php b/src/InputFilterPluginManager.php index 8f73026c..92a32328 100644 --- a/src/InputFilterPluginManager.php +++ b/src/InputFilterPluginManager.php @@ -26,7 +26,6 @@ * @psalm-import-type ServiceManagerConfiguration from ServiceManager * @template InstanceType of InputFilterInterface|InputInterface * @extends AbstractPluginManager - * @method InputFilterInterface|InputInterface get(string $name, ?array $options = null) */ class InputFilterPluginManager extends AbstractPluginManager { @@ -188,4 +187,17 @@ public function validatePlugin($plugin) throw new Exception\RuntimeException($e->getMessage(), $e->getCode(), $e); } } + + /** + * @inheritDoc + * @template T1 of InputInterface + * @template T2 of InputFilterInterface + * @param class-string|class-string|string $name + * @return T1|T2 + * @psalm-return ($name is class-string ? T1 : ($name is class-string ? T2 : T1|T2)) + */ + public function get($name, ?array $options = null) + { + return parent::get($name, $options); + } } diff --git a/test/StaticAnalysis/InputFilterPluginManagerType.php b/test/StaticAnalysis/InputFilterPluginManagerType.php new file mode 100644 index 00000000..5c9dd2c6 --- /dev/null +++ b/test/StaticAnalysis/InputFilterPluginManagerType.php @@ -0,0 +1,27 @@ +manager->get($anyString); + } + + public function getWithFQCNWillReturnTheObjectOfType(): InputFilterWithTemplatedValues + { + return $this->manager->get(InputFilterWithTemplatedValues::class); + } +} From be509ec926fd329f31c0f47fb45221aaf9189025 Mon Sep 17 00:00:00 2001 From: George Steel Date: Tue, 9 Jan 2024 14:30:10 +0000 Subject: [PATCH 4/6] Baseline issues stemming for more precise types in `get()` Signed-off-by: George Steel --- psalm-baseline.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index f9474070..0d4b369a 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -278,6 +278,15 @@ InputFilterPluginManager InputFilterPluginManager + + get + + + ? T1 : ($name is class-string ? T2 : T1|T2))]]> + + + parent::get($name, $options) + $factories From 68627d4cb18e63a57f26688cc92b659fedb36b6d Mon Sep 17 00:00:00 2001 From: George Steel Date: Tue, 9 Jan 2024 16:43:10 +0000 Subject: [PATCH 5/6] Simplify templates for `get()` Signed-off-by: George Steel --- psalm-baseline.xml | 2 +- src/InputFilterPluginManager.php | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 0d4b369a..e7036ac2 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -282,7 +282,7 @@ get - ? T1 : ($name is class-string ? T2 : T1|T2))]]> + ? T : InputInterface|InputFilterInterface)]]> parent::get($name, $options) diff --git a/src/InputFilterPluginManager.php b/src/InputFilterPluginManager.php index 92a32328..4e5101e4 100644 --- a/src/InputFilterPluginManager.php +++ b/src/InputFilterPluginManager.php @@ -190,11 +190,9 @@ public function validatePlugin($plugin) /** * @inheritDoc - * @template T1 of InputInterface - * @template T2 of InputFilterInterface - * @param class-string|class-string|string $name - * @return T1|T2 - * @psalm-return ($name is class-string ? T1 : ($name is class-string ? T2 : T1|T2)) + * @template T of InputInterface|InputFilterInterface + * @param class-string|string $name + * @return ($name is class-string ? T : InputInterface|InputFilterInterface) */ public function get($name, ?array $options = null) { From 765bebb3b45c6f52dff72581f2bcc43b32c4af23 Mon Sep 17 00:00:00 2001 From: George Steel Date: Wed, 10 Jan 2024 11:16:11 +0000 Subject: [PATCH 6/6] Refine types for `InputFilterPluginManager::get` and add additional SA test The return type union of `InputInterface|InputFilterInterface` is problematic in Psalm, so it has been "simplified" into 2 templates and a nested return conditional to ensure the expected return type in each of the 3 expected input types. Template constraints and Unions do not appear to work when used in a parameterised class-string, so `class-string` is problematic as is `class-string`, as is `class-string` where `T` = `FQCNa|FQCNb` Signed-off-by: George Steel --- psalm-baseline.xml | 2 +- psalm.xml.dist | 5 +++++ src/InputFilterPluginManager.php | 9 ++++++--- test/StaticAnalysis/InputFilterPluginManagerType.php | 5 +++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index e7036ac2..a8d27fd5 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -282,7 +282,7 @@ get - ? T : InputInterface|InputFilterInterface)]]> + ? T1 : ($name is class-string ? T2 : InputInterface|InputFilterInterface))]]> parent::get($name, $options) diff --git a/psalm.xml.dist b/psalm.xml.dist index f495a985..22355666 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -28,6 +28,11 @@ + + + + + diff --git a/src/InputFilterPluginManager.php b/src/InputFilterPluginManager.php index 4e5101e4..d7b38d20 100644 --- a/src/InputFilterPluginManager.php +++ b/src/InputFilterPluginManager.php @@ -190,9 +190,12 @@ public function validatePlugin($plugin) /** * @inheritDoc - * @template T of InputInterface|InputFilterInterface - * @param class-string|string $name - * @return ($name is class-string ? T : InputInterface|InputFilterInterface) + * phpcs:disable Generic.Files.LineLength.TooLong + * // Template constraint required or we get mixed added to output. Two templates because union does not work + * @template T1 of InputInterface + * @template T2 of InputFilterInterface + * @param class-string|class-string|string $name + * @return ($name is class-string ? T1 : ($name is class-string ? T2 : InputInterface|InputFilterInterface)) */ public function get($name, ?array $options = null) { diff --git a/test/StaticAnalysis/InputFilterPluginManagerType.php b/test/StaticAnalysis/InputFilterPluginManagerType.php index 5c9dd2c6..30c9476b 100644 --- a/test/StaticAnalysis/InputFilterPluginManagerType.php +++ b/test/StaticAnalysis/InputFilterPluginManagerType.php @@ -24,4 +24,9 @@ public function getWithFQCNWillReturnTheObjectOfType(): InputFilterWithTemplated { return $this->manager->get(InputFilterWithTemplatedValues::class); } + + public function getInvalidFQCNReturnsFallbackType(): InputInterface|InputFilterInterface + { + return $this->manager->get(self::class); + } }