From 8e6357f8708d1868938b654a54799966718fcd3c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 27 Feb 2023 19:11:47 +0100 Subject: [PATCH] Resolver::autowireArguments() fixed support for variadics The variadics argument cannot be used if a previous argument has been omitted. --- src/DI/Resolver.php | 32 ++++++------------- .../DI/Resolver.autowireArguments.errors.phpt | 26 ++++++--------- tests/DI/Resolver.autowireArguments.phpt | 22 ++++--------- 3 files changed, 25 insertions(+), 55 deletions(-) diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index d687a8033..7ccfa36ed 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -14,6 +14,7 @@ use Nette\DI\Definitions\Reference; use Nette\DI\Definitions\Statement; use Nette\PhpGenerator\Helpers as PhpHelpers; +use Nette\Utils\Arrays; use Nette\Utils\Callback; use Nette\Utils\Reflection; use Nette\Utils\Strings; @@ -254,7 +255,7 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo switch (true) { case $entity[0] === '': // function call - if (!Nette\Utils\Arrays::isList($arguments)) { + if (!Arrays::isList($arguments)) { throw new ServiceCreationException(sprintf( 'Unable to pass specified arguments to %s.', $entity[0] @@ -294,7 +295,7 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo $arguments = self::autowireArguments($rm, $arguments, $getter); $this->addDependency($rm); - } elseif (!Nette\Utils\Arrays::isList($arguments)) { + } elseif (!Arrays::isList($arguments)) { throw new ServiceCreationException(sprintf('Unable to pass specified arguments to %s::%s().', $type, $entity[1])); } } @@ -542,28 +543,15 @@ public static function autowireArguments( $paramName = $param->name; if ($param->isVariadic()) { - if (array_key_exists($paramName, $arguments)) { - if (!is_array($arguments[$paramName])) { - throw new ServiceCreationException(sprintf( - 'Parameter %s must be array, %s given.', - Reflection::toString($param), - gettype($arguments[$paramName]) - )); - } - - $variadics = $arguments[$paramName]; - unset($arguments[$paramName]); - } else { - $variadics = array_merge($arguments); - $arguments = []; - } - - if ($useName) { - $res[$paramName] = $variadics; - } else { - $res = array_merge($res, $variadics); + if ($useName && Arrays::some($arguments, function ($val, $key) { return is_int($key); })) { + throw new ServiceCreationException(sprintf( + 'Cannot use positional argument after named or omitted argument in %s.', + Reflection::toString($param) + )); } + $res = array_merge($res, $arguments); + $arguments = []; $optCount = 0; break; diff --git a/tests/DI/Resolver.autowireArguments.errors.phpt b/tests/DI/Resolver.autowireArguments.errors.phpt index 11c748bd3..89d4295a7 100644 --- a/tests/DI/Resolver.autowireArguments.errors.phpt +++ b/tests/DI/Resolver.autowireArguments.errors.phpt @@ -53,21 +53,13 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, 'Parameter $x in {closure}%a?% has no class type or default value, so its value must be specified.'); -// bad variadics (this is actually what PHP allows) -Assert::exception(function () { - Resolver::autowireArguments( - new ReflectionFunction(function (...$args) {}), - [1, 'args' => []], - function () {} - ); -}, Nette\DI\ServiceCreationException::class, 'Unable to pass specified arguments to {closure}%a?%.'); - - // bad variadics -Assert::exception(function () { - Resolver::autowireArguments( - new ReflectionFunction(function (...$args) {}), - ['args' => [], 1], - function () {} - ); -}, Nette\DI\ServiceCreationException::class, 'Unable to pass specified arguments to {closure}%a?%.'); +if (PHP_VERSION_ID >= 80000) { + Assert::exception(function () { + Resolver::autowireArguments( + new ReflectionFunction(function ($a = 1, ...$args) {}), + [1 => 'new1', 2 => 'new2'], + function () {} + ); + }, Nette\DI\ServiceCreationException::class, 'Cannot use positional argument after named or omitted argument in $args in {closure}%a?%.'); +} diff --git a/tests/DI/Resolver.autowireArguments.phpt b/tests/DI/Resolver.autowireArguments.phpt index 9735a165c..f674a7e14 100644 --- a/tests/DI/Resolver.autowireArguments.phpt +++ b/tests/DI/Resolver.autowireArguments.phpt @@ -98,27 +98,17 @@ Assert::equal( ) ); -// optional arguments + variadics +// optional arguments + named variadics Assert::equal( - PHP_VERSION_ID < 80000 ? [1, 'new1', 'new2'] : ['args' => ['new1', 'new2']], + PHP_VERSION_ID < 80000 ? [1, 'k1' => 'new1', 'k2' => 'new2'] : ['k1' => 'new1', 'k2' => 'new2'], Resolver::autowireArguments( new ReflectionFunction(function ($a = 1, ...$args) {}), - [1 => 'new1', 2 => 'new2'], + ['k1' => 'new1', 'k2' => 'new2'], function () {} ) ); -// optional arguments + variadics -Assert::equal( - ['new', 'new1', 'new2'], - Resolver::autowireArguments( - new ReflectionFunction(function ($a = 1, ...$args) {}), - ['a' => 'new', 1 => 'new1', 2 => 'new2'], - function () {} - ) -); - -// variadics as items +// variadics Assert::equal( [1, 2, 3], Resolver::autowireArguments( @@ -128,9 +118,9 @@ Assert::equal( ) ); -// variadics as array +// name of variadics is ignored Assert::equal( - [1, 2, 3], + ['args' => [1, 2, 3]], Resolver::autowireArguments( new ReflectionFunction(function (...$args) {}), ['args' => [1, 2, 3]],