From ae83d84c182dc006a68d1cf677dc1f49eaf46f16 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 2 Nov 2021 13:47:16 +0100 Subject: [PATCH 01/18] Resolver::autowireArguments() considers variadics as extra params + updated comment --- src/DI/Resolver.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index 023dea40c..0677d20a4 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -517,8 +517,13 @@ public static function autowireArguments( $res = []; foreach ($method->getParameters() as $num => $param) { + if ($param->isVariadic()) { + $num--; + break; + } + $paramName = $param->name; - if (!$param->isVariadic() && array_key_exists($paramName, $arguments)) { + if (array_key_exists($paramName, $arguments)) { $res[$num] = $arguments[$paramName]; unset($arguments[$paramName], $arguments[$num]); @@ -530,8 +535,8 @@ public static function autowireArguments( $res[$num] = self::autowireArgument($param, $getter); } - $optCount = $param->isOptional() && $res[$num] === ($param->isDefaultValueAvailable() ? Reflection::getParameterDefaultValue($param) : null) - ? $optCount + 1 + $optCount += $param->isOptional() && $res[$num] === ($param->isDefaultValueAvailable() ? Reflection::getParameterDefaultValue($param) : null) + ? 1 : 0; } @@ -611,8 +616,10 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab || $parameter->isOptional() || $parameter->isDefaultValueAvailable() ) { - // !optional + defaultAvailable = func($a = null, $b) since 5.4.7 - // optional + !defaultAvailable = i.e. Exception::__construct, mysqli::mysqli, ... + // !optional + defaultAvailable, !optional + !defaultAvailable since 8.1.0 = func($a = null, $b) + // optional + !defaultAvailable, optional + defaultAvailable since 8.0.0 = i.e. Exception::__construct, mysqli::mysqli, ... + // optional + !defaultAvailable = variadics + // in other cases the optional and defaultAvailable are identical return $parameter->isDefaultValueAvailable() ? Reflection::getParameterDefaultValue($parameter) : null; From 894218dd2b45399e6a59e44a709147794d28622a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 31 Aug 2021 11:19:10 +0200 Subject: [PATCH 02/18] opened 3.1-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 776373fc0..094d1a4ff 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } } } From 8cdb15c74f59aab383a6d6e4921fc0a24e337ac6 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 31 Aug 2021 11:20:03 +0200 Subject: [PATCH 03/18] requires PHP 7.2 --- .github/workflows/coding-style.yml | 2 +- .github/workflows/tests.yml | 4 ++-- composer.json | 2 +- readme.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml index 337a0a631..40437ada3 100644 --- a/.github/workflows/coding-style.yml +++ b/.github/workflows/coding-style.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: 7.1 + php-version: 7.2 coverage: none - run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index de8c8e95e..266902db6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1'] + php: ['7.2', '7.3', '7.4', '8.0', '8.1'] fail-fast: false @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: 7.1 + php-version: 7.2 coverage: none - run: composer update --no-progress --prefer-dist --prefer-lowest --prefer-stable diff --git a/composer.json b/composer.json index 094d1a4ff..c8a1b4566 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": ">=7.1 <8.2", + "php": ">=7.2 <8.2", "ext-tokenizer": "*", "nette/neon": "^3.3", "nette/php-generator": "^3.3.3", diff --git a/readme.md b/readme.md index 642cfc2fd..d68757b83 100644 --- a/readme.md +++ b/readme.md @@ -37,7 +37,7 @@ The recommended way to install is via Composer: composer require nette/di ``` -It requires PHP version 7.1 and supports PHP up to 8.1. +It requires PHP version 7.2 and supports PHP up to 8.1. Usage From 225f792e9333820bf52c0089695c591a6427490f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 13 Sep 2021 12:33:45 +0200 Subject: [PATCH 04/18] uses nette/utils 3.2 --- composer.json | 2 +- src/DI/Resolver.php | 2 +- tests/DI/Resolver.autowireArguments.errors.phpt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index c8a1b4566..ffa92426b 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "nette/php-generator": "^3.3.3", "nette/robot-loader": "^3.2", "nette/schema": "^1.1", - "nette/utils": "^3.1.6" + "nette/utils": "^3.2.5" }, "require-dev": { "nette/tester": "^2.2", diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index 0677d20a4..26a5895c4 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -571,7 +571,7 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab $desc = Reflection::toString($parameter); $type = Nette\Utils\Type::fromReflection($parameter); - if ($parameter->getType() instanceof \ReflectionIntersectionType) { + if ($type && $type->isIntersection()) { throw new ServiceCreationException(sprintf( 'Parameter %s has intersection type, so its value must be specified.', $desc diff --git a/tests/DI/Resolver.autowireArguments.errors.phpt b/tests/DI/Resolver.autowireArguments.errors.phpt index 3e4a732da..fdb70ecd6 100644 --- a/tests/DI/Resolver.autowireArguments.errors.phpt +++ b/tests/DI/Resolver.autowireArguments.errors.phpt @@ -35,4 +35,4 @@ Assert::exception(function () { Assert::exception(function () { Resolver::autowireArguments(new ReflectionFunction(function (...$args) {}), ['args' => []], function () {}); -}, Nette\DI\ServiceCreationException::class, 'Unable to pass specified arguments to {closure}%a?%.'); +}, Nette\DI\ServiceCreationException::class, 'Unable to pass specified arguments to {closure}().'); From 004a07ba3b7ea08fc5c09141832fd9763dc42f9c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 12 Jan 2021 16:06:00 +0100 Subject: [PATCH 05/18] removed deprecated stuff --- src/DI/Compiler.php | 14 ---- src/DI/Config/DefinitionSchema.php | 20 +++--- src/DI/Config/Helpers.php | 9 +-- src/DI/ContainerBuilder.php | 8 --- src/DI/Definitions/FactoryDefinition.php | 75 --------------------- src/DI/Definitions/ServiceDefinition.php | 51 -------------- src/DI/Extensions/DIExtension.php | 11 --- tests/DI/DIExtension.run.phpt | 32 --------- tests/DI/Definitions.ServiceDefinition.phpt | 23 ------- 9 files changed, 12 insertions(+), 231 deletions(-) delete mode 100644 tests/DI/DIExtension.run.phpt diff --git a/src/DI/Compiler.php b/src/DI/Compiler.php index f55cabe3a..b9fed1339 100644 --- a/src/DI/Compiler.php +++ b/src/DI/Compiler.php @@ -324,18 +324,4 @@ protected function createPhpGenerator(): PhpGenerator { return new PhpGenerator($this->builder); } - - - /** @deprecated use non-static Compiler::loadDefinitionsFromConfig() */ - public static function loadDefinitions(): void - { - throw new Nette\DeprecatedException(__METHOD__ . '() is deprecated, use non-static Compiler::loadDefinitionsFromConfig(array $configList).'); - } - - - /** @deprecated use non-static Compiler::loadDefinitionsFromConfig() */ - public static function loadDefinition(): void - { - throw new Nette\DeprecatedException(__METHOD__ . '() is deprecated, use non-static Compiler::loadDefinitionsFromConfig(array $configList).'); - } } diff --git a/src/DI/Config/DefinitionSchema.php b/src/DI/Config/DefinitionSchema.php index 05fea6877..1655571c3 100644 --- a/src/DI/Config/DefinitionSchema.php +++ b/src/DI/Config/DefinitionSchema.php @@ -96,16 +96,16 @@ public function normalize($def, Context $context) $def['factory'] = $def['create']; unset($def['create']); } - if (isset($def['class']) && !isset($def['type'])) { - if ($def['class'] instanceof Statement) { - $key = end($context->path); - trigger_error(sprintf("Service '%s': option 'class' should be changed to 'factory'.", $key), E_USER_DEPRECATED); - $def['factory'] = $def['class']; - unset($def['class']); - } elseif (!isset($def['factory']) && !isset($def['dynamic']) && !isset($def['imported'])) { - $def['factory'] = $def['class']; - unset($def['class']); - } + // back compatibility + if ( + isset($def['class']) + && !isset($def['type']) + && !isset($def['factory']) + && !isset($def['dynamic']) + && !isset($def['imported']) + ) { + $def['factory'] = $def['class']; + unset($def['class']); } foreach (['class' => 'type', 'dynamic' => 'imported'] as $alias => $original) { diff --git a/src/DI/Config/Helpers.php b/src/DI/Config/Helpers.php index ec1528482..273c71be6 100644 --- a/src/DI/Config/Helpers.php +++ b/src/DI/Config/Helpers.php @@ -23,19 +23,14 @@ final class Helpers public const PREVENT_MERGING = '_prevent_merging'; - /** - * Merges configurations. Left has higher priority than right one. - * @return array|string - */ + /** @deprecated */ public static function merge($left, $right) { return Nette\Schema\Helpers::merge($left, $right); } - /** - * Return true if array prevents merging and removes this information. - */ + /** @deprecated */ public static function takeParent(&$data): bool { if (is_array($data) && isset($data[self::PREVENT_MERGING])) { diff --git a/src/DI/ContainerBuilder.php b/src/DI/ContainerBuilder.php index 30933c7b1..9ac146f2b 100644 --- a/src/DI/ContainerBuilder.php +++ b/src/DI/ContainerBuilder.php @@ -410,12 +410,4 @@ public function formatPhp(string $statement, array $args): string }); return (new PhpGenerator($this))->formatPhp($statement, $args); } - - - /** @deprecated use resolve() */ - public function prepareClassList(): void - { - trigger_error(__METHOD__ . '() is deprecated, use resolve()', E_USER_DEPRECATED); - $this->resolve(); - } } diff --git a/src/DI/Definitions/FactoryDefinition.php b/src/DI/Definitions/FactoryDefinition.php index 0570b37bc..066222bad 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -87,81 +87,6 @@ public function getResultDefinition(): Definition } - /** - * @deprecated use ->getResultDefinition()->setFactory() - * @return static - */ - public function setFactory($factory, array $args = []) - { - trigger_error(sprintf('Service %s: %s() is deprecated, use ->getResultDefinition()->setFactory()', $this->getName(), __METHOD__), E_USER_DEPRECATED); - $this->resultDefinition->setFactory($factory, $args); - return $this; - } - - - /** @deprecated use ->getResultDefinition()->getFactory() */ - public function getFactory(): ?Statement - { - trigger_error(sprintf('Service %s: %s() is deprecated, use ->getResultDefinition()->getFactory()', $this->getName(), __METHOD__), E_USER_DEPRECATED); - return $this->resultDefinition->getFactory(); - } - - - /** - * @deprecated use ->getResultDefinition()->getEntity() - * @return mixed - */ - public function getEntity() - { - trigger_error(sprintf('Service %s: %s() is deprecated, use ->getResultDefinition()->getEntity()', $this->getName(), __METHOD__), E_USER_DEPRECATED); - return $this->resultDefinition->getEntity(); - } - - - /** - * @deprecated use ->getResultDefinition()->setArguments() - * @return static - */ - public function setArguments(array $args = []) - { - trigger_error(sprintf('Service %s: %s() is deprecated, use ->getResultDefinition()->setArguments()', $this->getName(), __METHOD__), E_USER_DEPRECATED); - $this->resultDefinition->setArguments($args); - return $this; - } - - - /** - * @deprecated use ->getResultDefinition()->setSetup() - * @return static - */ - public function setSetup(array $setup) - { - trigger_error(sprintf('Service %s: %s() is deprecated, use ->getResultDefinition()->setSetup()', $this->getName(), __METHOD__), E_USER_DEPRECATED); - $this->resultDefinition->setSetup($setup); - return $this; - } - - - /** @deprecated use ->getResultDefinition()->getSetup() */ - public function getSetup(): array - { - trigger_error(sprintf('Service %s: %s() is deprecated, use ->getResultDefinition()->getSetup()', $this->getName(), __METHOD__), E_USER_DEPRECATED); - return $this->resultDefinition->getSetup(); - } - - - /** - * @deprecated use ->getResultDefinition()->addSetup() - * @return static - */ - public function addSetup($entity, array $args = []) - { - trigger_error(sprintf('Service %s: %s() is deprecated, use ->getResultDefinition()->addSetup()', $this->getName(), __METHOD__), E_USER_DEPRECATED); - $this->resultDefinition->addSetup($entity, $args); - return $this; - } - - /** @return static */ public function setParameters(array $params) { diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 6b2ba6de6..10db1f0fb 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -35,20 +35,6 @@ public function __construct() } - /** @deprecated Use setType() */ - public function setClass(?string $type) - { - $this->setType($type); - if (func_num_args() > 1) { - trigger_error(sprintf('Service %s: %s() second parameter $args is deprecated, use setFactory()', $this->getName(), __METHOD__), E_USER_DEPRECATED); - if ($args = func_get_arg(1)) { - $this->setFactory($type, $args); - } - } - return $this; - } - - /** @return static */ public function setType(?string $type) { @@ -134,43 +120,6 @@ public function addSetup($entity, array $args = []) } - /** @deprecated */ - public function setParameters(array $params) - { - throw new Nette\DeprecatedException(sprintf('Service %s: %s() is deprecated.', $this->getName(), __METHOD__)); - } - - - /** @deprecated */ - public function getParameters(): array - { - trigger_error(sprintf('Service %s: %s() is deprecated.', $this->getName(), __METHOD__), E_USER_DEPRECATED); - return []; - } - - - /** @deprecated use $builder->addImportedDefinition(...) */ - public function setDynamic(): void - { - throw new Nette\DeprecatedException(sprintf('Service %s: %s() is deprecated, use $builder->addImportedDefinition(...)', $this->getName(), __METHOD__)); - } - - - /** @deprecated use $builder->addFactoryDefinition(...) or addAccessorDefinition(...) */ - public function setImplement(): void - { - throw new Nette\DeprecatedException(sprintf('Service %s: %s() is deprecated, use $builder->addFactoryDefinition(...)', $this->getName(), __METHOD__)); - } - - - /** @deprecated use addTag('nette.inject') */ - public function setInject(bool $state = true) - { - trigger_error(sprintf('Service %s: %s() is deprecated, use addTag(Nette\DI\Extensions\InjectExtension::TAG_INJECT)', $this->getName(), __METHOD__), E_USER_DEPRECATED); - return $this->addTag(Nette\DI\Extensions\InjectExtension::TAG_INJECT, $state); - } - - public function resolveType(Nette\DI\Resolver $resolver): void { if (!$this->getEntity()) { diff --git a/src/DI/Extensions/DIExtension.php b/src/DI/Extensions/DIExtension.php index 28d5bd7f2..3b3fc7a78 100644 --- a/src/DI/Extensions/DIExtension.php +++ b/src/DI/Extensions/DIExtension.php @@ -91,8 +91,6 @@ public function afterCompile(Nette\PhpGenerator\ClassType $class) ) { $this->enableTracyIntegration(); } - - $this->initializeTaggedServices(); } @@ -122,15 +120,6 @@ private function restrictTypes(Nette\PhpGenerator\ClassType $class): void } - private function initializeTaggedServices(): void - { - foreach (array_filter($this->getContainerBuilder()->findByTag('run')) as $name => $on) { - trigger_error("Tag 'run' used in service '$name' definition is deprecated.", E_USER_DEPRECATED); - $this->initialization->addBody('$this->getService(?);', [$name]); - } - } - - private function enableTracyIntegration(): void { Nette\Bridges\DITracy\ContainerPanel::$compilationTime = $this->time; diff --git a/tests/DI/DIExtension.run.phpt b/tests/DI/DIExtension.run.phpt deleted file mode 100644 index 27526245d..000000000 --- a/tests/DI/DIExtension.run.phpt +++ /dev/null @@ -1,32 +0,0 @@ -addExtension('di', new DIExtension); -$loader = new DI\Config\Loader; -$config = $loader->load(Tester\FileMock::create(' -services: - std: {factory: stdClass, tags: [run]} - - {factory: stdClass, tags: [run]} -', 'neon')); - -@eval($compiler->addConfig($config)->setClassName(Container1::class)->compile()); // @ tag is deprecated - -$container = new Container1; -Assert::false($container->isCreated('std')); - -$container->initialize(); -Assert::true($container->isCreated('std')); diff --git a/tests/DI/Definitions.ServiceDefinition.phpt b/tests/DI/Definitions.ServiceDefinition.phpt index 05e98f561..a458f38ff 100644 --- a/tests/DI/Definitions.ServiceDefinition.phpt +++ b/tests/DI/Definitions.ServiceDefinition.phpt @@ -19,11 +19,6 @@ Assert::exception(function () { $def->setType('Foo'); }, Nette\InvalidArgumentException::class, "Service '': Class or interface 'Foo' not found."); -Assert::exception(function () { - $def = new ServiceDefinition; - $def->setImplement('Foo'); -}, Nette\DeprecatedException::class); - test('', function () { $def = new ServiceDefinition; $def->setType(stdClass::class); @@ -35,24 +30,6 @@ test('', function () { Assert::null($def->getEntity()); }); -test('', function () { - $def = new ServiceDefinition; - Assert::error(function () use ($def) { - $def->setClass(stdClass::class, []); - }, E_USER_DEPRECATED); - Assert::same(stdClass::class, $def->getType()); - Assert::null($def->getEntity()); -}); - -test('', function () { - $def = new ServiceDefinition; - Assert::error(function () use ($def) { - $def->setClass(stdClass::class, [1, 2]); - }, E_USER_DEPRECATED); - Assert::same(stdClass::class, $def->getType()); - Assert::equal(new Statement(stdClass::class, [1, 2]), $def->getFactory()); -}); - test('', function () { $def = new ServiceDefinition; $def->setFactory(stdClass::class); From b0f506605addd860f9f66bbe8ab66824e8390b88 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 12 Jan 2021 16:23:16 +0100 Subject: [PATCH 06/18] some deprecated methods triggers notice --- src/DI/Definitions/Definition.php | 2 ++ src/DI/Definitions/ImportedDefinition.php | 1 + 2 files changed, 3 insertions(+) diff --git a/src/DI/Definitions/Definition.php b/src/DI/Definitions/Definition.php index fa88e0931..b858257c6 100644 --- a/src/DI/Definitions/Definition.php +++ b/src/DI/Definitions/Definition.php @@ -194,6 +194,7 @@ public function getClass(): ?string /** @deprecated Use '$def instanceof Nette\DI\Definitions\ImportedDefinition' */ public function isDynamic(): bool { + trigger_error(sprintf('Service %s: %s() is deprecated, use "instanceof ImportedDefinition".', $this->getName(), __METHOD__), E_USER_DEPRECATED); return false; } @@ -201,6 +202,7 @@ public function isDynamic(): bool /** @deprecated Use Nette\DI\Definitions\FactoryDefinition or AccessorDefinition */ public function getImplement(): ?string { + trigger_error(sprintf('Service %s: %s() is deprecated.', $this->getName(), __METHOD__), E_USER_DEPRECATED); return null; } diff --git a/src/DI/Definitions/ImportedDefinition.php b/src/DI/Definitions/ImportedDefinition.php index e03c245b7..2002f6f71 100644 --- a/src/DI/Definitions/ImportedDefinition.php +++ b/src/DI/Definitions/ImportedDefinition.php @@ -48,6 +48,7 @@ public function generateMethod(Nette\PhpGenerator\Method $method, PhpGenerator $ /** @deprecated use '$def instanceof ImportedDefinition' */ public function isDynamic(): bool { + trigger_error(sprintf('Service %s: %s() is deprecated, use "instanceof ImportedDefinition".', $this->getName(), __METHOD__), E_USER_DEPRECATED); return true; } } From 3f636f85705697d370a32bfe0ebec07e6b8a88fc Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 31 Dec 2020 20:04:30 +0100 Subject: [PATCH 07/18] PhpExtension & ConstantsExtension are deprecated (have been moved to nette/bootstrap) --- src/DI/Extensions/ConstantsExtension.php | 2 ++ src/DI/Extensions/PhpExtension.php | 2 ++ tests/DI/ConstantsExtension.basic.phpt | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/DI/Extensions/ConstantsExtension.php b/src/DI/Extensions/ConstantsExtension.php index ab94e8766..3d34fb5f3 100644 --- a/src/DI/Extensions/ConstantsExtension.php +++ b/src/DI/Extensions/ConstantsExtension.php @@ -14,11 +14,13 @@ /** * Constant definitions. + * @deprecated use Nette\Bootstrap\Extensions\ConstantsExtension */ final class ConstantsExtension extends Nette\DI\CompilerExtension { public function loadConfiguration() { + trigger_error(self::class . ' is deprecated, use Nette\Bootstrap\Extensions\ConstantsExtension.', E_USER_DEPRECATED); foreach ($this->getConfig() as $name => $value) { $this->initialization->addBody('define(?, ?);', [$name, $value]); } diff --git a/src/DI/Extensions/PhpExtension.php b/src/DI/Extensions/PhpExtension.php index 6a469e32f..117bdfe84 100644 --- a/src/DI/Extensions/PhpExtension.php +++ b/src/DI/Extensions/PhpExtension.php @@ -14,6 +14,7 @@ /** * PHP directives definition. + * @deprecated use Nette\Bootstrap\Extensions\PhpExtension */ final class PhpExtension extends Nette\DI\CompilerExtension { @@ -25,6 +26,7 @@ public function getConfigSchema(): Nette\Schema\Schema public function loadConfiguration() { + trigger_error(self::class . ' is deprecated, use Nette\Bootstrap\Extensions\PhpExtension.', E_USER_DEPRECATED); foreach ($this->getConfig() as $name => $value) { if ($value === null) { continue; diff --git a/tests/DI/ConstantsExtension.basic.phpt b/tests/DI/ConstantsExtension.basic.phpt index 421e49c2c..925c641d8 100644 --- a/tests/DI/ConstantsExtension.basic.phpt +++ b/tests/DI/ConstantsExtension.basic.phpt @@ -15,7 +15,7 @@ require __DIR__ . '/../bootstrap.php'; $compiler = new DI\Compiler; $compiler->addExtension('constants', new Nette\DI\Extensions\ConstantsExtension); -$container = createContainer($compiler, ' +$container = @createContainer($compiler, ' constants: a: hello A: WORLD From bfe90df74ff2a6f2e610cbf0a0322cccc4b33f9c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 10 Sep 2021 21:16:04 +0200 Subject: [PATCH 08/18] NeonAdapter: operator ? throws exception --- src/DI/Config/Adapters/NeonAdapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DI/Config/Adapters/NeonAdapter.php b/src/DI/Config/Adapters/NeonAdapter.php index a6bc96998..6886d8e98 100644 --- a/src/DI/Config/Adapters/NeonAdapter.php +++ b/src/DI/Config/Adapters/NeonAdapter.php @@ -75,7 +75,7 @@ public function process(array $arr): array } else { $tmp = $this->process([$val->value]); if (is_string($tmp[0]) && strpos($tmp[0], '?') !== false) { - trigger_error('Operator ? is deprecated in config files.', E_USER_DEPRECATED); + throw new Nette\DI\InvalidConfigurationException('Operator ? is deprecated in config file.'); } $val = new Statement($tmp[0], $this->process($val->attributes)); } From c4d9118142085fb76b4d9a3ff580ea054a5fde70 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 26 Sep 2021 23:08:35 +0200 Subject: [PATCH 09/18] generated factories should have defined return type, otherwise triggers warning --- src/DI/Definitions/AccessorDefinition.php | 5 +++ src/DI/Definitions/FactoryDefinition.php | 5 +++ src/DI/Definitions/LocatorDefinition.php | 7 +++ src/DI/Helpers.php | 6 +-- .../Compiler.extension.defaultServices.phpt | 8 +--- tests/DI/Compiler.generatedAccessor.phpt | 3 +- tests/DI/Compiler.generatedFactory.phpt | 45 ++++++------------- ...ler.generatedFactory.scalarParameters.phpt | 3 +- tests/DI/ContainerBuilder.accessor.phpt | 6 +-- tests/DI/ContainerBuilder.aliases.phpt | 8 ++-- tests/DI/ContainerBuilder.aliases2.phpt | 4 +- ...ContainerBuilder.autowiring.implement.phpt | 3 +- .../ContainerBuilder.factory.arguments.phpt | 3 +- tests/DI/ContainerBuilder.factory.error.phpt | 4 +- tests/DI/ContainerBuilder.factory.phpt | 6 +-- ...ContainerBuilder.factory.resolveClass.phpt | 40 +++-------------- ...DecoratorExtension.factories.accessor.phpt | 5 +-- tests/DI/DecoratorExtension.factories.phpt | 5 +-- .../Definitions.AccessorDefinition.api.phpt | 4 +- ...efinitions.AccessorDefinition.resolve.phpt | 14 +++--- .../DI/Definitions.FactoryDefinition.api.phpt | 4 +- ...Definitions.FactoryDefinition.resolve.phpt | 16 +++---- .../DI/Definitions.LocatorDefinition.api.phpt | 20 ++++----- 23 files changed, 91 insertions(+), 133 deletions(-) diff --git a/src/DI/Definitions/AccessorDefinition.php b/src/DI/Definitions/AccessorDefinition.php index 013497112..0c2268858 100644 --- a/src/DI/Definitions/AccessorDefinition.php +++ b/src/DI/Definitions/AccessorDefinition.php @@ -56,6 +56,11 @@ public function setImplement(string $interface) $interface )); } + try { + Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()"); + } catch (Nette\DI\ServiceCreationException $e) { + trigger_error($e->getMessage(), E_USER_DEPRECATED); + } return parent::setType($interface); } diff --git a/src/DI/Definitions/FactoryDefinition.php b/src/DI/Definitions/FactoryDefinition.php index 066222bad..56f43ff2e 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -56,6 +56,11 @@ public function setImplement(string $interface) $interface )); } + try { + Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()"); + } catch (Nette\DI\ServiceCreationException $e) { + trigger_error($e->getMessage(), E_USER_DEPRECATED); + } return parent::setType($interface); } diff --git a/src/DI/Definitions/LocatorDefinition.php b/src/DI/Definitions/LocatorDefinition.php index c34bca543..0b3517cce 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -47,6 +47,13 @@ public function setImplement(string $interface) $method->name )); } + if ($method->getNumberOfParameters() === 0) { + try { + Nette\DI\Helpers::ensureClassType(Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", true); + } catch (Nette\DI\ServiceCreationException $e) { + trigger_error($e->getMessage(), E_USER_DEPRECATED); + } + } } return parent::setType($interface); } diff --git a/src/DI/Helpers.php b/src/DI/Helpers.php index 5ff7db222..8e54db170 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -216,12 +216,12 @@ public static function getReturnTypeAnnotation(\ReflectionFunctionAbstract $func } - public static function ensureClassType(?Type $type, string $hint): string + public static function ensureClassType(?Type $type, string $hint, bool $allowNullable = false): string { if (!$type) { throw new ServiceCreationException(sprintf('%s is not declared.', ucfirst($hint))); - } elseif (!$type->isClass() || $type->isUnion()) { - throw new ServiceCreationException(sprintf("%s is not expected to be nullable/union/intersection/built-in, '%s' given.", ucfirst($hint), $type)); + } elseif (!$type->isClass() || (!$allowNullable && $type->isUnion())) { + throw new ServiceCreationException(sprintf("%s is not expected to be %sunion/intersection/built-in, '%s' given.", ucfirst($hint), $allowNullable ? '' : 'nullable/', $type)); } $class = $type->getSingleName(); if (!class_exists($class) && !interface_exists($class)) { diff --git a/tests/DI/Compiler.extension.defaultServices.phpt b/tests/DI/Compiler.extension.defaultServices.phpt index fa4f981d4..e780a5241 100644 --- a/tests/DI/Compiler.extension.defaultServices.phpt +++ b/tests/DI/Compiler.extension.defaultServices.phpt @@ -21,8 +21,7 @@ interface IIpsum } interface IIpsumFactory { - /** @return IIpsum */ - public function create(); + public function create(): IIpsum; } interface IFooBar { @@ -46,10 +45,7 @@ class FooBar implements IFooBar class Factory { - /** - * @return Lorem - */ - public static function createLorem() + public static function createLorem(): Lorem { return new Lorem; } diff --git a/tests/DI/Compiler.generatedAccessor.phpt b/tests/DI/Compiler.generatedAccessor.phpt index d6368d004..443770764 100644 --- a/tests/DI/Compiler.generatedAccessor.phpt +++ b/tests/DI/Compiler.generatedAccessor.phpt @@ -19,8 +19,7 @@ class Lorem interface ILoremAccessor { - /** @return Lorem */ - public function get(); + public function get(): Lorem; } diff --git a/tests/DI/Compiler.generatedFactory.phpt b/tests/DI/Compiler.generatedFactory.phpt index 3e2c2f7ad..e90a77027 100644 --- a/tests/DI/Compiler.generatedFactory.phpt +++ b/tests/DI/Compiler.generatedFactory.phpt @@ -16,10 +16,7 @@ require __DIR__ . '/../bootstrap.php'; interface ILoremFactory { - /** - * @return Lorem - */ - public function create(); + public function create(): Lorem; } class Lorem @@ -43,11 +40,7 @@ interface IFinderFactory interface IArticleFactory { - /** - * @param string - * @return Article - */ - public function create($title); + public function create($title): Article; } class Article @@ -98,11 +91,7 @@ class Baz interface IFooFactory { - /** - * @param Baz - * @return Foo - */ - public function create(Baz $baz = null); + public function create(Baz $baz = null): Foo; } class Dolor @@ -121,8 +110,7 @@ class Dolor interface DolorFactory { - /** @return Dolor */ - public function create(Bar $bar = null, $foo); + public function create(Bar $bar = null, $foo): Dolor; } @@ -142,8 +130,7 @@ class TestClass interface ITestClassFactory { - /** @return TestClass */ - public function create($bar); + public function create($bar): TestClass; } class TestExtension extends DI\CompilerExtension @@ -168,7 +155,7 @@ class TestExtension extends DI\CompilerExtension $compiler = new DI\Compiler; $compiler->addExtension('test', new TestExtension); -$container = createContainer($compiler, 'files/compiler.generatedFactory.neon'); +@$container = createContainer($compiler, 'files/compiler.generatedFactory.neon'); // missing type triggers warning Assert::type(ILoremFactory::class, $container->getService('lorem')); @@ -284,15 +271,13 @@ class Bad1 interface Bad2 { - public function create(Baz $bar); + public function create(Baz $bar): Bad1; } Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addFactoryDefinition('one') - ->setImplement(Bad2::class) - ->getResultDefinition() - ->setFactory(Bad1::class); + ->setImplement(Bad2::class); $builder->complete(); }, Nette\InvalidStateException::class, "Service 'one' (type of Bad2): Type of \$bar in Bad2::create() doesn't match type in Bad1 constructor."); @@ -307,15 +292,13 @@ class Bad3 interface Bad4 { - public function create($baz); + public function create($baz): Bad3; } Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addFactoryDefinition('one') - ->setImplement(Bad4::class) - ->getResultDefinition() - ->setFactory(Bad3::class); + ->setImplement(Bad4::class); $builder->complete(); }, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Unused parameter \$baz when implementing method Bad4::create(), did you mean \$bar?"); @@ -330,15 +313,13 @@ class Bad5 interface Bad6 { - public function create($baz); + public function create($baz): Bad5; } Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addFactoryDefinition('one') - ->setImplement(Bad6::class) - ->getResultDefinition() - ->setFactory(Bad5::class); + ->setImplement(Bad6::class); $builder->complete(); }, Nette\InvalidStateException::class, "Service 'one' (type of Bad6): Unused parameter \$baz when implementing method Bad6::create()."); @@ -346,7 +327,7 @@ Assert::exception(function () { interface Bad7 { - public function get(); + public function get(): stdClass; } Assert::exception(function () { diff --git a/tests/DI/Compiler.generatedFactory.scalarParameters.phpt b/tests/DI/Compiler.generatedFactory.scalarParameters.phpt index be92ac892..3cb72ff7f 100644 --- a/tests/DI/Compiler.generatedFactory.scalarParameters.phpt +++ b/tests/DI/Compiler.generatedFactory.scalarParameters.phpt @@ -15,8 +15,7 @@ require __DIR__ . '/../bootstrap.php'; interface IArticleFactory { - /** @return Article */ - public function create(string $title); + public function create(string $title): Article; } class Article diff --git a/tests/DI/ContainerBuilder.accessor.phpt b/tests/DI/ContainerBuilder.accessor.phpt index 940538e26..6333ba56d 100644 --- a/tests/DI/ContainerBuilder.accessor.phpt +++ b/tests/DI/ContainerBuilder.accessor.phpt @@ -15,7 +15,7 @@ require __DIR__ . '/../bootstrap.php'; interface StdClassAccessor { - public function get(); + public function get(): stdClass; } interface AnnotatedAccessor @@ -48,8 +48,8 @@ $builder->addAccessorDefinition('one') ->setImplement(StdClassAccessor::class) ->setReference(stdClass::class); -$builder->addAccessorDefinition('two') - ->setImplement(AnnotatedAccessor::class); +@$builder->addAccessorDefinition('two') + ->setImplement(AnnotatedAccessor::class); // missing type triggers warning $builder->addAccessorDefinition('three') ->setImplement(StdClassAccessor::class) diff --git a/tests/DI/ContainerBuilder.aliases.phpt b/tests/DI/ContainerBuilder.aliases.phpt index 85c7e4e7b..d2bf878b2 100644 --- a/tests/DI/ContainerBuilder.aliases.phpt +++ b/tests/DI/ContainerBuilder.aliases.phpt @@ -19,12 +19,12 @@ class Service interface ServiceFactory { - public function create(); + public function create(): Service; } interface ServiceFactory2 { - public function create(); + public function create(): Service; } $builder = new DI\ContainerBuilder; @@ -39,13 +39,13 @@ $builder->addFactoryDefinition('aliasedFactory') ->setImplement(ServiceFactory::class) ->setAutowired(false) ->getResultDefinition() - ->setFactory('@serviceFactory'); + ->setFactory('@service'); $builder->addFactoryDefinition('aliasedFactoryViaClass') ->setImplement(ServiceFactory::class) ->setAutowired(false) ->getResultDefinition() - ->setFactory('@\ServiceFactory'); + ->setFactory('@\Service'); $builder->addDefinition('aliasedService') ->setFactory('@service'); diff --git a/tests/DI/ContainerBuilder.aliases2.phpt b/tests/DI/ContainerBuilder.aliases2.phpt index 4833db445..b4ed8db61 100644 --- a/tests/DI/ContainerBuilder.aliases2.phpt +++ b/tests/DI/ContainerBuilder.aliases2.phpt @@ -19,12 +19,12 @@ class Service interface ServiceFactory { - public function create(); + public function create(): Service; } interface ServiceFactory2 { - public function create(); + public function create(): Service; } $builder = new DI\ContainerBuilder; diff --git a/tests/DI/ContainerBuilder.autowiring.implement.phpt b/tests/DI/ContainerBuilder.autowiring.implement.phpt index 92c82783f..6c9ac20d9 100644 --- a/tests/DI/ContainerBuilder.autowiring.implement.phpt +++ b/tests/DI/ContainerBuilder.autowiring.implement.phpt @@ -15,8 +15,7 @@ require __DIR__ . '/../bootstrap.php'; interface ITestFactory { - /** @return Test */ - public function create(); + public function create(): Test; } class Foo diff --git a/tests/DI/ContainerBuilder.factory.arguments.phpt b/tests/DI/ContainerBuilder.factory.arguments.phpt index 9af2779e3..8967dd02b 100644 --- a/tests/DI/ContainerBuilder.factory.arguments.phpt +++ b/tests/DI/ContainerBuilder.factory.arguments.phpt @@ -26,8 +26,7 @@ class Foo interface FooFactory { - /** @return Foo */ - public function create(); + public function create(): Foo; } diff --git a/tests/DI/ContainerBuilder.factory.error.phpt b/tests/DI/ContainerBuilder.factory.error.phpt index b4f83c685..008081d81 100644 --- a/tests/DI/ContainerBuilder.factory.error.phpt +++ b/tests/DI/ContainerBuilder.factory.error.phpt @@ -73,8 +73,8 @@ interface Bad4 Assert::exception(function () { $builder = new DI\ContainerBuilder; - $builder->addFactoryDefinition('one') - ->setImplement(Bad4::class); + @$builder->addFactoryDefinition('one') + ->setImplement(Bad4::class); // missing type triggers warning $builder->complete(); }, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Return type of Bad4::create() is not declared."); diff --git a/tests/DI/ContainerBuilder.factory.phpt b/tests/DI/ContainerBuilder.factory.phpt index 501c4a6b0..cb31f1c47 100644 --- a/tests/DI/ContainerBuilder.factory.phpt +++ b/tests/DI/ContainerBuilder.factory.phpt @@ -15,7 +15,7 @@ require __DIR__ . '/../bootstrap.php'; interface StdClassFactory { - public function create(); + public function create(): stdClass; } interface AnnotatedFactory @@ -38,8 +38,8 @@ $builder->addFactoryDefinition('one') ->getResultDefinition() ->setFactory(stdClass::class); -$builder->addFactoryDefinition('two') - ->setImplement(AnnotatedFactory::class); +@$builder->addFactoryDefinition('two') + ->setImplement(AnnotatedFactory::class); // missing type triggers warning $builder->addDefinition('three') ->setType(FactoryReceiver::class); diff --git a/tests/DI/ContainerBuilder.factory.resolveClass.phpt b/tests/DI/ContainerBuilder.factory.resolveClass.phpt index af17c05b5..efaf5101f 100644 --- a/tests/DI/ContainerBuilder.factory.resolveClass.phpt +++ b/tests/DI/ContainerBuilder.factory.resolveClass.phpt @@ -12,15 +12,13 @@ namespace A class Factory { - /** @return Foo */ - public function createFoo() + public function createFoo(): Foo { return new Foo; } - /** @return Bar */ - public function createBar() + public function createBar(): Bar { return new Bar; } @@ -45,26 +43,7 @@ namespace C { class SelfFactory { - /** @return self */ - public static function create() - { - return new self; - } - } - - class ThisFactory - { - /** @return $this */ - public static function create() - { - return new self; - } - } - - class StaticFactory - { - /** @return static */ - public static function create() + public static function create(): self { return new self; } @@ -82,7 +61,7 @@ namespace interface StdClassFactory { - public function create(); + public function create(): stdClass; } @@ -96,7 +75,7 @@ namespace $builder->addFactoryDefinition('two') ->setImplement(StdClassFactory::class) ->getResultDefinition() - ->setFactory('@one'); + ->setFactory('@eight'); $builder->addFactoryDefinition('three') ->setImplement(StdClassFactory::class) @@ -117,10 +96,7 @@ namespace ->setFactory('C\SelfFactory::create'); $builder->addDefinition('eight') - ->setFactory('C\ThisFactory::create'); - - $builder->addDefinition('nine') - ->setFactory('C\StaticFactory::create'); + ->setFactory('stdClass'); $container = createContainer($builder); @@ -128,7 +104,7 @@ namespace Assert::type(StdClassFactory::class, $container->getService('one')); Assert::type(StdClassFactory::class, $container->getService('two')); - Assert::type(StdClassFactory::class, $container->getService('two')->create()); + Assert::type(stdClass::class, $container->getService('two')->create()); Assert::notSame($container->getService('two')->create(), $container->getService('two')->create()); Assert::type(StdClassFactory::class, $container->getService('three')); @@ -139,6 +115,4 @@ namespace Assert::type(B\Bar::class, $container->getByType(B\Bar::class)); Assert::type(C\SelfFactory::class, $container->getByType(C\SelfFactory::class)); - Assert::type(C\ThisFactory::class, $container->getByType(C\ThisFactory::class)); - Assert::type(C\StaticFactory::class, $container->getByType(C\StaticFactory::class)); } diff --git a/tests/DI/DecoratorExtension.factories.accessor.phpt b/tests/DI/DecoratorExtension.factories.accessor.phpt index 35db040c6..9c5ceaeee 100644 --- a/tests/DI/DecoratorExtension.factories.accessor.phpt +++ b/tests/DI/DecoratorExtension.factories.accessor.phpt @@ -14,10 +14,7 @@ require __DIR__ . '/../bootstrap.php'; interface FooAccessor { - /** - * @return Foo - */ - public function get(); + public function get(): Foo; } class Foo diff --git a/tests/DI/DecoratorExtension.factories.phpt b/tests/DI/DecoratorExtension.factories.phpt index 7823aa89f..56c8a2c1a 100644 --- a/tests/DI/DecoratorExtension.factories.phpt +++ b/tests/DI/DecoratorExtension.factories.phpt @@ -14,10 +14,7 @@ require __DIR__ . '/../bootstrap.php'; interface FooFactory { - /** - * @return Foo - */ - public function create(); + public function create(): Foo; } class Foo diff --git a/tests/DI/Definitions.AccessorDefinition.api.phpt b/tests/DI/Definitions.AccessorDefinition.api.phpt index 7b6df72bf..ff3a9fbf1 100644 --- a/tests/DI/Definitions.AccessorDefinition.api.phpt +++ b/tests/DI/Definitions.AccessorDefinition.api.phpt @@ -42,7 +42,7 @@ interface Bad5 interface Good1 { - public function get(); + public function get(): stdClass; } @@ -96,7 +96,7 @@ Assert::exception(function () { Assert::noError(function () { $def = new AccessorDefinition; - $def->setImplement(Good1::class); + @$def->setImplement(Good1::class); // missing type triggers warning Assert::same(Good1::class, $def->getImplement()); Assert::same(Good1::class, $def->getType()); }); diff --git a/tests/DI/Definitions.AccessorDefinition.resolve.phpt b/tests/DI/Definitions.AccessorDefinition.resolve.phpt index c45c63e1c..5c7270a1f 100644 --- a/tests/DI/Definitions.AccessorDefinition.resolve.phpt +++ b/tests/DI/Definitions.AccessorDefinition.resolve.phpt @@ -13,12 +13,12 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -interface Good1 +interface Bad1 { public function get(); } -interface Good2 +interface Good1 { public function get(): stdClass; } @@ -33,11 +33,11 @@ Assert::exception(function () { Assert::exception(function () { $def = new AccessorDefinition; - $def->setImplement(Good1::class); + @$def->setImplement(Bad1::class); // missing type triggers warning $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); $resolver->completeDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Service of type Good1: Return type of Good1::get() is not declared.'); +}, Nette\DI\ServiceCreationException::class, 'Service of type Bad1: Return type of Bad1::get() is not declared.'); Assert::noError(function () { @@ -52,7 +52,7 @@ Assert::noError(function () { Assert::noError(function () { $def = new AccessorDefinition; - $def->setImplement(Good2::class); + $def->setImplement(Good1::class); $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); @@ -61,9 +61,9 @@ Assert::noError(function () { Assert::exception(function () { $def = new AccessorDefinition; - $def->setImplement(Good2::class); + $def->setImplement(Good1::class); $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); $resolver->completeDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Service of type Good2: Service of type stdClass not found. Did you add it to configuration file?'); +}, Nette\DI\ServiceCreationException::class, 'Service of type Good1: Service of type stdClass not found. Did you add it to configuration file?'); diff --git a/tests/DI/Definitions.FactoryDefinition.api.phpt b/tests/DI/Definitions.FactoryDefinition.api.phpt index 4b8e54275..522078a23 100644 --- a/tests/DI/Definitions.FactoryDefinition.api.phpt +++ b/tests/DI/Definitions.FactoryDefinition.api.phpt @@ -36,7 +36,7 @@ interface Bad4 interface Good1 { - public function create(); + public function create(): stdClass; } @@ -84,7 +84,7 @@ Assert::exception(function () { Assert::noError(function () { $def = new FactoryDefinition; - $def->setImplement(Good1::class); + @$def->setImplement(Good1::class); // missing type triggers warning Assert::same(Good1::class, $def->getImplement()); Assert::same(Good1::class, $def->getType()); }); diff --git a/tests/DI/Definitions.FactoryDefinition.resolve.phpt b/tests/DI/Definitions.FactoryDefinition.resolve.phpt index 582023168..ea31b381c 100644 --- a/tests/DI/Definitions.FactoryDefinition.resolve.phpt +++ b/tests/DI/Definitions.FactoryDefinition.resolve.phpt @@ -13,12 +13,12 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -interface Good1 +interface Bad1 { public function create(); } -interface Good2 +interface Good1 { public function create(): stdClass; } @@ -33,10 +33,10 @@ Assert::exception(function () { Assert::exception(function () { $def = new FactoryDefinition; - $def->setImplement(Good1::class); + @$def->setImplement(Bad1::class); // missing type triggers warning $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Service of type Good1: Return type of Good1::create() is not declared.'); +}, Nette\DI\ServiceCreationException::class, 'Service of type Bad1: Return type of Bad1::create() is not declared.'); Assert::noError(function () { @@ -52,7 +52,7 @@ Assert::noError(function () { Assert::noError(function () { $def = new FactoryDefinition; - $def->setImplement(Good2::class); + $def->setImplement(Good1::class); $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); @@ -62,7 +62,7 @@ Assert::noError(function () { Assert::noError(function () { $def = new FactoryDefinition; - $def->setImplement(Good2::class); + $def->setImplement(Good1::class); $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); @@ -72,9 +72,9 @@ Assert::noError(function () { Assert::exception(function () { $def = new FactoryDefinition; - $def->setImplement(Good2::class); + $def->setImplement(Good1::class); $def->getResultDefinition()->setType(DateTime::class); $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Service of type Good2: Factory for stdClass cannot create incompatible DateTime type.'); +}, Nette\DI\ServiceCreationException::class, 'Service of type Good1: Factory for stdClass cannot create incompatible DateTime type.'); diff --git a/tests/DI/Definitions.LocatorDefinition.api.phpt b/tests/DI/Definitions.LocatorDefinition.api.phpt index 84dbac9fa..25068cf2f 100644 --- a/tests/DI/Definitions.LocatorDefinition.api.phpt +++ b/tests/DI/Definitions.LocatorDefinition.api.phpt @@ -20,46 +20,46 @@ interface Bad1 interface Bad2 { - public function create(); + public function create(): stdClass; } interface Bad3 { - public function get(); + public function get(): stdClass; } interface Bad4 { - public function get($name); + public function get($name): stdClass; - public function foo(); + public function foo(): stdClass; } interface Bad5 { - public static function get($name); + public static function get($name): stdClass; } interface Bad6 { - public function get($arg, $arg2); + public function get($arg, $arg2): stdClass; } interface Good1 { - public function get($name); + public function get($name): stdClass; } interface Good2 { - public function create($name); + public function create($name): stdClass; } interface Good3 { - public function createA(); + public function createA(): stdClass; - public function getB(); + public function getB(): stdClass; } From 9c697f8d2eeab202e6dc021eba6aa751f867ff9e Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 27 Sep 2021 00:44:29 +0200 Subject: [PATCH 10/18] annotations @return are deprecated --- src/DI/Resolver.php | 5 ++++- tests/DI/Compiler.extensionOverride.phpt | 6 ++---- tests/DI/Compiler.services.autowiring.phpt | 3 +-- tests/DI/Compiler.services.factory.phpt | 6 ++---- .../DI/ContainerBuilder.autowiring.chaining.phpt | 6 ++---- tests/DI/ContainerBuilder.basic.phpt | 3 +-- tests/DI/ContainerBuilder.basic2.phpt | 3 +-- tests/DI/ContainerBuilder.basic3.phpt | 3 +-- tests/DI/ContainerBuilder.basic4.phpt | 9 +++------ tests/DI/ContainerBuilder.byClass.phpt | 6 ++---- ...ainerBuilder.factory.resolveBuiltinTypes.phpt | 16 ++++++++-------- tests/DI/ContainerBuilder.factory.rich.phpt | 6 ++---- 12 files changed, 29 insertions(+), 43 deletions(-) diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index 26a5895c4..caf54bc0c 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -126,8 +126,11 @@ public function resolveEntityType(Statement $statement): ?string } $this->addDependency($reflection); - $type = Nette\Utils\Type::fromReflection($reflection) ?? Helpers::getReturnTypeAnnotation($reflection); + $type = Nette\Utils\Type::fromReflection($reflection) ?? ($annotation = Helpers::getReturnTypeAnnotation($reflection)); if ($type) { + if (isset($annotation)) { + trigger_error('Annotation @return should be replaced with native return type at ' . Callback::toString($entity), E_USER_DEPRECATED); + } return Helpers::ensureClassType($type, sprintf('return type of %s()', Callback::toString($entity))); } return null; diff --git a/tests/DI/Compiler.extensionOverride.phpt b/tests/DI/Compiler.extensionOverride.phpt index 6a9a38b73..2adb5619f 100644 --- a/tests/DI/Compiler.extensionOverride.phpt +++ b/tests/DI/Compiler.extensionOverride.phpt @@ -15,8 +15,7 @@ require __DIR__ . '/../bootstrap.php'; class Factory { - /** @return Lorem */ - public static function createLorem(...$args) + public static function createLorem(...$args): Lorem { return new Lorem(...$args); } @@ -25,8 +24,7 @@ class Factory class IpsumFactory { - /** @return Ipsum */ - public static function create(...$args) + public static function create(...$args): Ipsum { return new Ipsum(...$args); } diff --git a/tests/DI/Compiler.services.autowiring.phpt b/tests/DI/Compiler.services.autowiring.phpt index 57a5388f0..c49ce65d9 100644 --- a/tests/DI/Compiler.services.autowiring.phpt +++ b/tests/DI/Compiler.services.autowiring.phpt @@ -15,8 +15,7 @@ require __DIR__ . '/../bootstrap.php'; class Factory { - /** @return Model auto-wiring using annotation */ - public static function createModel() + public static function createModel(): Model { return new Model; } diff --git a/tests/DI/Compiler.services.factory.phpt b/tests/DI/Compiler.services.factory.phpt index 315081d4b..f637d8f79 100644 --- a/tests/DI/Compiler.services.factory.phpt +++ b/tests/DI/Compiler.services.factory.phpt @@ -15,8 +15,7 @@ require __DIR__ . '/../bootstrap.php'; class Factory { - /** @return Lorem */ - public static function createLorem($arg) + public static function createLorem($arg): Lorem { return new Lorem(__METHOD__ . ' ' . implode(' ', func_get_args())); } @@ -31,8 +30,7 @@ class Lorem } - /** @return Lorem */ - public function foo(...$args) + public function foo(...$args): self { $this->foo = $args; return $this; diff --git a/tests/DI/ContainerBuilder.autowiring.chaining.phpt b/tests/DI/ContainerBuilder.autowiring.chaining.phpt index b4bb276b4..a8783d4ba 100644 --- a/tests/DI/ContainerBuilder.autowiring.chaining.phpt +++ b/tests/DI/ContainerBuilder.autowiring.chaining.phpt @@ -15,8 +15,7 @@ require __DIR__ . '/../bootstrap.php'; class Foo { - /** @return Bar */ - public static function create(Test $test) + public static function create(Test $test): Bar { return new Bar; } @@ -29,8 +28,7 @@ class Foo class Bar { - /** @return Baz */ - public function create(Test $test) + public function create(Test $test): Baz { return new Baz; } diff --git a/tests/DI/ContainerBuilder.basic.phpt b/tests/DI/ContainerBuilder.basic.phpt index dd1c3a8bb..66bc51bc1 100644 --- a/tests/DI/ContainerBuilder.basic.phpt +++ b/tests/DI/ContainerBuilder.basic.phpt @@ -19,8 +19,7 @@ class Service public $methods; - /** @return Service */ - public static function create(DI\Container $container = null) + public static function create(DI\Container $container = null): self { return new self(array_slice(func_get_args(), 1)); } diff --git a/tests/DI/ContainerBuilder.basic2.phpt b/tests/DI/ContainerBuilder.basic2.phpt index 087f8921b..1bda434e5 100644 --- a/tests/DI/ContainerBuilder.basic2.phpt +++ b/tests/DI/ContainerBuilder.basic2.phpt @@ -20,8 +20,7 @@ class Factory } - /** @return Factory */ - public static function create() + public static function create(): self { return new self; } diff --git a/tests/DI/ContainerBuilder.basic3.phpt b/tests/DI/ContainerBuilder.basic3.phpt index 4bbcd7784..e124ec100 100644 --- a/tests/DI/ContainerBuilder.basic3.phpt +++ b/tests/DI/ContainerBuilder.basic3.phpt @@ -13,8 +13,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -/** @return stdClass */ -function create() +function create(): stdClass { return new stdClass; } diff --git a/tests/DI/ContainerBuilder.basic4.phpt b/tests/DI/ContainerBuilder.basic4.phpt index 6fa263fc5..48663dcb2 100644 --- a/tests/DI/ContainerBuilder.basic4.phpt +++ b/tests/DI/ContainerBuilder.basic4.phpt @@ -9,8 +9,7 @@ use Tester\Assert; require __DIR__ . '/../bootstrap.php'; -/** @return ClassA */ -function func() +function func(): ClassA { return new ClassA; } @@ -18,8 +17,7 @@ function func() class ClassA { - /** @return ClassB */ - public function funcA(stdClass $arg) + public function funcA(stdClass $arg): ClassB { return new ClassB; } @@ -27,8 +25,7 @@ class ClassA class ClassB { - /** @return ClassC */ - public function funcB(stdClass $arg) + public function funcB(stdClass $arg): ClassC { return new ClassC; } diff --git a/tests/DI/ContainerBuilder.byClass.phpt b/tests/DI/ContainerBuilder.byClass.phpt index 1b4c98d4d..b73309432 100644 --- a/tests/DI/ContainerBuilder.byClass.phpt +++ b/tests/DI/ContainerBuilder.byClass.phpt @@ -35,8 +35,7 @@ class AnnotatedFactory public $methods; - /** @return stdClass */ - public function create() + public function create(): stdClass { $this->methods[] = [__FUNCTION__, func_get_args()]; return new stdClass; @@ -57,8 +56,7 @@ class UninstantiableFactory } - /** @return stdClass */ - public function create() + public function create(): stdClass { } } diff --git a/tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt b/tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt index 9ce792b35..aae931c9b 100644 --- a/tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt +++ b/tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt @@ -81,7 +81,7 @@ namespace ->setType(A\Factory::class); $builder->addDefinition('a') ->setFactory('@factory::createArray'); - $container = createContainer($builder); + $container = @createContainer($builder); // annotation @return is deprecated }, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of A\\Factory::createArray() is not expected to be nullable/union/intersection/built-in, 'array' given."); Assert::exception(function () { @@ -90,7 +90,7 @@ namespace ->setType(A\Factory::class); $builder->addDefinition('c') ->setFactory('@factory::createCallable'); - $container = createContainer($builder); + $container = @createContainer($builder); // annotation @return is deprecated }, Nette\DI\ServiceCreationException::class, "Service 'c': Return type of A\\Factory::createCallable() is not expected to be nullable/union/intersection/built-in, 'callable' given."); Assert::exception(function () { @@ -99,7 +99,7 @@ namespace ->setType(A\Factory::class); $builder->addDefinition('s') ->setFactory('@factory::createString'); - $container = createContainer($builder); + $container = @createContainer($builder); // annotation @return is deprecated }, Nette\DI\ServiceCreationException::class, "Service 's': Return type of A\\Factory::createString() is not expected to be nullable/union/intersection/built-in, 'string' given."); Assert::exception(function () { @@ -108,7 +108,7 @@ namespace ->setType(A\Factory::class); $builder->addDefinition('i') ->setFactory('@factory::createInt'); - $container = createContainer($builder); + $container = @createContainer($builder); // annotation @return is deprecated }, Nette\DI\ServiceCreationException::class, "Service 'i': Return type of A\\Factory::createInt() is not expected to be nullable/union/intersection/built-in, 'int' given."); Assert::exception(function () { @@ -117,7 +117,7 @@ namespace ->setType(A\Factory::class); $builder->addDefinition('b') ->setFactory('@factory::createBool'); - $container = createContainer($builder); + $container = @createContainer($builder); // annotation @return is deprecated }, Nette\DI\ServiceCreationException::class, "Service 'b': Return type of A\\Factory::createBool() is not expected to be nullable/union/intersection/built-in, 'bool' given."); Assert::exception(function () { @@ -126,7 +126,7 @@ namespace ->setType(A\Factory::class); $builder->addDefinition('f') ->setFactory('@factory::createFloat'); - $container = createContainer($builder); + $container = @createContainer($builder); // annotation @return is deprecated }, Nette\DI\ServiceCreationException::class, "Service 'f': Return type of A\\Factory::createFloat() is not expected to be nullable/union/intersection/built-in, 'float' given."); Assert::exception(function () { @@ -135,7 +135,7 @@ namespace ->setType(A\Factory::class); $builder->addDefinition('f') ->setFactory('@factory::createObject'); - $container = createContainer($builder); + $container = @createContainer($builder); // annotation @return is deprecated }, Nette\DI\ServiceCreationException::class, "Service 'f': Unknown service type, specify it or declare return type of factory."); Assert::exception(function () { @@ -144,7 +144,7 @@ namespace ->setType(A\Factory::class); $builder->addDefinition('f') ->setFactory('@factory::createMixed'); - $container = createContainer($builder); + $container = @createContainer($builder); // annotation @return is deprecated }, Nette\DI\ServiceCreationException::class, "Service 'f': Unknown service type, specify it or declare return type of factory."); } diff --git a/tests/DI/ContainerBuilder.factory.rich.phpt b/tests/DI/ContainerBuilder.factory.rich.phpt index b26fdb0af..435004119 100644 --- a/tests/DI/ContainerBuilder.factory.rich.phpt +++ b/tests/DI/ContainerBuilder.factory.rich.phpt @@ -16,8 +16,7 @@ require __DIR__ . '/../bootstrap.php'; class Factory { - /** @return Obj */ - public function create() + public function create(): Obj { return new Obj; } @@ -31,8 +30,7 @@ class Factory class Obj { - /** @return Obj */ - public function foo(...$args) + public function foo(...$args): self { $this->args[] = $args; return $this; From 4bcdb7506764ba3eb6a808173f71b12cf4925d97 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 2 Nov 2021 12:50:53 +0100 Subject: [PATCH 11/18] Resolver: non-optional autowired parameters trigger notice that value is required [Closes #271] --- src/DI/Resolver.php | 11 ++++++++++- tests/DI/Resolver.autowireArguments.80.phpt | 7 +++---- tests/DI/Resolver.autowireArguments.phpt | 7 +++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index caf54bc0c..01898927d 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -623,10 +623,19 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab // optional + !defaultAvailable, optional + defaultAvailable since 8.0.0 = i.e. Exception::__construct, mysqli::mysqli, ... // optional + !defaultAvailable = variadics // in other cases the optional and defaultAvailable are identical - return $parameter->isDefaultValueAvailable() + $default = $parameter->isDefaultValueAvailable() ? Reflection::getParameterDefaultValue($parameter) : null; + if (!$parameter->isOptional()) { + trigger_error(sprintf( + 'The parameter %s should have a declared value in the configuration. Value %s is currently used.', + $desc, + var_export($default, true) + ), E_USER_DEPRECATED); + } + return $default; + } else { throw new ServiceCreationException(sprintf( 'Parameter %s has %s, so its value must be specified.', diff --git a/tests/DI/Resolver.autowireArguments.80.phpt b/tests/DI/Resolver.autowireArguments.80.phpt index 1d252b569..204b40e22 100644 --- a/tests/DI/Resolver.autowireArguments.80.phpt +++ b/tests/DI/Resolver.autowireArguments.80.phpt @@ -36,10 +36,9 @@ Assert::exception(function () { Resolver::autowireArguments(new ReflectionMethod(Test::class, 'methodUnion'), [], function () {}); }, Nette\InvalidStateException::class, 'Parameter $self in Test::methodUnion() has union type and no default value, so its value must be specified.'); -Assert::same( - [null], - Resolver::autowireArguments(new ReflectionMethod(Test::class, 'methodUnionNullable'), [], function () {}), -); +Assert::error(function () { + Resolver::autowireArguments(new ReflectionMethod(Test::class, 'methodUnionNullable'), [], function () {}); +}, E_USER_DEPRECATED, 'The parameter $nullable in Test::methodUnionNullable() should have a declared value in the configuration. Value NULL is currently used.'); Assert::same( [], diff --git a/tests/DI/Resolver.autowireArguments.phpt b/tests/DI/Resolver.autowireArguments.phpt index 4caf96bf5..cf58805ac 100644 --- a/tests/DI/Resolver.autowireArguments.phpt +++ b/tests/DI/Resolver.autowireArguments.phpt @@ -32,12 +32,11 @@ Assert::equal( }) ); -Assert::equal( - [new Test, new Test, null, null], +Assert::error(function () { Resolver::autowireArguments(new ReflectionMethod(Test::class, 'methodNullable'), [], function ($type) { return $type === Test::class ? new Test : null; - }) -); + }); +}, E_USER_DEPRECATED, 'The parameter $nullable2 in Test::methodNullable() should have a declared value in the configuration. Value NULL is currently used.'); // variadics From 9730aaae46fedfb92cb9e5ab12f06d003a21e004 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 27 Sep 2021 01:34:41 +0200 Subject: [PATCH 12/18] DefinitionSchema moved to Nette\DI\Extensions --- src/DI/{Config => Extensions}/DefinitionSchema.php | 3 ++- src/DI/Extensions/ServicesExtension.php | 2 +- tests/DI/DefinitionSchema.normalize.phpt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) rename src/DI/{Config => Extensions}/DefinitionSchema.php (99%) diff --git a/src/DI/Config/DefinitionSchema.php b/src/DI/Extensions/DefinitionSchema.php similarity index 99% rename from src/DI/Config/DefinitionSchema.php rename to src/DI/Extensions/DefinitionSchema.php index 1655571c3..d68fddd94 100644 --- a/src/DI/Config/DefinitionSchema.php +++ b/src/DI/Extensions/DefinitionSchema.php @@ -7,9 +7,10 @@ declare(strict_types=1); -namespace Nette\DI\Config; +namespace Nette\DI\Extensions; use Nette; +use Nette\DI\Config\Helpers; use Nette\DI\Definitions; use Nette\DI\Definitions\Statement; use Nette\Schema\Context; diff --git a/src/DI/Extensions/ServicesExtension.php b/src/DI/Extensions/ServicesExtension.php index 2bb84904a..d1febbf42 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -24,7 +24,7 @@ final class ServicesExtension extends Nette\DI\CompilerExtension public function getConfigSchema(): Nette\Schema\Schema { - return Nette\Schema\Expect::arrayOf(new Nette\DI\Config\DefinitionSchema($this->getContainerBuilder())); + return Nette\Schema\Expect::arrayOf(new DefinitionSchema($this->getContainerBuilder())); } diff --git a/tests/DI/DefinitionSchema.normalize.phpt b/tests/DI/DefinitionSchema.normalize.phpt index 733cca6a2..f759a3c21 100644 --- a/tests/DI/DefinitionSchema.normalize.phpt +++ b/tests/DI/DefinitionSchema.normalize.phpt @@ -6,8 +6,8 @@ declare(strict_types=1); -use Nette\DI\Config\DefinitionSchema; use Nette\DI\Definitions\Statement; +use Nette\DI\Extensions\DefinitionSchema; use Tester\Assert; From 86ab7b34f9dd5b835ed0f7655ba2e14dead819c5 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 28 Sep 2021 12:31:27 +0200 Subject: [PATCH 13/18] deprecated option 'parameters' used by generated factories --- src/DI/Definitions/FactoryDefinition.php | 17 ++++++++++++++++- ...ler.generatedFactory.nullableParameters.phpt | 3 ++- .../Compiler.generatedFactory.returnTypes.phpt | 3 ++- ...piler.generatedFactory.scalarParameters.phpt | 3 ++- tests/DI/ContainerBuilder.factory.params.phpt | 4 ++-- tests/DI/Definitions.FactoryDefinition.api.phpt | 2 +- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/DI/Definitions/FactoryDefinition.php b/src/DI/Definitions/FactoryDefinition.php index 56f43ff2e..9db0247db 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -92,14 +92,29 @@ public function getResultDefinition(): Definition } - /** @return static */ + /** @deprecated */ public function setParameters(array $params) { + if ($params) { + $old = $new = []; + foreach ($params as $k => $v) { + $tmp = explode(' ', is_int($k) ? $v : $k); + $old[] = '%' . end($tmp) . '%'; + $new[] = '$' . end($tmp); + } + trigger_error(sprintf( + "Service '%s': Option 'parameters' is deprecated and should be removed. The %s should be replaced with %s in configuration.", + $this->getName(), + implode(', ', $old), + implode(', ', $new) + ), E_USER_DEPRECATED); + } $this->parameters = $params; return $this; } + /** @deprecated */ public function getParameters(): array { return $this->parameters; diff --git a/tests/DI/Compiler.generatedFactory.nullableParameters.phpt b/tests/DI/Compiler.generatedFactory.nullableParameters.phpt index 43ca87089..929af4eea 100644 --- a/tests/DI/Compiler.generatedFactory.nullableParameters.phpt +++ b/tests/DI/Compiler.generatedFactory.nullableParameters.phpt @@ -40,7 +40,8 @@ class Article } $compiler = new DI\Compiler; -$container = createContainer($compiler, ' +// parameters are deprecated +$container = @createContainer($compiler, ' services: article: diff --git a/tests/DI/Compiler.generatedFactory.returnTypes.phpt b/tests/DI/Compiler.generatedFactory.returnTypes.phpt index 2b05d9257..15a4d097c 100644 --- a/tests/DI/Compiler.generatedFactory.returnTypes.phpt +++ b/tests/DI/Compiler.generatedFactory.returnTypes.phpt @@ -34,7 +34,8 @@ class FooArticle extends Article } $compiler = new DI\Compiler; -$container = createContainer($compiler, ' +// parameters are deprecated +$container = @createContainer($compiler, ' services: article: factory: Article(%title%) diff --git a/tests/DI/Compiler.generatedFactory.scalarParameters.phpt b/tests/DI/Compiler.generatedFactory.scalarParameters.phpt index 3cb72ff7f..68666489d 100644 --- a/tests/DI/Compiler.generatedFactory.scalarParameters.phpt +++ b/tests/DI/Compiler.generatedFactory.scalarParameters.phpt @@ -30,7 +30,8 @@ class Article } $compiler = new DI\Compiler; -$container = createContainer($compiler, ' +// parameters are deprecated +$container = @createContainer($compiler, ' services: article: factory: Article(%title%) diff --git a/tests/DI/ContainerBuilder.factory.params.phpt b/tests/DI/ContainerBuilder.factory.params.phpt index 2fbe0a464..a5e1d25e2 100644 --- a/tests/DI/ContainerBuilder.factory.params.phpt +++ b/tests/DI/ContainerBuilder.factory.params.phpt @@ -26,8 +26,8 @@ $builder->addFactoryDefinition('one') ->setFactory(stdClass::class) ->addSetup('$a', [$builder::literal('$a')]); -$builder->addFactoryDefinition('two') - ->setParameters(['stdClass foo', 'array bar', 'foobar' => null]) +@$builder->addFactoryDefinition('two') + ->setParameters(['stdClass foo', 'array bar', 'foobar' => null]) // parameters is deprecated ->setImplement(StdClassFactory::class) ->getResultDefinition() ->setFactory(stdClass::class) diff --git a/tests/DI/Definitions.FactoryDefinition.api.phpt b/tests/DI/Definitions.FactoryDefinition.api.phpt index 522078a23..526aba439 100644 --- a/tests/DI/Definitions.FactoryDefinition.api.phpt +++ b/tests/DI/Definitions.FactoryDefinition.api.phpt @@ -94,7 +94,7 @@ test('', function () { $def = new FactoryDefinition; $def->setImplement(Good1::class); - $def->setParameters(['a' => 1]); + @$def->setParameters(['a' => 1]); // parameters are deprecated Assert::same(['a' => 1], $def->getParameters()); }); From 2a298effd691a9c52c62a7ada3a55f299f97ef41 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 28 Sep 2021 13:37:14 +0200 Subject: [PATCH 14/18] option 'create' is used primary internally --- src/DI/Extensions/DefinitionSchema.php | 25 ++++++++++++----------- src/DI/Extensions/ServicesExtension.php | 10 ++++----- tests/DI/Compiler.loadConfig.include.phpt | 2 +- tests/DI/DefinitionSchema.normalize.phpt | 14 ++++++------- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/DI/Extensions/DefinitionSchema.php b/src/DI/Extensions/DefinitionSchema.php index d68fddd94..86027045b 100644 --- a/src/DI/Extensions/DefinitionSchema.php +++ b/src/DI/Extensions/DefinitionSchema.php @@ -84,28 +84,29 @@ public function normalize($def, Context $context) $res += $def->arguments; } elseif (count($def->arguments) > 1) { $res['references'] = $def->arguments; - } elseif ($factory = array_shift($def->arguments)) { - $res['factory'] = $factory; + } elseif ($creator = array_shift($def->arguments)) { + $res['create'] = $creator; } return $res; } elseif (!is_array($def) || isset($def[0], $def[1])) { - return ['factory' => $def]; + return ['create' => $def]; } elseif (is_array($def)) { - if (isset($def['create']) && !isset($def['factory'])) { - $def['factory'] = $def['create']; - unset($def['create']); - } // back compatibility + if (isset($def['factory']) && !isset($def['create'])) { + $def['create'] = $def['factory']; + unset($def['factory']); + } + if ( isset($def['class']) && !isset($def['type']) - && !isset($def['factory']) + && !isset($def['create']) && !isset($def['dynamic']) && !isset($def['imported']) ) { - $def['factory'] = $def['class']; + $def['create'] = $def['class']; unset($def['class']); } @@ -200,7 +201,7 @@ private static function getServiceSchema(): Schema { return Expect::structure([ 'type' => Expect::type('string'), - 'factory' => Expect::type('callable|Nette\DI\Definitions\Statement'), + 'create' => Expect::type('callable|Nette\DI\Definitions\Statement'), 'arguments' => Expect::array(), 'setup' => Expect::listOf('callable|Nette\DI\Definitions\Statement|array:1'), 'inject' => Expect::bool(), @@ -217,7 +218,7 @@ private static function getAccessorSchema(): Schema return Expect::structure([ 'type' => Expect::string(), 'implement' => Expect::string(), - 'factory' => Expect::type('callable|Nette\DI\Definitions\Statement'), + 'create' => Expect::type('callable|Nette\DI\Definitions\Statement'), 'autowired' => Expect::type('bool|string|array'), 'tags' => Expect::array(), ]); @@ -228,7 +229,7 @@ private static function getFactorySchema(): Schema { return Expect::structure([ 'type' => Expect::string(), - 'factory' => Expect::type('callable|Nette\DI\Definitions\Statement'), + 'create' => Expect::type('callable|Nette\DI\Definitions\Statement'), 'implement' => Expect::string(), 'arguments' => Expect::array(), 'setup' => Expect::listOf('callable|Nette\DI\Definitions\Statement|array:1'), diff --git a/src/DI/Extensions/ServicesExtension.php b/src/DI/Extensions/ServicesExtension.php index d1febbf42..12d97f825 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -80,8 +80,8 @@ private function loadDefinition(?string $name, \stdClass $config): void */ private function updateServiceDefinition(Definitions\ServiceDefinition $definition, \stdClass $config): void { - if ($config->factory) { - $definition->setFactory(Helpers::filterArguments([$config->factory])[0]); + if ($config->create) { + $definition->setFactory(Helpers::filterArguments([$config->create])[0]); $definition->setType(null); } @@ -121,7 +121,7 @@ private function updateAccessorDefinition(Definitions\AccessorDefinition $defini $definition->setImplement($config->implement); } - if ($ref = $config->factory ?? $config->type ?? null) { + if ($ref = $config->create ?? $config->type ?? null) { $definition->setReference($ref); } } @@ -136,8 +136,8 @@ private function updateFactoryDefinition(Definitions\FactoryDefinition $definiti $definition->setAutowired(true); } - if ($config->factory) { - $resultDef->setFactory(Helpers::filterArguments([$config->factory])[0]); + if ($config->create) { + $resultDef->setFactory(Helpers::filterArguments([$config->create])[0]); } if ($config->type) { diff --git a/tests/DI/Compiler.loadConfig.include.phpt b/tests/DI/Compiler.loadConfig.include.phpt index 4f3f78b1f..26d00f815 100644 --- a/tests/DI/Compiler.loadConfig.include.phpt +++ b/tests/DI/Compiler.loadConfig.include.phpt @@ -41,7 +41,7 @@ Assert::equal([ 'services' => [ 'a' => (object) [ 'type' => null, - 'factory' => stdClass::class, + 'create' => stdClass::class, 'arguments' => [], 'setup' => [], 'inject' => null, diff --git a/tests/DI/DefinitionSchema.normalize.phpt b/tests/DI/DefinitionSchema.normalize.phpt index f759a3c21..890072f83 100644 --- a/tests/DI/DefinitionSchema.normalize.phpt +++ b/tests/DI/DefinitionSchema.normalize.phpt @@ -26,17 +26,17 @@ Assert::with(DefinitionSchema::class, function () { Assert::same([], $schema->normalize(null, $context)); Assert::same([], $schema->normalize([], $context)); Assert::same([false], $schema->normalize(false, $context)); - Assert::same(['factory' => true], $schema->normalize(true, $context)); - Assert::same(['factory' => 'class'], $schema->normalize('class', $context)); + Assert::same(['create' => true], $schema->normalize(true, $context)); + Assert::same(['create' => 'class'], $schema->normalize('class', $context)); Assert::same(['implement' => Iface::class], $schema->normalize(Iface::class, $context)); - Assert::same(['factory' => ['class', 'method']], $schema->normalize(['class', 'method'], $context)); - Assert::same(['factory' => [Iface::class, 'method']], $schema->normalize([Iface::class, 'method'], $context)); + Assert::same(['create' => ['class', 'method']], $schema->normalize(['class', 'method'], $context)); + Assert::same(['create' => [Iface::class, 'method']], $schema->normalize([Iface::class, 'method'], $context)); $statement = new Statement(['class', 'method']); - Assert::same(['factory' => $statement], $schema->normalize($statement, $context)); + Assert::same(['create' => $statement], $schema->normalize($statement, $context)); $statement = new Statement(Iface::class, ['foo']); - Assert::same(['implement' => Iface::class, 'factory' => 'foo'], $schema->normalize($statement, $context)); + Assert::same(['implement' => Iface::class, 'create' => 'foo'], $schema->normalize($statement, $context)); $statement = new Statement(Iface::class, ['stdClass', 'stdClass']); Assert::same(['implement' => Iface::class, 'references' => ['stdClass', 'stdClass']], $schema->normalize($statement, $context)); @@ -45,7 +45,7 @@ Assert::with(DefinitionSchema::class, function () { Assert::same(['implement' => Iface::class, 'tagged' => 123], $schema->normalize($statement, $context)); // aliases - Assert::same(['factory' => 'val'], $schema->normalize(['class' => 'val'], $context)); + Assert::same(['create' => 'val'], $schema->normalize(['class' => 'val'], $context)); Assert::same(['imported' => 'val'], $schema->normalize(['dynamic' => 'val'], $context)); Assert::exception(function () use ($schema, $context) { From feeaaa0b8e288ced902d4a260b12beca82641cd6 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 26 Sep 2021 23:11:18 +0200 Subject: [PATCH 15/18] exception messages use [Service ...]\n format [WIP] added Definition::getDescriptor(), Helpers::entityToString() --- src/DI/Definitions/AccessorDefinition.php | 14 +-- src/DI/Definitions/Definition.php | 27 ++++- src/DI/Definitions/FactoryDefinition.php | 17 +-- src/DI/Definitions/LocatorDefinition.php | 14 +-- src/DI/Definitions/ServiceDefinition.php | 10 ++ src/DI/Extensions/InjectExtension.php | 24 ++++ src/DI/Extensions/ServicesExtension.php | 9 +- src/DI/Helpers.php | 36 +++++- src/DI/PhpGenerator.php | 2 +- src/DI/Resolver.php | 65 ++++------- tests/DI/Compiler.configOverride.phpt | 3 +- .../DI/Compiler.extensionOverride.errors.phpt | 3 +- tests/DI/Compiler.functions.phpt | 4 +- tests/DI/Compiler.generatedFactory.phpt | 9 +- .../ContainerBuilder.autowiring.novalue.phpt | 6 +- tests/DI/ContainerBuilder.error.phpt | 30 ++++- tests/DI/ContainerBuilder.factory.error.phpt | 104 +++++++++++++----- ...erBuilder.factory.resolveBuiltinTypes.phpt | 24 ++-- tests/DI/ContainerBuilder.recursive.phpt | 3 +- tests/DI/ContainerBuilder.selfdependency.phpt | 4 +- .../Definitions.AccessorDefinition.api.phpt | 21 ++-- ...efinitions.AccessorDefinition.resolve.phpt | 9 +- .../DI/Definitions.FactoryDefinition.api.phpt | 18 ++- ...Definitions.FactoryDefinition.resolve.phpt | 9 +- tests/DI/Definitions.ImportedDefinition.phpt | 6 +- .../DI/Definitions.LocatorDefinition.api.phpt | 24 ++-- ...Definitions.LocatorDefinition.resolve.phpt | 3 +- tests/DI/Definitions.ServiceDefinition.phpt | 3 +- tests/DI/InjectExtension.errors.phpt | 9 +- .../DI/Resolver.autowireArguments.errors.phpt | 6 +- 30 files changed, 356 insertions(+), 160 deletions(-) diff --git a/src/DI/Definitions/AccessorDefinition.php b/src/DI/Definitions/AccessorDefinition.php index 0c2268858..6a364f70b 100644 --- a/src/DI/Definitions/AccessorDefinition.php +++ b/src/DI/Definitions/AccessorDefinition.php @@ -30,8 +30,8 @@ public function setImplement(string $interface) { if (!interface_exists($interface)) { throw new Nette\InvalidArgumentException(sprintf( - "Service '%s': Interface '%s' not found.", - $this->getName(), + "[%s]\nInterface '%s' not found.", + $this->getDescriptor(), $interface )); } @@ -45,19 +45,19 @@ public function setImplement(string $interface) || count($rc->getMethods()) > 1 ) { throw new Nette\InvalidArgumentException(sprintf( - "Service '%s': Interface %s must have just one non-static method get().", - $this->getName(), + "[%s]\nInterface %s must have just one non-static method get().", + $this->getDescriptor(), $interface )); } elseif ($method->getNumberOfParameters()) { throw new Nette\InvalidArgumentException(sprintf( - "Service '%s': Method %s::get() must have no parameters.", - $this->getName(), + "[%s]\nMethod %s::get() must have no parameters.", + $this->getDescriptor(), $interface )); } try { - Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()"); + Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()", $this->getDescriptor()); } catch (Nette\DI\ServiceCreationException $e) { trigger_error($e->getMessage(), E_USER_DEPRECATED); } diff --git a/src/DI/Definitions/Definition.php b/src/DI/Definitions/Definition.php index b858257c6..568b9d1f2 100644 --- a/src/DI/Definitions/Definition.php +++ b/src/DI/Definitions/Definition.php @@ -55,6 +55,29 @@ final public function getName(): ?string } + final public function isAnonymous(): bool + { + return !$this->name || ctype_digit($this->name); + } + + + public function getDescriptor(): string + { + if (!$this->isAnonymous()) { + return "Service '$this->name'" . ($this->type ? " of type $this->type" : ''); + + } elseif ($this->type) { + return "Service of type $this->type"; + + } elseif ($this->name) { + return "Service '$this->name'"; + + } else { + return 'Service ?'; + } + } + + /** @return static */ protected function setType(?string $type) { @@ -65,8 +88,8 @@ protected function setType(?string $type) $this->type = null; } elseif (!class_exists($type) && !interface_exists($type)) { throw new Nette\InvalidArgumentException(sprintf( - "Service '%s': Class or interface '%s' not found.", - $this->name, + "[%s]\nClass or interface '%s' not found.", + $this->getDescriptor(), $type )); } else { diff --git a/src/DI/Definitions/FactoryDefinition.php b/src/DI/Definitions/FactoryDefinition.php index 9db0247db..200dde667 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -42,8 +42,8 @@ public function setImplement(string $interface) { if (!interface_exists($interface)) { throw new Nette\InvalidArgumentException(sprintf( - "Service '%s': Interface '%s' not found.", - $this->getName(), + "[%s]\nInterface '%s' not found.", + $this->getDescriptor(), $interface )); } @@ -51,13 +51,13 @@ public function setImplement(string $interface) $method = $rc->getMethods()[0] ?? null; if (!$method || $method->isStatic() || $method->name !== self::METHOD_CREATE || count($rc->getMethods()) > 1) { throw new Nette\InvalidArgumentException(sprintf( - "Service '%s': Interface %s must have just one non-static method create().", - $this->getName(), + "[%s]\nInterface %s must have just one non-static method create().", + $this->getDescriptor(), $interface )); } try { - Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()"); + Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()", $this->getDescriptor()); } catch (Nette\DI\ServiceCreationException $e) { trigger_error($e->getMessage(), E_USER_DEPRECATED); } @@ -103,8 +103,8 @@ public function setParameters(array $params) $new[] = '$' . end($tmp); } trigger_error(sprintf( - "Service '%s': Option 'parameters' is deprecated and should be removed. The %s should be replaced with %s in configuration.", - $this->getName(), + "[%s]\nOption 'parameters' is deprecated and should be removed. The %s should be replaced with %s in configuration.", + $this->getDescriptor(), implode(', ', $old), implode(', ', $new) ), E_USER_DEPRECATED); @@ -143,7 +143,8 @@ public function resolveType(Nette\DI\Resolver $resolver): void if ($type && !$type->allows($resultDef->getType())) { throw new ServiceCreationException(sprintf( - 'Factory for %s cannot create incompatible %s type.', + "[%s]\nFactory for %s cannot create incompatible %s type.", + $this->getDescriptor(), $type, $resultDef->getType() )); diff --git a/src/DI/Definitions/LocatorDefinition.php b/src/DI/Definitions/LocatorDefinition.php index 0b3517cce..86d53b773 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -28,11 +28,11 @@ final class LocatorDefinition extends Definition public function setImplement(string $interface) { if (!interface_exists($interface)) { - throw new Nette\InvalidArgumentException(sprintf("Service '%s': Interface '%s' not found.", $this->getName(), $interface)); + throw new Nette\InvalidArgumentException(sprintf("[%s]\nInterface '%s' not found.", $this->getDescriptor(), $interface)); } $methods = (new \ReflectionClass($interface))->getMethods(); if (!$methods) { - throw new Nette\InvalidArgumentException(sprintf("Service '%s': Interface %s must have at least one method.", $this->getName(), $interface)); + throw new Nette\InvalidArgumentException(sprintf("[%s]\nInterface %s must have at least one method.", $this->getDescriptor(), $interface)); } foreach ($methods as $method) { @@ -41,15 +41,15 @@ public function setImplement(string $interface) || (preg_match('#^(get|create)[A-Z]#', $method->name) && $method->getNumberOfParameters() === 0) )) { throw new Nette\InvalidArgumentException(sprintf( - "Service '%s': Method %s::%s() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static.", - $this->getName(), + "[%s]\nMethod %s::%s() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static.", + $this->getDescriptor(), $interface, $method->name )); } if ($method->getNumberOfParameters() === 0) { try { - Nette\DI\Helpers::ensureClassType(Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", true); + Nette\DI\Helpers::ensureClassType(Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", $this->getDescriptor(), true); } catch (Nette\DI\ServiceCreationException $e) { trigger_error($e->getMessage(), E_USER_DEPRECATED); } @@ -111,8 +111,8 @@ public function complete(Nette\DI\Resolver $resolver): void foreach ($resolver->getContainerBuilder()->findByTag($this->tagged) as $name => $tag) { if (isset($this->references[$tag])) { trigger_error(sprintf( - "Service '%s': duplicated tag '%s' with value '%s'.", - $this->getName(), + "[%s]\nDuplicated tag '%s' with value '%s'.", + $this->getDescriptor(), $this->tagged, $tag ), E_USER_NOTICE); diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 10db1f0fb..cd7015d0f 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -35,6 +35,16 @@ public function __construct() } + public function getDescriptor(): string + { + $entity = $this->getEntity(); + if ($entity && $this->isAnonymous()) { + return 'Service ' . (is_string($entity) ? "of type $entity" : Nette\DI\Helpers::entityToString($entity)); + } + return parent::getDescriptor(); + } + + /** @return static */ public function setType(?string $type) { diff --git a/src/DI/Extensions/InjectExtension.php b/src/DI/Extensions/InjectExtension.php index 718437a1a..af8144022 100644 --- a/src/DI/Extensions/InjectExtension.php +++ b/src/DI/Extensions/InjectExtension.php @@ -62,6 +62,7 @@ private function updateDefinition(Definitions\ServiceDefinition $def): void unset($setups[$key]); } } + self::checkType($class, $property, $type, $builder, $def); array_unshift($setups, $inject); } @@ -148,7 +149,30 @@ public static function callInjects(DI\Container $container, $service): void } foreach (self::getInjectProperties(get_class($service)) as $property => $type) { + self::checkType($service, $property, $type, $container); $service->$property = $container->getByType($type); } } + + + /** + * @param object|string $class + * @param DI\Container|DI\ContainerBuilder|null $container + */ + private static function checkType( + $class, + string $name, + ?string $type, + $container, + Definitions\Definition $def = null + ): void { + if ($container && !$container->getByType($type, false)) { + throw new Nette\DI\MissingServiceException(sprintf( + "%sService of type %s required by %s not found.\nDid you add it to configuration file?", + $def ? '[' . $def->getDescriptor() . "]\n" : '', + $type, + Reflection::toString(new \ReflectionProperty($class, $name)) + )); + } + } } diff --git a/src/DI/Extensions/ServicesExtension.php b/src/DI/Extensions/ServicesExtension.php index 12d97f825..fbef73ac1 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -55,7 +55,7 @@ private function loadDefinition(?string $name, \stdClass $config): void $this->getContainerBuilder()->removeDefinition($name); return; } elseif (!empty($config->alteration) && !$this->getContainerBuilder()->hasDefinition($name)) { - throw new Nette\DI\InvalidConfigurationException('missing original definition for alteration.'); + throw new Nette\DI\InvalidConfigurationException('Missing original definition for alteration.'); } $def = $this->retrieveDefinition($name, $config); @@ -69,8 +69,13 @@ private function loadDefinition(?string $name, \stdClass $config): void ]; $this->{$methods[$config->defType]}($def, $config); $this->updateDefinition($def, $config); + } catch (\Exception $e) { - throw new Nette\DI\InvalidConfigurationException(($name ? "Service '$name': " : '') . $e->getMessage(), 0, $e); + $message = $e->getMessage(); + if ($name && !Nette\Utils\Strings::startsWith($message, '[Service ')) { + $message = "[Service '$name']\n$message"; + } + throw new Nette\DI\InvalidConfigurationException($message, 0, $e); } } diff --git a/src/DI/Helpers.php b/src/DI/Helpers.php index 8e54db170..2c7ae2e2e 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -216,16 +216,21 @@ public static function getReturnTypeAnnotation(\ReflectionFunctionAbstract $func } - public static function ensureClassType(?Type $type, string $hint, bool $allowNullable = false): string - { + public static function ensureClassType( + ?Type $type, + string $hint, + string $descriptor = '', + bool $allowNullable = false + ): string { + $descriptor = $descriptor ? "[$descriptor]\n" : ''; if (!$type) { - throw new ServiceCreationException(sprintf('%s is not declared.', ucfirst($hint))); + throw new ServiceCreationException(sprintf('%s%s is not declared.', $descriptor, ucfirst($hint))); } elseif (!$type->isClass() || (!$allowNullable && $type->isUnion())) { - throw new ServiceCreationException(sprintf("%s is not expected to be %sunion/intersection/built-in, '%s' given.", ucfirst($hint), $allowNullable ? '' : 'nullable/', $type)); + throw new ServiceCreationException(sprintf("%s%s is not expected to be %sunion/intersection/built-in, '%s' given.", $descriptor, ucfirst($hint), $allowNullable ? '' : 'nullable/', $type)); } $class = $type->getSingleName(); if (!class_exists($class) && !interface_exists($class)) { - throw new ServiceCreationException(sprintf("Class '%s' not found.\nCheck the %s.", $class, $hint)); + throw new ServiceCreationException(sprintf("%sClass '%s' not found.\nCheck the %s.", $descriptor, $class, $hint)); } return $class; } @@ -264,4 +269,25 @@ public static function convertType($value, string $type) $type )); } + + + /** + * @param string|array|Reference $entity + */ + public static function entityToString($entity, bool $inner = false): string + { + if (is_string($entity)) { + return $entity . ($inner ? '()' : ''); + + } elseif ($entity instanceof Reference) { + return '@' . $entity->getValue(); + + } elseif (is_array($entity)) { + [$a, $b] = $entity; + return self::entityToString($a instanceof Statement ? $a->entity : $a, true) + . '::' + . $b + . (strpos($b, '$') === false ? '()' : ''); + } + } } diff --git a/src/DI/PhpGenerator.php b/src/DI/PhpGenerator.php index 05d44c5e4..9db80529f 100644 --- a/src/DI/PhpGenerator.php +++ b/src/DI/PhpGenerator.php @@ -104,7 +104,7 @@ public function generateMethod(Definitions\Definition $def): Php\Method return $method; } catch (\Exception $e) { - throw new ServiceCreationException("Service '$name': " . $e->getMessage(), 0, $e); + throw new ServiceCreationException(sprintf("[%s]\n%s", $def->getDescriptor(), $e->getMessage()), 0, $e); } } diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index 01898927d..78b6846fd 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -295,9 +295,19 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo try { $arguments = $this->completeArguments($arguments); + } catch (ServiceCreationException $e) { - if (!strpos($e->getMessage(), ' (used in')) { - $e->setMessage($e->getMessage() . " (used in {$this->entityToString($entity)})"); + if (!strpos($e->getMessage(), "\nRelated to")) { + if (is_string($entity)) { + $desc = $entity . '::__construct()'; + } else { + $desc = Helpers::entityToString($entity); + $desc = preg_replace('~@self::~A', '', $desc); + } + if ($currentServiceAllowed) { + $desc .= ' in setup'; + } + $e->setMessage($e->getMessage() . "\nRelated to $desc."); } throw $e; } @@ -433,24 +443,15 @@ public function addDependency($dep) private function completeException(\Exception $e, Definition $def): ServiceCreationException { - if ($e instanceof ServiceCreationException && Strings::startsWith($e->getMessage(), "Service '")) { + $message = $e->getMessage(); + if ($e instanceof ServiceCreationException && Strings::startsWith($message, '[Service ')) { return $e; } - $name = $def->getName(); - $type = $def->getType(); - if ($name && !ctype_digit($name)) { - $message = "Service '$name'" . ($type ? " (type of $type)" : '') . ': '; - } elseif ($type) { - $message = "Service of type $type: "; - } elseif ($def instanceof Definitions\ServiceDefinition && $def->getEntity()) { - $message = 'Service (' . $this->entityToString($def->getEntity()) . '): '; - } else { - $message = ''; + if ($tmp = $def->getType()) { + $message = str_replace(" $tmp::", ' ' . preg_replace('~.*\\\\~', '', $tmp) . '::', $message); } - $message .= $type - ? str_replace("$type::", preg_replace('~.*\\\\~', '', $type) . '::', $e->getMessage()) - : $e->getMessage(); + $message = '[' . $def->getDescriptor() . "]\n" . $message; return $e instanceof ServiceCreationException ? $e->setMessage($message) @@ -458,32 +459,6 @@ private function completeException(\Exception $e, Definition $def): ServiceCreat } - private function entityToString($entity): string - { - $referenceToText = function (Reference $ref): string { - return $ref->isSelf() && $this->currentService - ? '@' . $this->currentService->getName() - : '@' . $ref->getValue(); - }; - if (is_string($entity)) { - return $entity . '::__construct()'; - } elseif ($entity instanceof Reference) { - $entity = $referenceToText($entity); - } elseif (is_array($entity)) { - if (strpos($entity[1], '$') === false) { - $entity[1] .= '()'; - } - if ($entity[0] instanceof Reference) { - $entity[0] = $referenceToText($entity[0]); - } elseif (!is_string($entity[0])) { - return $entity[1]; - } - return implode('::', $entity); - } - return (string) $entity; - } - - private function convertReferences(array $arguments): array { array_walk_recursive($arguments, function (&$val): void { @@ -587,19 +562,19 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab } catch (MissingServiceException $e) { $res = null; } catch (ServiceCreationException $e) { - throw new ServiceCreationException("{$e->getMessage()} (required by $desc)", 0, $e); + throw new ServiceCreationException(sprintf("%s\nRequired by %s.", $e->getMessage(), $desc), 0, $e); } if ($res !== null || $parameter->allowsNull()) { return $res; } elseif (class_exists($class) || interface_exists($class)) { throw new ServiceCreationException(sprintf( - 'Service of type %s required by %s not found. Did you add it to configuration file?', + "Service of type %s required by %s not found.\nDid you add it to configuration file?", $class, $desc )); } else { throw new ServiceCreationException(sprintf( - "Class '%s' required by %s not found. Check the parameter type and 'use' statements.", + "Class '%s' required by %s not found.\nCheck the parameter type and 'use' statements.", $class, $desc )); diff --git a/tests/DI/Compiler.configOverride.phpt b/tests/DI/Compiler.configOverride.phpt index 20669e361..63aa223f2 100644 --- a/tests/DI/Compiler.configOverride.phpt +++ b/tests/DI/Compiler.configOverride.phpt @@ -59,4 +59,5 @@ $compiler->addConfig([ Assert::exception(function () use ($compiler, $class) { $compiler->setClassName($class) ->compile(); -}, DI\InvalidConfigurationException::class, "Service 's3': missing original definition for alteration."); +}, DI\InvalidConfigurationException::class, "[Service 's3'] +Missing original definition for alteration."); diff --git a/tests/DI/Compiler.extensionOverride.errors.phpt b/tests/DI/Compiler.extensionOverride.errors.phpt index a4b942814..945f0e5b5 100644 --- a/tests/DI/Compiler.extensionOverride.errors.phpt +++ b/tests/DI/Compiler.extensionOverride.errors.phpt @@ -21,4 +21,5 @@ services: bad: alteration: yes '); -}, Nette\DI\InvalidConfigurationException::class, "Service 'bad': missing original definition for alteration."); +}, Nette\DI\InvalidConfigurationException::class, "[Service 'bad'] +Missing original definition for alteration."); diff --git a/tests/DI/Compiler.functions.phpt b/tests/DI/Compiler.functions.phpt index b789deded..4bf4ec40f 100644 --- a/tests/DI/Compiler.functions.phpt +++ b/tests/DI/Compiler.functions.phpt @@ -85,4 +85,6 @@ Assert::exception(function () { services: - Service(bool(123, 10)) '); -}, Nette\InvalidStateException::class, 'Service of type Service: Function bool() expects at most 1 parameter, 2 given. (used in Service::__construct())'); +}, Nette\InvalidStateException::class, '[Service of type Service] +Function bool() expects at most 1 parameter, 2 given. +Related to Service::__construct().'); diff --git a/tests/DI/Compiler.generatedFactory.phpt b/tests/DI/Compiler.generatedFactory.phpt index e90a77027..8e5bdab2b 100644 --- a/tests/DI/Compiler.generatedFactory.phpt +++ b/tests/DI/Compiler.generatedFactory.phpt @@ -279,7 +279,8 @@ Assert::exception(function () { $builder->addFactoryDefinition('one') ->setImplement(Bad2::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad2): Type of \$bar in Bad2::create() doesn't match type in Bad1 constructor."); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad2] +Type of \$bar in Bad2::create() doesn't match type in Bad1 constructor."); @@ -300,7 +301,8 @@ Assert::exception(function () { $builder->addFactoryDefinition('one') ->setImplement(Bad4::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Unused parameter \$baz when implementing method Bad4::create(), did you mean \$bar?"); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad4] +Unused parameter \$baz when implementing method Bad4::create(), did you mean \$bar?"); @@ -321,7 +323,8 @@ Assert::exception(function () { $builder->addFactoryDefinition('one') ->setImplement(Bad6::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad6): Unused parameter \$baz when implementing method Bad6::create()."); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad6] +Unused parameter \$baz when implementing method Bad6::create()."); diff --git a/tests/DI/ContainerBuilder.autowiring.novalue.phpt b/tests/DI/ContainerBuilder.autowiring.novalue.phpt index 89a14ff14..63b8361d6 100644 --- a/tests/DI/ContainerBuilder.autowiring.novalue.phpt +++ b/tests/DI/ContainerBuilder.autowiring.novalue.phpt @@ -24,7 +24,8 @@ Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('foo')->setType(Foo::class); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'foo' (type of Foo): Parameter \$x in Foo::__construct() has no class type or default value, so its value must be specified."); +}, Nette\DI\ServiceCreationException::class, "[Service 'foo' of type Foo] +Parameter \$x in Foo::__construct() has no class type or default value, so its value must be specified."); class Bar @@ -38,7 +39,8 @@ Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('foo')->setType(Bar::class); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'foo' (type of Bar): Parameter \$x in Bar::__construct() has no class type or default value, so its value must be specified."); +}, Nette\DI\ServiceCreationException::class, "[Service 'foo' of type Bar] +Parameter \$x in Bar::__construct() has no class type or default value, so its value must be specified."); class Bar2 diff --git a/tests/DI/ContainerBuilder.error.phpt b/tests/DI/ContainerBuilder.error.phpt index 3bef4bab2..46e690429 100644 --- a/tests/DI/ContainerBuilder.error.phpt +++ b/tests/DI/ContainerBuilder.error.phpt @@ -7,6 +7,7 @@ declare(strict_types=1); use Nette\DI; +use Nette\DI\Definitions\Statement; use Tester\Assert; @@ -20,7 +21,8 @@ $builder->addDefinition('one') Assert::exception(function () use ($builder) { $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of stdClass): Expected function, method or property name, '1234' given."); +}, Nette\InvalidStateException::class, "[Service 'one' of type stdClass] +Expected function, method or property name, '1234' given."); @@ -43,4 +45,28 @@ $builder->addDefinition('one') Assert::exception(function () use ($builder) { $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of stdClass): Missing argument for \$prop[]."); +}, Nette\InvalidStateException::class, "[Service 'one' of type stdClass] +Missing argument for \$prop[]."); + + + +$builder = new DI\ContainerBuilder; +$builder->addDefinition(null) + ->setFactory([new Statement('Unknown'), 'foo']); + +Assert::exception(function () use ($builder) { + $builder->complete(); +}, Nette\DI\ServiceCreationException::class, "[Service Unknown()::foo()] +Class 'Unknown' not found."); + + + +$builder = new DI\ContainerBuilder; +$builder->addDefinition(null) + ->setFactory('stdClass') + ->addSetup([new Statement('Unknown'), 'foo']); + +Assert::exception(function () use ($builder) { + $builder->complete(); +}, Nette\DI\ServiceCreationException::class, "[Service of type stdClass] +Class 'Unknown' not found."); diff --git a/tests/DI/ContainerBuilder.factory.error.phpt b/tests/DI/ContainerBuilder.factory.error.phpt index 008081d81..2e3d9d2d6 100644 --- a/tests/DI/ContainerBuilder.factory.error.phpt +++ b/tests/DI/ContainerBuilder.factory.error.phpt @@ -18,14 +18,16 @@ require __DIR__ . '/../bootstrap.php'; Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setType('X')->setFactory('Unknown'); -}, Nette\InvalidArgumentException::class, "Service 'one': Class or interface 'X' not found."); +}, Nette\InvalidArgumentException::class, "[Service 'one'] +Class or interface 'X' not found."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition(null)->setFactory('Unknown'); $builder->complete(); -}, Nette\DI\ServiceCreationException::class, "Service (Unknown::__construct()): Class 'Unknown' not found."); +}, Nette\DI\ServiceCreationException::class, "[Service of type Unknown] +Class 'Unknown' not found."); Assert::exception(function () { @@ -33,7 +35,8 @@ Assert::exception(function () { $builder->addDefinition('one')->setFactory('@two'); $builder->addDefinition('two')->setFactory('Unknown'); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'two': Class 'Unknown' not found."); +}, Nette\InvalidStateException::class, "[Service 'two'] +Class 'Unknown' not found."); Assert::exception(function () { @@ -41,28 +44,32 @@ Assert::exception(function () { $builder->addDefinition('one')->setFactory(new Reference('two')); $builder->addDefinition('two')->setFactory('Unknown'); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'two': Class 'Unknown' not found."); +}, Nette\InvalidStateException::class, "[Service 'two'] +Class 'Unknown' not found."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setFactory('stdClass::foo'); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one': Method stdClass::foo() is not callable."); +}, Nette\InvalidStateException::class, "[Service 'one'] +Method stdClass::foo() is not callable."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setFactory('Nette\DI\Container::foo'); // has __magic $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one': Method Nette\\DI\\Container::foo() is not callable."); +}, Nette\InvalidStateException::class, "[Service 'one'] +Method Nette\\DI\\Container::foo() is not callable."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addFactoryDefinition('one') ->setImplement('Unknown'); -}, Nette\InvalidArgumentException::class, "Service 'one': Interface 'Unknown' not found."); +}, Nette\InvalidArgumentException::class, "[Service 'one'] +Interface 'Unknown' not found."); @@ -76,7 +83,8 @@ Assert::exception(function () { @$builder->addFactoryDefinition('one') ->setImplement(Bad4::class); // missing type triggers warning $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Return type of Bad4::create() is not declared."); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad4] +Return type of Bad4::create() is not declared."); interface Bad5 @@ -89,7 +97,8 @@ Assert::exception(function () { $builder->addAccessorDefinition('one') ->setImplement(Bad5::class); $builder->complete(); -}, Nette\InvalidArgumentException::class, "Service 'one': Method Bad5::get() must have no parameters."); +}, Nette\InvalidArgumentException::class, "[Service 'one'] +Method Bad5::get() must have no parameters."); class Bad6 @@ -103,7 +112,8 @@ Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setFactory('Bad6::create'); $builder->complete(); -}, Nette\DI\ServiceCreationException::class, "Service 'one': Method Bad6::create() is not callable."); +}, Nette\DI\ServiceCreationException::class, "[Service 'one'] +Method Bad6::create() is not callable."); class Bad7 @@ -117,7 +127,8 @@ Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setFactory('Bad7::create'); $builder->complete(); -}, Nette\DI\ServiceCreationException::class, "Service 'one': Unknown service type, specify it or declare return type of factory."); +}, Nette\DI\ServiceCreationException::class, "[Service 'one'] +Unknown service type, specify it or declare return type of factory."); class Bad8 @@ -131,7 +142,8 @@ Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setType(Bad8::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad8): Class Bad8 has private constructor."); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad8] +Class Bad8 has private constructor."); class Good @@ -146,14 +158,18 @@ Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setFactory(Good::class, [new Statement('Unknown')]); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Good): Class 'Unknown' not found. (used in Good::__construct())"); +}, Nette\InvalidStateException::class, "[Service 'one' of type Good] +Class 'Unknown' not found. +Related to Good::__construct()."); // fail in argument Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setFactory(Good::class, [new Statement(Bad8::class)]); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Good): Class Bad8 has private constructor. (used in Good::__construct())"); +}, Nette\InvalidStateException::class, "[Service 'one' of type Good] +Class Bad8 has private constructor. +Related to Good::__construct()."); abstract class Bad9 @@ -168,7 +184,8 @@ Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setType(Bad9::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad9): Class Bad9 is abstract."); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad9] +Class Bad9 is abstract."); trait Bad10 @@ -183,7 +200,8 @@ Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setFactory('Bad10::method'); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one': Method Bad10::method() is not callable."); +}, Nette\InvalidStateException::class, "[Service 'one'] +Method Bad10::method() is not callable."); class ConstructorParam @@ -208,7 +226,9 @@ services: b: stdClass bad: ConstructorParam '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of ConstructorParam): Multiple services of type stdClass found: a, b (required by \$x in ConstructorParam::__construct())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type ConstructorParam] +Multiple services of type stdClass found: a, b +Required by \$x in ConstructorParam::__construct()."); // forced autowiring fail @@ -219,7 +239,9 @@ services: b: stdClass bad: ConstructorParam(@\stdClass) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of ConstructorParam): Multiple services of type stdClass found: a, b (used in ConstructorParam::__construct())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type ConstructorParam] +Multiple services of type stdClass found: a, b +Related to ConstructorParam::__construct()."); // autowiring fail in chain @@ -230,7 +252,9 @@ services: b: stdClass bad: MethodParam()::foo() '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of MethodParam): Multiple services of type stdClass found: a, b (required by \$x in MethodParam::foo())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type MethodParam] +Multiple services of type stdClass found: a, b +Required by \$x in MethodParam::foo()."); // forced autowiring fail in chain @@ -241,7 +265,9 @@ services: b: stdClass bad: MethodParam()::foo(@\stdClass) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of MethodParam): Multiple services of type stdClass found: a, b (used in foo())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type MethodParam] +Multiple services of type stdClass found: a, b +Related to MethodParam()::foo()."); // autowiring fail in argument @@ -252,7 +278,10 @@ services: b: stdClass bad: Good(ConstructorParam()) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (required by \$x in ConstructorParam::__construct()) (used in Good::__construct())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type Good] +Multiple services of type stdClass found: a, b +Required by \$x in ConstructorParam::__construct(). +Related to Good::__construct()."); // forced autowiring fail in argument @@ -263,7 +292,9 @@ services: b: stdClass bad: Good(ConstructorParam(@\stdClass)) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (used in ConstructorParam::__construct())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type Good] +Multiple services of type stdClass found: a, b +Related to ConstructorParam::__construct()."); // autowiring fail in chain in argument @@ -274,7 +305,10 @@ services: b: stdClass bad: Good(MethodParam()::foo()) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (required by \$x in MethodParam::foo()) (used in Good::__construct())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type Good] +Multiple services of type stdClass found: a, b +Required by \$x in MethodParam::foo(). +Related to Good::__construct()."); // forced autowiring fail in chain in argument @@ -285,7 +319,9 @@ services: b: stdClass bad: Good(MethodParam()::foo(@\stdClass)) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (used in foo())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type Good] +Multiple services of type stdClass found: a, b +Related to MethodParam()::foo()."); // forced autowiring fail in property passing @@ -299,7 +335,9 @@ services: setup: - $a = @\stdClass '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (used in @bad::\$a)"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type Good] +Multiple services of type stdClass found: a, b +Related to \$a in setup."); // autowiring fail in rich property passing @@ -313,7 +351,9 @@ services: setup: - $a = MethodParam()::foo(@\stdClass) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (used in foo())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type Good] +Multiple services of type stdClass found: a, b +Related to MethodParam()::foo() in setup."); // autowiring fail in method calling @@ -327,7 +367,9 @@ services: setup: - foo '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of MethodParam): Multiple services of type stdClass found: a, b (required by \$x in MethodParam::foo())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type MethodParam] +Multiple services of type stdClass found: a, b +Required by \$x in MethodParam::foo()."); // forced autowiring fail in method calling @@ -341,7 +383,9 @@ services: setup: - bar(@\stdClass) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (used in @bad::bar())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type Good] +Multiple services of type stdClass found: a, b +Related to bar() in setup."); // autowiring fail in rich method calling @@ -355,4 +399,6 @@ services: setup: - bar(MethodParam()::foo(@\stdClass)) '); -}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (used in foo())"); +}, Nette\DI\ServiceCreationException::class, "[Service 'bad' of type Good] +Multiple services of type stdClass found: a, b +Related to MethodParam()::foo() in setup."); diff --git a/tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt b/tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt index aae931c9b..75790052a 100644 --- a/tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt +++ b/tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt @@ -82,7 +82,8 @@ namespace $builder->addDefinition('a') ->setFactory('@factory::createArray'); $container = @createContainer($builder); // annotation @return is deprecated - }, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of A\\Factory::createArray() is not expected to be nullable/union/intersection/built-in, 'array' given."); + }, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of A\\Factory::createArray() is not expected to be nullable/union/intersection/built-in, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; @@ -91,7 +92,8 @@ namespace $builder->addDefinition('c') ->setFactory('@factory::createCallable'); $container = @createContainer($builder); // annotation @return is deprecated - }, Nette\DI\ServiceCreationException::class, "Service 'c': Return type of A\\Factory::createCallable() is not expected to be nullable/union/intersection/built-in, 'callable' given."); + }, Nette\DI\ServiceCreationException::class, "[Service 'c'] +Return type of A\\Factory::createCallable() is not expected to be nullable/union/intersection/built-in, 'callable' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; @@ -100,7 +102,8 @@ namespace $builder->addDefinition('s') ->setFactory('@factory::createString'); $container = @createContainer($builder); // annotation @return is deprecated - }, Nette\DI\ServiceCreationException::class, "Service 's': Return type of A\\Factory::createString() is not expected to be nullable/union/intersection/built-in, 'string' given."); + }, Nette\DI\ServiceCreationException::class, "[Service 's'] +Return type of A\\Factory::createString() is not expected to be nullable/union/intersection/built-in, 'string' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; @@ -109,7 +112,8 @@ namespace $builder->addDefinition('i') ->setFactory('@factory::createInt'); $container = @createContainer($builder); // annotation @return is deprecated - }, Nette\DI\ServiceCreationException::class, "Service 'i': Return type of A\\Factory::createInt() is not expected to be nullable/union/intersection/built-in, 'int' given."); + }, Nette\DI\ServiceCreationException::class, "[Service 'i'] +Return type of A\\Factory::createInt() is not expected to be nullable/union/intersection/built-in, 'int' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; @@ -118,7 +122,8 @@ namespace $builder->addDefinition('b') ->setFactory('@factory::createBool'); $container = @createContainer($builder); // annotation @return is deprecated - }, Nette\DI\ServiceCreationException::class, "Service 'b': Return type of A\\Factory::createBool() is not expected to be nullable/union/intersection/built-in, 'bool' given."); + }, Nette\DI\ServiceCreationException::class, "[Service 'b'] +Return type of A\\Factory::createBool() is not expected to be nullable/union/intersection/built-in, 'bool' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; @@ -127,7 +132,8 @@ namespace $builder->addDefinition('f') ->setFactory('@factory::createFloat'); $container = @createContainer($builder); // annotation @return is deprecated - }, Nette\DI\ServiceCreationException::class, "Service 'f': Return type of A\\Factory::createFloat() is not expected to be nullable/union/intersection/built-in, 'float' given."); + }, Nette\DI\ServiceCreationException::class, "[Service 'f'] +Return type of A\\Factory::createFloat() is not expected to be nullable/union/intersection/built-in, 'float' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; @@ -136,7 +142,8 @@ namespace $builder->addDefinition('f') ->setFactory('@factory::createObject'); $container = @createContainer($builder); // annotation @return is deprecated - }, Nette\DI\ServiceCreationException::class, "Service 'f': Unknown service type, specify it or declare return type of factory."); + }, Nette\DI\ServiceCreationException::class, "[Service 'f'] +Unknown service type, specify it or declare return type of factory."); Assert::exception(function () { $builder = new DI\ContainerBuilder; @@ -145,6 +152,7 @@ namespace $builder->addDefinition('f') ->setFactory('@factory::createMixed'); $container = @createContainer($builder); // annotation @return is deprecated - }, Nette\DI\ServiceCreationException::class, "Service 'f': Unknown service type, specify it or declare return type of factory."); + }, Nette\DI\ServiceCreationException::class, "[Service 'f'] +Unknown service type, specify it or declare return type of factory."); } diff --git a/tests/DI/ContainerBuilder.recursive.phpt b/tests/DI/ContainerBuilder.recursive.phpt index 50ca69c71..fa3ea3cc6 100644 --- a/tests/DI/ContainerBuilder.recursive.phpt +++ b/tests/DI/ContainerBuilder.recursive.phpt @@ -29,4 +29,5 @@ $builder->addDefinition('two') Assert::exception(function () use ($builder) { $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'two': Circular reference detected for services: one, two."); +}, Nette\DI\ServiceCreationException::class, "[Service 'two'] +Circular reference detected for services: one, two."); diff --git a/tests/DI/ContainerBuilder.selfdependency.phpt b/tests/DI/ContainerBuilder.selfdependency.phpt index 7230c1925..498178a20 100644 --- a/tests/DI/ContainerBuilder.selfdependency.phpt +++ b/tests/DI/ContainerBuilder.selfdependency.phpt @@ -27,4 +27,6 @@ $builder->addDefinition(null) Assert::exception(function () use ($builder) { createContainer($builder); -}, Nette\DI\ServiceCreationException::class, 'Service of type Foo: Service of type Foo required by $foo in Foo::__construct() not found. Did you add it to configuration file?'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Foo] +Service of type Foo required by $foo in Foo::__construct() not found. +Did you add it to configuration file?'); diff --git a/tests/DI/Definitions.AccessorDefinition.api.phpt b/tests/DI/Definitions.AccessorDefinition.api.phpt index ff3a9fbf1..47f7d7436 100644 --- a/tests/DI/Definitions.AccessorDefinition.api.phpt +++ b/tests/DI/Definitions.AccessorDefinition.api.phpt @@ -55,43 +55,50 @@ Assert::exception(function () { Assert::exception(function () { $def = new AccessorDefinition; $def->setImplement('Foo'); -}, Nette\InvalidArgumentException::class, "Service '': Interface 'Foo' not found."); +}, Nette\InvalidArgumentException::class, "[Service ?] +Interface 'Foo' not found."); Assert::exception(function () { $def = new AccessorDefinition; $def->setImplement(stdClass::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface 'stdClass' not found."); +}, Nette\InvalidArgumentException::class, "[Service ?] +Interface 'stdClass' not found."); Assert::exception(function () { $def = new AccessorDefinition; $def->setImplement(Bad1::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad1 must have just one non-static method get()."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad1 must have just one non-static method get().'); Assert::exception(function () { $def = new AccessorDefinition; $def->setImplement(Bad2::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad2 must have just one non-static method get()."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad2 must have just one non-static method get().'); Assert::exception(function () { $def = new AccessorDefinition; $def->setImplement(Bad3::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad3 must have just one non-static method get()."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad3 must have just one non-static method get().'); Assert::exception(function () { $def = new AccessorDefinition; $def->setImplement(Bad4::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad4 must have just one non-static method get()."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad4 must have just one non-static method get().'); Assert::exception(function () { $def = new AccessorDefinition; $def->setImplement(Bad5::class); -}, Nette\InvalidArgumentException::class, "Service '': Method Bad5::get() must have no parameters."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad5::get() must have no parameters.'); Assert::noError(function () { diff --git a/tests/DI/Definitions.AccessorDefinition.resolve.phpt b/tests/DI/Definitions.AccessorDefinition.resolve.phpt index 5c7270a1f..10752fe8c 100644 --- a/tests/DI/Definitions.AccessorDefinition.resolve.phpt +++ b/tests/DI/Definitions.AccessorDefinition.resolve.phpt @@ -28,7 +28,8 @@ Assert::exception(function () { $def = new AccessorDefinition; $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Type of service is unknown.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Type of service is unknown.'); Assert::exception(function () { @@ -37,7 +38,8 @@ Assert::exception(function () { $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); $resolver->completeDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Service of type Bad1: Return type of Bad1::get() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Bad1] +Return type of Bad1::get() is not declared.'); Assert::noError(function () { @@ -66,4 +68,5 @@ Assert::exception(function () { $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); $resolver->completeDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Service of type Good1: Service of type stdClass not found. Did you add it to configuration file?'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Good1] +Service of type stdClass not found. Did you add it to configuration file?'); diff --git a/tests/DI/Definitions.FactoryDefinition.api.phpt b/tests/DI/Definitions.FactoryDefinition.api.phpt index 526aba439..f05b610a8 100644 --- a/tests/DI/Definitions.FactoryDefinition.api.phpt +++ b/tests/DI/Definitions.FactoryDefinition.api.phpt @@ -49,37 +49,43 @@ Assert::exception(function () { Assert::exception(function () { $def = new FactoryDefinition; $def->setImplement('Foo'); -}, Nette\InvalidArgumentException::class, "Service '': Interface 'Foo' not found."); +}, Nette\InvalidArgumentException::class, "[Service ?] +Interface 'Foo' not found."); Assert::exception(function () { $def = new FactoryDefinition; $def->setImplement(stdClass::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface 'stdClass' not found."); +}, Nette\InvalidArgumentException::class, "[Service ?] +Interface 'stdClass' not found."); Assert::exception(function () { $def = new FactoryDefinition; $def->setImplement(Bad1::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad1 must have just one non-static method create()."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad1 must have just one non-static method create().'); Assert::exception(function () { $def = new FactoryDefinition; $def->setImplement(Bad2::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad2 must have just one non-static method create()."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad2 must have just one non-static method create().'); Assert::exception(function () { $def = new FactoryDefinition; $def->setImplement(Bad3::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad3 must have just one non-static method create()."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad3 must have just one non-static method create().'); Assert::exception(function () { $def = new FactoryDefinition; $def->setImplement(Bad4::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad4 must have just one non-static method create()."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad4 must have just one non-static method create().'); Assert::noError(function () { diff --git a/tests/DI/Definitions.FactoryDefinition.resolve.phpt b/tests/DI/Definitions.FactoryDefinition.resolve.phpt index ea31b381c..a986c39e3 100644 --- a/tests/DI/Definitions.FactoryDefinition.resolve.phpt +++ b/tests/DI/Definitions.FactoryDefinition.resolve.phpt @@ -28,7 +28,8 @@ Assert::exception(function () { $def = new FactoryDefinition; $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Type is missing in definition of service.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Type is missing in definition of service.'); Assert::exception(function () { @@ -36,7 +37,8 @@ Assert::exception(function () { @$def->setImplement(Bad1::class); // missing type triggers warning $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Service of type Bad1: Return type of Bad1::create() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Bad1] +Return type of Bad1::create() is not declared.'); Assert::noError(function () { @@ -77,4 +79,5 @@ Assert::exception(function () { $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Service of type Good1: Factory for stdClass cannot create incompatible DateTime type.'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Good1] +Factory for stdClass cannot create incompatible DateTime type.'); diff --git a/tests/DI/Definitions.ImportedDefinition.phpt b/tests/DI/Definitions.ImportedDefinition.phpt index 1ff490e4e..b63eb2564 100644 --- a/tests/DI/Definitions.ImportedDefinition.phpt +++ b/tests/DI/Definitions.ImportedDefinition.phpt @@ -16,14 +16,16 @@ require __DIR__ . '/../bootstrap.php'; Assert::exception(function () { $def = new ImportedDefinition; $def->setType('Foo'); -}, Nette\InvalidArgumentException::class, "Service '': Class or interface 'Foo' not found."); +}, Nette\InvalidArgumentException::class, "[Service ?] +Class or interface 'Foo' not found."); Assert::exception(function () { $def = new ImportedDefinition; $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Type of service is unknown.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Type of service is unknown.'); test('', function () { diff --git a/tests/DI/Definitions.LocatorDefinition.api.phpt b/tests/DI/Definitions.LocatorDefinition.api.phpt index 25068cf2f..b1d3d13c8 100644 --- a/tests/DI/Definitions.LocatorDefinition.api.phpt +++ b/tests/DI/Definitions.LocatorDefinition.api.phpt @@ -72,49 +72,57 @@ Assert::exception(function () { Assert::exception(function () { $def = new LocatorDefinition; $def->setImplement('Foo'); -}, Nette\InvalidArgumentException::class, "Service '': Interface 'Foo' not found."); +}, Nette\InvalidArgumentException::class, "[Service ?] +Interface 'Foo' not found."); Assert::exception(function () { $def = new LocatorDefinition; $def->setImplement(stdClass::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface 'stdClass' not found."); +}, Nette\InvalidArgumentException::class, "[Service ?] +Interface 'stdClass' not found."); Assert::exception(function () { $def = new LocatorDefinition; $def->setImplement(Bad1::class); -}, Nette\InvalidArgumentException::class, "Service '': Interface Bad1 must have at least one method."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Interface Bad1 must have at least one method.'); Assert::exception(function () { $def = new LocatorDefinition; $def->setImplement(Bad2::class); -}, Nette\InvalidArgumentException::class, "Service '': Method Bad2::create() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad2::create() does not meet the requirements: is create($name), get($name), create*() or get*() and is non-static.'); Assert::exception(function () { $def = new LocatorDefinition; $def->setImplement(Bad3::class); -}, Nette\InvalidArgumentException::class, "Service '': Method Bad3::get() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad3::get() does not meet the requirements: is create($name), get($name), create*() or get*() and is non-static.'); Assert::exception(function () { $def = new LocatorDefinition; $def->setImplement(Bad4::class); -}, Nette\InvalidArgumentException::class, "Service '': Method Bad4::foo() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad4::foo() does not meet the requirements: is create($name), get($name), create*() or get*() and is non-static.'); Assert::exception(function () { $def = new LocatorDefinition; $def->setImplement(Bad5::class); -}, Nette\InvalidArgumentException::class, "Service '': Method Bad5::get() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad5::get() does not meet the requirements: is create($name), get($name), create*() or get*() and is non-static.'); Assert::exception(function () { $def = new LocatorDefinition; $def->setImplement(Bad6::class); -}, Nette\InvalidArgumentException::class, "Service '': Method Bad6::get() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad6::get() does not meet the requirements: is create($name), get($name), create*() or get*() and is non-static.'); Assert::noError(function () { diff --git a/tests/DI/Definitions.LocatorDefinition.resolve.phpt b/tests/DI/Definitions.LocatorDefinition.resolve.phpt index 2a12fbf54..45714e3a8 100644 --- a/tests/DI/Definitions.LocatorDefinition.resolve.phpt +++ b/tests/DI/Definitions.LocatorDefinition.resolve.phpt @@ -33,7 +33,8 @@ Assert::exception(function () { $def = new LocatorDefinition; $resolver = new Nette\DI\Resolver(new Nette\DI\ContainerBuilder); $resolver->resolveDefinition($def); -}, Nette\DI\ServiceCreationException::class, 'Type of service is unknown.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Type of service is unknown.'); test('', function () { diff --git a/tests/DI/Definitions.ServiceDefinition.phpt b/tests/DI/Definitions.ServiceDefinition.phpt index a458f38ff..93606fdde 100644 --- a/tests/DI/Definitions.ServiceDefinition.phpt +++ b/tests/DI/Definitions.ServiceDefinition.phpt @@ -17,7 +17,8 @@ require __DIR__ . '/../bootstrap.php'; Assert::exception(function () { $def = new ServiceDefinition; $def->setType('Foo'); -}, Nette\InvalidArgumentException::class, "Service '': Class or interface 'Foo' not found."); +}, Nette\InvalidArgumentException::class, "[Service ?] +Class or interface 'Foo' not found."); test('', function () { $def = new ServiceDefinition; diff --git a/tests/DI/InjectExtension.errors.phpt b/tests/DI/InjectExtension.errors.phpt index e45b665d3..12bc18457 100644 --- a/tests/DI/InjectExtension.errors.phpt +++ b/tests/DI/InjectExtension.errors.phpt @@ -58,7 +58,9 @@ services: factory: ServiceA inject: yes '); -}, InvalidStateException::class, "Service 'service' (type of ServiceA): Service of type DateTimeImmutable not found. Did you add it to configuration file?"); +}, InvalidStateException::class, "[Service 'service' of type ServiceA] +Service of type DateTimeImmutable required by ServiceA::\$a not found. +Did you add it to configuration file?"); Assert::exception(function () { @@ -72,6 +74,9 @@ services: '); }, InvalidStateException::class, "Class 'Unknown' not found. Check the type of property ServiceB::\$a."); +// }, InvalidStateException::class, "[Service 'service' of type ServiceB] +// Class 'Unknown' required by ServiceB::\$a not found. +// Check the property type and 'use' statements."); Assert::exception(function () { @@ -84,6 +89,8 @@ services: inject: yes '); }, InvalidStateException::class, 'Type of property ServiceC::$a is not declared.'); +//}, InvalidStateException::class, "[Service 'service' of type ServiceC] +//Property ServiceC::\$a has no type."); Assert::error(function () { diff --git a/tests/DI/Resolver.autowireArguments.errors.phpt b/tests/DI/Resolver.autowireArguments.errors.phpt index fdb70ecd6..c05135c65 100644 --- a/tests/DI/Resolver.autowireArguments.errors.phpt +++ b/tests/DI/Resolver.autowireArguments.errors.phpt @@ -15,12 +15,14 @@ require __DIR__ . '/../bootstrap.php'; Assert::exception(function () { Resolver::autowireArguments(new ReflectionFunction(function (stdClass $x) {}), [], function () {}); -}, Nette\DI\ServiceCreationException::class, 'Service of type stdClass required by $x in {closure}() not found. Did you add it to configuration file?'); +}, Nette\DI\ServiceCreationException::class, 'Service of type stdClass required by $x in {closure}() not found. +Did you add it to configuration file?'); Assert::exception(function () { Resolver::autowireArguments(new ReflectionFunction(function (Foo $x) {}), [], function () {}); -}, Nette\DI\ServiceCreationException::class, "Class 'Foo' required by \$x in {closure}() not found. Check the parameter type and 'use' statements."); +}, Nette\DI\ServiceCreationException::class, "Class 'Foo' required by \$x in {closure}() not found. +Check the parameter type and 'use' statements."); Assert::exception(function () { From 690cb8328da85d5ac567ef66cb525a0cf5dfb3ce Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 20 Oct 2021 00:16:55 +0200 Subject: [PATCH 16/18] Three ... dots are deprecated, triggers notices --- src/DI/Extensions/DecoratorExtension.php | 4 ++-- src/DI/Extensions/ParametersExtension.php | 2 +- src/DI/Extensions/ServicesExtension.php | 14 ++++++++------ src/DI/Helpers.php | 9 +++++---- tests/DI/Compiler.services.autowiring.phpt | 2 +- tests/DI/Helpers.filterArguments.phpt | 4 ++-- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/DI/Extensions/DecoratorExtension.php b/src/DI/Extensions/DecoratorExtension.php index 02cfa2940..9c80b0cb3 100644 --- a/src/DI/Extensions/DecoratorExtension.php +++ b/src/DI/Extensions/DecoratorExtension.php @@ -40,8 +40,8 @@ public function beforeCompile() if ($info->inject !== null) { $info->tags[InjectExtension::TAG_INJECT] = $info->inject; } - $this->addSetups($type, Nette\DI\Helpers::filterArguments($info->setup)); - $this->addTags($type, Nette\DI\Helpers::filterArguments($info->tags)); + $this->addSetups($type, Nette\DI\Helpers::filterArguments($info->setup, 'decorator')); + $this->addTags($type, Nette\DI\Helpers::filterArguments($info->tags, 'decorator')); } } diff --git a/src/DI/Extensions/ParametersExtension.php b/src/DI/Extensions/ParametersExtension.php index 0625ef805..52cb52454 100644 --- a/src/DI/Extensions/ParametersExtension.php +++ b/src/DI/Extensions/ParametersExtension.php @@ -43,7 +43,7 @@ public function loadConfiguration() foreach ($this->dynamicParams as $key) { $params[$key] = array_key_exists($key, $params) - ? new DynamicParameter($generator->formatPhp('($this->parameters[?] \?\? ?)', $resolver->completeArguments(Nette\DI\Helpers::filterArguments([$key, $params[$key]])))) + ? new DynamicParameter($generator->formatPhp('($this->parameters[?] \?\? ?)', $resolver->completeArguments(Nette\DI\Helpers::filterArguments([$key, $params[$key]], 'parameters')))) : new DynamicParameter((new Nette\PhpGenerator\Dumper)->format('$this->parameters[?]', $key)); } diff --git a/src/DI/Extensions/ServicesExtension.php b/src/DI/Extensions/ServicesExtension.php index fbef73ac1..1134ac6a9 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -85,8 +85,9 @@ private function loadDefinition(?string $name, \stdClass $config): void */ private function updateServiceDefinition(Definitions\ServiceDefinition $definition, \stdClass $config): void { + $desc = $definition->getDescriptor(); if ($config->create) { - $definition->setFactory(Helpers::filterArguments([$config->create])[0]); + $definition->setFactory(Helpers::filterArguments([$config->create], $desc)[0]); $definition->setType(null); } @@ -95,7 +96,7 @@ private function updateServiceDefinition(Definitions\ServiceDefinition $definiti } if ($config->arguments) { - $arguments = Helpers::filterArguments($config->arguments); + $arguments = Helpers::filterArguments($config->arguments, $desc); if (empty($config->reset['arguments']) && !Nette\Utils\Arrays::isList($arguments)) { $arguments += $definition->getFactory()->arguments; } @@ -106,7 +107,7 @@ private function updateServiceDefinition(Definitions\ServiceDefinition $definiti if (!empty($config->reset['setup'])) { $definition->setSetup([]); } - foreach (Helpers::filterArguments($config->setup) as $id => $setup) { + foreach (Helpers::filterArguments($config->setup, $desc) as $id => $setup) { if (is_array($setup)) { $setup = new Statement(key($setup), array_values($setup)); } @@ -135,6 +136,7 @@ private function updateAccessorDefinition(Definitions\AccessorDefinition $defini private function updateFactoryDefinition(Definitions\FactoryDefinition $definition, \stdClass $config): void { $resultDef = $definition->getResultDefinition(); + $desc = $definition->getDescriptor(); if (isset($config->implement)) { $definition->setImplement($config->implement); @@ -142,7 +144,7 @@ private function updateFactoryDefinition(Definitions\FactoryDefinition $definiti } if ($config->create) { - $resultDef->setFactory(Helpers::filterArguments([$config->create])[0]); + $resultDef->setFactory(Helpers::filterArguments([$config->create], $desc)[0]); } if ($config->type) { @@ -150,7 +152,7 @@ private function updateFactoryDefinition(Definitions\FactoryDefinition $definiti } if ($config->arguments) { - $arguments = Helpers::filterArguments($config->arguments); + $arguments = Helpers::filterArguments($config->arguments, $desc); if (empty($config->reset['arguments']) && !Nette\Utils\Arrays::isList($arguments)) { $arguments += $resultDef->getFactory()->arguments; } @@ -161,7 +163,7 @@ private function updateFactoryDefinition(Definitions\FactoryDefinition $definiti if (!empty($config->reset['setup'])) { $resultDef->setSetup([]); } - foreach (Helpers::filterArguments($config->setup) as $id => $setup) { + foreach (Helpers::filterArguments($config->setup, $desc) as $id => $setup) { if (is_array($setup)) { $setup = new Statement(key($setup), array_values($setup)); } diff --git a/src/DI/Helpers.php b/src/DI/Helpers.php index 2c7ae2e2e..6585cf509 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -130,10 +130,11 @@ public static function escape($value) /** * Removes ... and process constants recursively. */ - public static function filterArguments(array $args): array + public static function filterArguments(array $args, string $hint = ''): array { foreach ($args as $k => $v) { if ($v === '...') { + trigger_error("[$hint] Replace ... with _ in configuration file.", E_USER_DEPRECATED); unset($args[$k]); } elseif ( PHP_VERSION_ID >= 80100 @@ -147,10 +148,10 @@ public static function filterArguments(array $args): array } elseif (is_string($v) && preg_match('#^@[\w\\\\]+$#D', $v)) { $args[$k] = new Reference(substr($v, 1)); } elseif (is_array($v)) { - $args[$k] = self::filterArguments($v); + $args[$k] = self::filterArguments($v, $hint); } elseif ($v instanceof Statement) { - [$tmp] = self::filterArguments([$v->getEntity()]); - $args[$k] = new Statement($tmp, self::filterArguments($v->arguments)); + [$tmp] = self::filterArguments([$v->getEntity()], $hint); + $args[$k] = new Statement($tmp, self::filterArguments($v->arguments, $hint)); } } return $args; diff --git a/tests/DI/Compiler.services.autowiring.phpt b/tests/DI/Compiler.services.autowiring.phpt index c49ce65d9..ec043dcd2 100644 --- a/tests/DI/Compiler.services.autowiring.phpt +++ b/tests/DI/Compiler.services.autowiring.phpt @@ -52,7 +52,7 @@ services: factory: Factory()::createModel setup: # local methods - - test(...) + - test(_) - @model::test() - @self::test() diff --git a/tests/DI/Helpers.filterArguments.phpt b/tests/DI/Helpers.filterArguments.phpt index 35cdf7a39..d3e4e4523 100644 --- a/tests/DI/Helpers.filterArguments.phpt +++ b/tests/DI/Helpers.filterArguments.phpt @@ -18,7 +18,7 @@ Assert::same([], Helpers::filterArguments([])); Assert::same( ['a', 'b', 3 => ['c'], [1 => 'd']], - Helpers::filterArguments(['a', 'b', '...', ['c', '...'], ['...', 'd']]) + @Helpers::filterArguments(['a', 'b', '...', ['c', '...'], ['...', 'd']]) // ... is deprecated ); Assert::same( @@ -33,5 +33,5 @@ Assert::equal( Assert::equal( [new Statement('class', ['a', 2 => Nette\DI\ContainerBuilder::THIS_CONTAINER])], - Helpers::filterArguments([new Statement('class', ['a', '...', 'Nette\DI\ContainerBuilder::THIS_CONTAINER'])]) + @Helpers::filterArguments([new Statement('class', ['a', '...', 'Nette\DI\ContainerBuilder::THIS_CONTAINER'])]) // ... is deprecated ); From f2f3e1311e225fa27d2affc179509421a386a2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Barto=C5=A1?= Date: Sun, 21 Nov 2021 22:49:19 +0100 Subject: [PATCH 17/18] Container: add typehints to inherited properties --- src/DI/Container.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DI/Container.php b/src/DI/Container.php index d7df4c98e..3081bdb39 100644 --- a/src/DI/Container.php +++ b/src/DI/Container.php @@ -19,7 +19,7 @@ class Container { use Nette\SmartObject; - /** @var array user parameters */ + /** @var array user parameters */ public $parameters = []; /** @var string[] services name => type (complete list of available services) */ @@ -28,10 +28,10 @@ class Container /** @var string[] alias => service name */ protected $aliases = []; - /** @var array[] tag name => service name => tag value */ + /** @var array> tag name => service name => tag value */ protected $tags = []; - /** @var array[] type => level => services */ + /** @var array>> type => level => services */ protected $wiring = []; /** @var object[] service name => instance */ From e6f9c3d38e6e804cc190f653c4a4055beed9d27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Barto=C5=A1?= Date: Sun, 21 Nov 2021 22:54:31 +0100 Subject: [PATCH 18/18] Definitions: add container property type --- src/DI/Definitions/AccessorDefinition.php | 8 ++++++-- src/DI/Definitions/FactoryDefinition.php | 8 ++++++-- src/DI/Definitions/LocatorDefinition.php | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/DI/Definitions/AccessorDefinition.php b/src/DI/Definitions/AccessorDefinition.php index 6a364f70b..cf17d80d3 100644 --- a/src/DI/Definitions/AccessorDefinition.php +++ b/src/DI/Definitions/AccessorDefinition.php @@ -117,13 +117,17 @@ public function generateMethod(Nette\PhpGenerator\Method $method, Nette\DI\PhpGe $class = (new Nette\PhpGenerator\ClassType) ->addImplement($this->getType()); - $class->addProperty('container') + $containerType = $generator->getClassName(); + $container = $class->addProperty('container') ->setPrivate(); + if (PHP_VERSION_ID >= 74000) { + $container->setType($containerType); + } $class->addMethod('__construct') ->addBody('$this->container = $container;') ->addParameter('container') - ->setType($generator->getClassName()); + ->setType($containerType); $rm = new \ReflectionMethod($this->getType(), self::METHOD_GET); diff --git a/src/DI/Definitions/FactoryDefinition.php b/src/DI/Definitions/FactoryDefinition.php index 200dde667..91ce0139a 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -241,13 +241,17 @@ public function generateMethod(Php\Method $method, Nette\DI\PhpGenerator $genera $class = (new Php\ClassType) ->addImplement($this->getType()); - $class->addProperty('container') + $containerType = $generator->getClassName(); + $container = $class->addProperty('container') ->setPrivate(); + if (PHP_VERSION_ID >= 74000) { + $container->setType($containerType); + } $class->addMethod('__construct') ->addBody('$this->container = $container;') ->addParameter('container') - ->setType($generator->getClassName()); + ->setType($containerType); $methodCreate = $class->addMethod(self::METHOD_CREATE); $this->resultDefinition->generateMethod($methodCreate, $generator); diff --git a/src/DI/Definitions/LocatorDefinition.php b/src/DI/Definitions/LocatorDefinition.php index 86d53b773..947a3d926 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -132,13 +132,17 @@ public function generateMethod(Nette\PhpGenerator\Method $method, Nette\DI\PhpGe $class = (new Nette\PhpGenerator\ClassType) ->addImplement($this->getType()); - $class->addProperty('container') + $containerType = $generator->getClassName(); + $container = $class->addProperty('container') ->setPrivate(); + if (PHP_VERSION_ID >= 74000) { + $container->setType($containerType); + } $class->addMethod('__construct') ->addBody('$this->container = $container;') ->addParameter('container') - ->setType($generator->getClassName()); + ->setType($containerType); foreach ((new \ReflectionClass($this->getType()))->getMethods() as $rm) { preg_match('#^(get|create)(.*)#', $rm->name, $m);