From 82cb2eee7dc8991995ea3e881ebe2e81193902a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Hub=C3=ADk?= Date: Mon, 12 Oct 2026 13:39:15 +0200 Subject: [PATCH] Keep getValues() extension for nette/forms 2.x PHPDoc ArrayHash|array nette/forms 2.3 and 2.4 describe the return type of Container::getValues() as @return Nette\Utils\ArrayHash|array, which is not more precise than what the extension infers either. Since 2.0.13 the extension was skipped for it and every getValues() call was typed as array|ArrayHash, including getValues(true). Claude-Session: https://claude.ai/code/session_01FBu2xVRvxJ4rGqBf4Mjrhy --- ...tainerValuesDynamicReturnTypeExtension.php | 14 +++-- ...erValuesDynamicReturnTypeExtensionTest.php | 1 + .../FormContainerArrayHashOrArrayValues.php | 53 +++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 tests/Type/Nette/data/FormContainerArrayHashOrArrayValues.php diff --git a/src/Type/Nette/FormContainerValuesDynamicReturnTypeExtension.php b/src/Type/Nette/FormContainerValuesDynamicReturnTypeExtension.php index 10501d4..a62b2af 100644 --- a/src/Type/Nette/FormContainerValuesDynamicReturnTypeExtension.php +++ b/src/Type/Nette/FormContainerValuesDynamicReturnTypeExtension.php @@ -45,10 +45,18 @@ public function isMethodSupported(MethodReflection $methodReflection): bool } // nette/forms 3.2.9+ and methods overriding it can describe a more precise return type in PHPDoc, - // older versions state just object|array - return $returnTag->getType()->isSuperTypeOf( + // older versions state just object|array (3.0 - 3.2.8) or ArrayHash|array (2.x) + $returnType = $returnTag->getType(); + if ($returnType->isSuperTypeOf( TypeCombinator::union(new ArrayType(new MixedType(), new MixedType()), new ObjectWithoutClassType()), - )->yes(); + )->yes()) { + return true; + } + + $arrayHashOrArray = TypeCombinator::union(new ArrayType(new MixedType(), new MixedType()), new ObjectType('Nette\Utils\ArrayHash')); + + return $returnType->isSuperTypeOf($arrayHashOrArray)->yes() + && $arrayHashOrArray->isSuperTypeOf($returnType)->yes(); } public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type diff --git a/tests/Type/Nette/FormContainerValuesDynamicReturnTypeExtensionTest.php b/tests/Type/Nette/FormContainerValuesDynamicReturnTypeExtensionTest.php index 778295b..57becf2 100644 --- a/tests/Type/Nette/FormContainerValuesDynamicReturnTypeExtensionTest.php +++ b/tests/Type/Nette/FormContainerValuesDynamicReturnTypeExtensionTest.php @@ -14,6 +14,7 @@ final class FormContainerValuesDynamicReturnTypeExtensionTest extends TypeInfere public static function dataFileAsserts(): iterable { yield from self::gatherAssertTypes(__DIR__ . '/data/FormContainerOverriddenValues.php'); + yield from self::gatherAssertTypes(__DIR__ . '/data/FormContainerArrayHashOrArrayValues.php'); try { $formsVersion = class_exists(InstalledVersions::class) diff --git a/tests/Type/Nette/data/FormContainerArrayHashOrArrayValues.php b/tests/Type/Nette/data/FormContainerArrayHashOrArrayValues.php new file mode 100644 index 0000000..0a9e24b --- /dev/null +++ b/tests/Type/Nette/data/FormContainerArrayHashOrArrayValues.php @@ -0,0 +1,53 @@ + + */ + public function getValues($returnType = null, ?array $controls = null) + { + return parent::getValues($returnType, $controls); + } + +} + +function (FormWithArrayHashOrArrayValues $form): void { + assertType('array', $form->getValues(true)); + assertType('array', $form->getValues('array')); + assertType(ArrayHash::class, $form->getValues()); + assertType(ArrayHash::class, $form->getValues(null)); +}; + +function (FormWithNarrowerValues $form): void { + assertType('array|' . CustomValues::class, $form->getValues(true)); + assertType('array|' . CustomValues::class, $form->getValues()); +};