From 26a4eec4b7ac0316448cc756c9494a48b725bbd0 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 4 Feb 2024 23:21:07 +0100 Subject: [PATCH 01/26] FactoryDefinition: result definition is added to container [WIP] --- src/DI/Definitions/FactoryDefinition.php | 5 +++++ src/DI/Extensions/DecoratorExtension.php | 18 ++---------------- src/DI/Extensions/InjectExtension.php | 9 ++------- src/DI/Extensions/ServicesExtension.php | 2 +- tests/DI/DecoratorExtension.factories.phpt | 16 ++++++++-------- tests/SearchExtension/all.phpt | 1 + tests/SearchExtension/batches.phpt | 1 + tests/SearchExtension/classes.phpt | 3 +++ tests/SearchExtension/duplicates.phpt | 1 + tests/SearchExtension/files.phpt | 4 ++++ tests/SearchExtension/parents.phpt | 2 ++ tests/SearchExtension/tags.phpt | 2 ++ 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/DI/Definitions/FactoryDefinition.php b/src/DI/Definitions/FactoryDefinition.php index 7c2e69caa..ff5e6c537 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -24,6 +24,7 @@ final class FactoryDefinition extends Definition private const MethodCreate = 'create'; private Definition $resultDefinition; + private ?string $reference = null; public function __construct() @@ -87,6 +88,10 @@ public function resolveType(Nette\DI\Resolver $resolver): void { if (!$this->getType()) { throw new ServiceCreationException('Type is missing in definition of service.'); + + } elseif ($this->reference === null) { + $this->resultDefinition->setAutowired(false); + $this->reference = $resolver->getContainerBuilder()->addDefinition(null, $this->resultDefinition)->getName(); } $type = Type::fromReflection(new \ReflectionMethod($this->getType(), self::MethodCreate)); diff --git a/src/DI/Extensions/DecoratorExtension.php b/src/DI/Extensions/DecoratorExtension.php index e5a4cc129..43c8aeaa9 100644 --- a/src/DI/Extensions/DecoratorExtension.php +++ b/src/DI/Extensions/DecoratorExtension.php @@ -50,11 +50,7 @@ public function beforeCompile(): void public function addSetups(string $type, array $setups): void { - foreach ($this->findByType($type) as $def) { - if ($def instanceof Definitions\FactoryDefinition) { - $def = $def->getResultDefinition(); - } - + foreach ($this->getContainerBuilder()->findByType($type) as $def) { foreach ($setups as $setup) { if (is_array($setup)) { $setup = new Definitions\Statement(key($setup), array_values($setup)); @@ -69,18 +65,8 @@ public function addSetups(string $type, array $setups): void public function addTags(string $type, array $tags): void { $tags = Nette\Utils\Arrays::normalize($tags, filling: true); - foreach ($this->findByType($type) as $def) { + foreach ($this->getContainerBuilder()->findByType($type) as $def) { $def->setTags($def->getTags() + $tags); } } - - - private function findByType(string $type): array - { - return array_filter( - $this->getContainerBuilder()->getDefinitions(), - fn(Definitions\Definition $def): bool => is_a($def->getType(), $type, true) - || ($def instanceof Definitions\FactoryDefinition && is_a($def->getResultType(), $type, allow_string: true)), - ); - } } diff --git a/src/DI/Extensions/InjectExtension.php b/src/DI/Extensions/InjectExtension.php index 4c40b30c0..d26ab92df 100644 --- a/src/DI/Extensions/InjectExtension.php +++ b/src/DI/Extensions/InjectExtension.php @@ -35,13 +35,8 @@ public function getConfigSchema(): Nette\Schema\Schema public function beforeCompile(): void { foreach ($this->getContainerBuilder()->getDefinitions() as $def) { - if ($def->getTag(self::TagInject)) { - $def = $def instanceof Definitions\FactoryDefinition - ? $def->getResultDefinition() - : $def; - if ($def instanceof Definitions\ServiceDefinition) { - $this->updateDefinition($def); - } + if ($def instanceof Definitions\ServiceDefinition && $def->getTag(self::TagInject)) { + $this->updateDefinition($def); } } } diff --git a/src/DI/Extensions/ServicesExtension.php b/src/DI/Extensions/ServicesExtension.php index 8bb50257d..38ab845be 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -169,7 +169,7 @@ private function updateFactoryDefinition(Definitions\FactoryDefinition $definiti } if (isset($config->inject)) { - $definition->addTag(InjectExtension::TagInject, $config->inject); + $resultDef->addTag(InjectExtension::TagInject, $config->inject); } } diff --git a/tests/DI/DecoratorExtension.factories.phpt b/tests/DI/DecoratorExtension.factories.phpt index ba99a41dc..6b9b2e92a 100644 --- a/tests/DI/DecoratorExtension.factories.phpt +++ b/tests/DI/DecoratorExtension.factories.phpt @@ -30,7 +30,7 @@ $compiler->addExtension('foo', new class extends DI\CompilerExtension { public function beforeCompile() { $this->getContainerBuilder() - ->addFactoryDefinition('bar') + ->addFactoryDefinition('fac1') ->setImplement(FooFactory::class); } }); @@ -45,16 +45,16 @@ decorator: FooFactory: tags: [a] services: - foo: {implement: FooFactory} + fac2: {implement: FooFactory} '); $builder = $compiler->getContainerBuilder(); -Assert::true($builder->getDefinition('foo')->getTag(DI\Extensions\InjectExtension::TagInject)); -Assert::true($builder->getDefinition('foo')->getTag('a')); -Assert::count(1, $builder->getDefinition('foo')->getResultDefinition()->getSetup()); +Assert::true($builder->getDefinition('fac1')->getTag('a')); +Assert::count(1, $builder->getDefinition('fac1')->getResultDefinition()->getSetup()); +Assert::true($builder->getDefinition('fac1')->getResultDefinition()->getTag(DI\Extensions\InjectExtension::TagInject)); -Assert::true($builder->getDefinition('bar')->getTag(DI\Extensions\InjectExtension::TagInject)); -Assert::true($builder->getDefinition('bar')->getTag('a')); -Assert::count(1, $builder->getDefinition('bar')->getResultDefinition()->getSetup()); +Assert::true($builder->getDefinition('fac2')->getTag('a')); +Assert::count(1, $builder->getDefinition('fac2')->getResultDefinition()->getSetup()); +Assert::true($builder->getDefinition('fac2')->getResultDefinition()->getTag(DI\Extensions\InjectExtension::TagInject)); diff --git a/tests/SearchExtension/all.phpt b/tests/SearchExtension/all.phpt index f2d066d9f..a950d7742 100644 --- a/tests/SearchExtension/all.phpt +++ b/tests/SearchExtension/all.phpt @@ -22,6 +22,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/batches.phpt b/tests/SearchExtension/batches.phpt index 26fda917b..4e4ac6d44 100644 --- a/tests/SearchExtension/batches.phpt +++ b/tests/SearchExtension/batches.phpt @@ -37,4 +37,5 @@ Assert::same([ 'Foo\\ClassBar' => ['foo' => true], 'InterfaceOk1' => ['ok' => true], 'InterfaceOk2' => ['ok' => true], + 'stdClass' => [], ], $services); diff --git a/tests/SearchExtension/classes.phpt b/tests/SearchExtension/classes.phpt index cea9cfac7..4d21f9087 100644 --- a/tests/SearchExtension/classes.phpt +++ b/tests/SearchExtension/classes.phpt @@ -23,6 +23,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -53,6 +54,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -102,4 +104,5 @@ Assert::same([ 'ExtendsStdClass', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/duplicates.phpt b/tests/SearchExtension/duplicates.phpt index d8b2eec98..c52f03a71 100644 --- a/tests/SearchExtension/duplicates.phpt +++ b/tests/SearchExtension/duplicates.phpt @@ -28,4 +28,5 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/files.phpt b/tests/SearchExtension/files.phpt index b72a55147..3fdea3b07 100644 --- a/tests/SearchExtension/files.phpt +++ b/tests/SearchExtension/files.phpt @@ -23,6 +23,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -41,6 +42,7 @@ Assert::same([ 'ExtendsStdClass', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -63,6 +65,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -83,4 +86,5 @@ Assert::same([ 'ExtendsStdClass', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/parents.phpt b/tests/SearchExtension/parents.phpt index bf8bf1254..c36fb78f2 100644 --- a/tests/SearchExtension/parents.phpt +++ b/tests/SearchExtension/parents.phpt @@ -23,6 +23,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -99,4 +100,5 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/tags.phpt b/tests/SearchExtension/tags.phpt index 7c2002658..6064d0be1 100644 --- a/tests/SearchExtension/tags.phpt +++ b/tests/SearchExtension/tags.phpt @@ -24,6 +24,7 @@ Assert::same([ 'ExtendsStdClass' => ['a' => 1, 'b' => 2], 'InterfaceOk1' => ['a' => 1, 'b' => 2], 'InterfaceOk2' => ['a' => 1, 'b' => 2], + 'stdClass' => [], ], $services); @@ -43,4 +44,5 @@ Assert::same([ 'ExtendsStdClass' => [], 'InterfaceOk1' => [], 'InterfaceOk2' => [], + 'stdClass' => [], ], $services); From c337c4e30befdc48c4b8522c21311de0f51bc9d8 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 16 May 2024 15:27:40 +0200 Subject: [PATCH 02/26] Reapply "LocatorDefinition: deprecated support for create($name) method (BC break)" This reverts commit 87662ebfb4baabf4788666170b04ba1cde8ed043. --- src/DI/Definitions/LocatorDefinition.php | 4 +++- tests/DI/Compiler.generatedLocator.phpt | 3 ++- tests/DI/Definitions.LocatorDefinition.api.phpt | 12 ++++++------ .../Definitions.LocatorDefinition.render.create.phpt | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/DI/Definitions/LocatorDefinition.php b/src/DI/Definitions/LocatorDefinition.php index fe8b3aecd..f4fee5d66 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -39,7 +39,7 @@ public function setImplement(string $interface): static || (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.", + "Service '%s': Method %s::%s() does not meet the requirements: is create*(), get*() or get(\$name) and is non-static.", $this->getName(), $interface, $method->name, @@ -52,6 +52,8 @@ public function setImplement(string $interface): static "return type of $interface::$method->name()", allowNullable: true, ); + } elseif (str_starts_with($method->name, 'create')) { + trigger_error(sprintf("Service '%s': Method %s::create(\$name) is deprecated, use createFoo().", $this->getName(), $interface), E_USER_DEPRECATED); } } diff --git a/tests/DI/Compiler.generatedLocator.phpt b/tests/DI/Compiler.generatedLocator.phpt index 34816729e..940f25432 100644 --- a/tests/DI/Compiler.generatedLocator.phpt +++ b/tests/DI/Compiler.generatedLocator.phpt @@ -42,7 +42,8 @@ interface LocatorFactoryN } -$container = createContainer(new DI\Compiler, ' +// create($name) is deprecated +$container = @createContainer(new DI\Compiler, ' services: - LoremChild diff --git a/tests/DI/Definitions.LocatorDefinition.api.phpt b/tests/DI/Definitions.LocatorDefinition.api.phpt index 50a771bd0..aaef81002 100644 --- a/tests/DI/Definitions.LocatorDefinition.api.phpt +++ b/tests/DI/Definitions.LocatorDefinition.api.phpt @@ -84,31 +84,31 @@ Assert::exception(function () { 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*(), get*() or get(\$name) 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*(), get*() or get(\$name) 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*(), get*() or get(\$name) 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*(), get*() or get(\$name) 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*(), get*() or get(\$name) and is non-static."); Assert::noError(function () { @@ -121,7 +121,7 @@ Assert::noError(function () { Assert::noError(function () { $def = new LocatorDefinition; - $def->setImplement(Good2::class); + @$def->setImplement(Good2::class); // create($name) is deprecated Assert::same(Good2::class, $def->getImplement()); Assert::same(Good2::class, $def->getType()); }); diff --git a/tests/DI/Definitions.LocatorDefinition.render.create.phpt b/tests/DI/Definitions.LocatorDefinition.render.create.phpt index 4c649129f..833fdbf75 100644 --- a/tests/DI/Definitions.LocatorDefinition.render.create.phpt +++ b/tests/DI/Definitions.LocatorDefinition.render.create.phpt @@ -22,7 +22,7 @@ interface Good test('', function () { $def = new LocatorDefinition; $def->setName('abc'); - $def->setImplement(Good::class); + @$def->setImplement(Good::class); // create($name) is deprecated $def->setReferences(['first' => '@a', 'second' => 'stdClass']); $builder = new Nette\DI\ContainerBuilder; From 7e11757736196e3c5147113cd13d043b3042a5a7 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 12 Sep 2021 23:09:50 +0200 Subject: [PATCH 03/26] opened 4.0-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 28498797b..37f8b10c7 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.0-dev" } } } From c4129fcca8a4105af3d6b6b7607a6a5712aa3945 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 11 Dec 2023 17:11:13 +0100 Subject: [PATCH 04/26] 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 | 13 ++- src/DI/Definitions/LocatorDefinition.php | 13 ++- src/DI/Definitions/ServiceDefinition.php | 11 ++ src/DI/Extensions/InjectExtension.php | 8 +- src/DI/Extensions/ServicesExtension.php | 9 +- src/DI/Helpers.php | 32 +++++- src/DI/PhpGenerator.php | 2 +- src/DI/Resolver.php | 68 ++++-------- tests/DI/Compiler.configOverride.phpt | 3 +- .../DI/Compiler.extensionOverride.errors.phpt | 3 +- tests/DI/Compiler.first-class-callable.phpt | 7 +- tests/DI/Compiler.functions.phpt | 4 +- tests/DI/Compiler.generatedFactory.phpt | 9 +- .../ContainerBuilder.autowiring.novalue.phpt | 6 +- tests/DI/ContainerBuilder.create.error.phpt | 104 +++++++++++++----- tests/DI/ContainerBuilder.error.phpt | 34 +++++- tests/DI/ContainerBuilder.recursive.phpt | 3 +- .../ContainerBuilder.resolveTypes.next.phpt | 27 +++-- tests/DI/ContainerBuilder.resolveTypes.phpt | 27 +++-- tests/DI/ContainerBuilder.selfdependency.phpt | 4 +- .../Definitions.AccessorDefinition.api.phpt | 24 ++-- ...efinitions.AccessorDefinition.resolve.phpt | 9 +- .../DI/Definitions.FactoryDefinition.api.phpt | 21 ++-- ...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 | 9 +- 32 files changed, 370 insertions(+), 175 deletions(-) diff --git a/src/DI/Definitions/AccessorDefinition.php b/src/DI/Definitions/AccessorDefinition.php index bd6c1a1a5..5063d2d26 100644 --- a/src/DI/Definitions/AccessorDefinition.php +++ b/src/DI/Definitions/AccessorDefinition.php @@ -28,8 +28,8 @@ public function setImplement(string $interface): static { 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, )); } @@ -44,19 +44,19 @@ public function setImplement(string $interface): static || 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, )); } - Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()"); + Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()", $this->getDescriptor()); return parent::setType($interface); } diff --git a/src/DI/Definitions/Definition.php b/src/DI/Definitions/Definition.php index ca96659b8..5d6bdadb4 100644 --- a/src/DI/Definitions/Definition.php +++ b/src/DI/Definitions/Definition.php @@ -46,6 +46,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 ?'; + } + } + + protected function setType(?string $type): static { if ($this->autowired && $this->notifier && $this->type !== $type) { @@ -56,8 +79,8 @@ protected function setType(?string $type): static $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 ff5e6c537..7ed5eb933 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -37,8 +37,8 @@ public function setImplement(string $interface): static { 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, )); } @@ -47,13 +47,13 @@ public function setImplement(string $interface): static $method = $rc->getMethods()[0] ?? null; if (!$method || $method->isStatic() || $method->name !== self::MethodCreate || 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, )); } - Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()"); + Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()", $this->getDescriptor()); return parent::setType($interface); } @@ -110,7 +110,8 @@ public function resolveType(Nette\DI\Resolver $resolver): void if (!$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 f4fee5d66..4e7170e34 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -25,12 +25,12 @@ final class LocatorDefinition extends Definition public function setImplement(string $interface): static { 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) { @@ -39,8 +39,8 @@ public function setImplement(string $interface): static || (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*(), get*() or get(\$name) and is non-static.", - $this->getName(), + "[%s]\nMethod %s::%s() does not meet the requirements: is create*(), get*() or get(\$name) and is non-static.", + $this->getDescriptor(), $interface, $method->name, )); @@ -50,6 +50,7 @@ public function setImplement(string $interface): static Nette\DI\Helpers::ensureClassType( Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", + $this->getDescriptor(), allowNullable: true, ); } elseif (str_starts_with($method->name, 'create')) { @@ -112,8 +113,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, )); diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 6cec76a86..671a93059 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -36,6 +36,17 @@ 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(); + } + + public function setType(?string $type): static { return parent::setType($type); diff --git a/src/DI/Extensions/InjectExtension.php b/src/DI/Extensions/InjectExtension.php index d26ab92df..534e3025f 100644 --- a/src/DI/Extensions/InjectExtension.php +++ b/src/DI/Extensions/InjectExtension.php @@ -62,7 +62,7 @@ private function updateDefinition(Definitions\ServiceDefinition $def): void } if ($builder) { - self::checkType($class, $property, $type, $builder); + self::checkType($class, $property, $type, $builder, $def); } array_unshift($setups, $inject); } @@ -143,7 +143,7 @@ public static function callInjects(DI\Container $container, object $service): vo } foreach (self::getInjectProperties($service::class) as $property => $type) { - self::checkType($service, $property, $type, $container); + self::checkType($service, $property, $type, $container, null); $service->$property = $container->getByType($type); } } @@ -154,11 +154,13 @@ private static function checkType( string $name, ?string $type, DI\Container|DI\ContainerBuilder $container, + ?Definitions\Definition $def, ): void { if (!$container->getByType($type, throw: false)) { throw new Nette\DI\MissingServiceException(sprintf( - 'Service of type %s required by %s not found. Did you add it to configuration file?', + "%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 38ab845be..8e35ea635 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -53,7 +53,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); @@ -68,7 +68,12 @@ private function loadDefinition(?string $name, \stdClass $config): void $this->{$methods[$config->defType]}($def, $config); $this->updateDefinition($def, $config); } catch (\Throwable $e) { - throw new Nette\DI\InvalidConfigurationException(($name ? "Service '$name': " : '') . $e->getMessage(), 0, $e); + $message = $e->getMessage(); + if ($name && !str_starts_with($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 610239b8d..ee409f700 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -232,17 +232,23 @@ 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->allows('null'))) { - throw new ServiceCreationException(sprintf("%s is expected to not be %sbuilt-in/complex, '%s' given.", ucfirst($hint), $allowNullable ? '' : 'nullable/', $type)); + throw new ServiceCreationException(sprintf("%s%s is expected to not be %sbuilt-in/complex, '%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; @@ -282,4 +288,22 @@ public static function convertType(mixed $value, string $type): mixed $type, )); } + + + public static function entityToString(string|array|Reference $entity, bool $inner = false): string + { + if (is_string($entity)) { + return $entity . ($inner ? '()' : ''); + + } elseif ($entity instanceof Reference) { + return '@' . $entity->getValue(); + + } else { + [$a, $b] = $entity; + return self::entityToString($a instanceof Statement ? $a->getEntity() : $a, inner: true) + . '::' + . $b + . (str_contains($b, '$') ? '' : '()'); + } + } } diff --git a/src/DI/PhpGenerator.php b/src/DI/PhpGenerator.php index da0abf59a..698e1c0e8 100644 --- a/src/DI/PhpGenerator.php +++ b/src/DI/PhpGenerator.php @@ -95,7 +95,7 @@ public function generateMethod(Definitions\Definition $def): Php\Method return $method; } catch (\Throwable $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 3e6afc3ff..a83f858fe 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -303,9 +303,21 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo try { $arguments = $this->completeArguments($arguments); + } catch (ServiceCreationException $e) { - if (!str_contains($e->getMessage(), ' (used in')) { - $e->setMessage($e->getMessage() . " (used in {$this->entityToString($entity)})"); + if (!str_contains($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; @@ -442,25 +454,16 @@ public function addDependency(\ReflectionClass|\ReflectionFunctionAbstract|strin private function completeException(\Throwable $e, Definition $def): ServiceCreationException { - if ($e instanceof ServiceCreationException && str_starts_with($e->getMessage(), "Service '")) { + $message = $e->getMessage(); + if ($e instanceof ServiceCreationException && str_starts_with($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) @@ -468,33 +471,6 @@ private function completeException(\Throwable $e, Definition $def): ServiceCreat } - private function entityToString($entity): string - { - $referenceToText = fn(Reference $ref): string => $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 (!str_contains($entity[1], '$')) { - $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 { @@ -606,20 +582,20 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab } catch (MissingServiceException) { $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->isOptional()) { 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 ab810683a..e8d49c717 100644 --- a/tests/DI/Compiler.configOverride.phpt +++ b/tests/DI/Compiler.configOverride.phpt @@ -59,5 +59,6 @@ $compiler->addConfig([ Assert::exception( fn() => $compiler->setClassName($class)->compile(), DI\InvalidConfigurationException::class, - "Service 's3': missing original definition for alteration.", + "[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.first-class-callable.phpt b/tests/DI/Compiler.first-class-callable.phpt index c2601dff0..d4f51376e 100644 --- a/tests/DI/Compiler.first-class-callable.phpt +++ b/tests/DI/Compiler.first-class-callable.phpt @@ -50,7 +50,8 @@ Assert::exception(function () { $compiler = new DI\Compiler; $compiler->addConfig($loader->load(Tester\FileMock::create($config, 'neon'))); $compiler->compile(); -}, Nette\DI\ServiceCreationException::class, 'Service of type Closure: Cannot create closure for Service(...)'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Service] +Cannot create closure for Service(...)'); // Invalid callable 2 @@ -63,4 +64,6 @@ Assert::exception(function () { $compiler = new DI\Compiler; $compiler->addConfig($loader->load(Tester\FileMock::create($config, 'neon'))); $compiler->compile(); -}, Nette\DI\ServiceCreationException::class, 'Service of type Service: Cannot create closure for Service(...) (used in Service::__construct())'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Service] +Cannot create closure for Service(...) +Related to Service::__construct().'); diff --git a/tests/DI/Compiler.functions.phpt b/tests/DI/Compiler.functions.phpt index 0afb97a42..08831b44c 100644 --- a/tests/DI/Compiler.functions.phpt +++ b/tests/DI/Compiler.functions.phpt @@ -93,5 +93,7 @@ Assert::exception( - Service(bool(123, 10)) '), Nette\InvalidStateException::class, - 'Service of type Service: Function bool() expects 1 parameter, 2 given. (used in Service::__construct())', + '[Service of type Service] +Function bool() expects 1 parameter, 2 given. +Related to Service::__construct().', ); diff --git a/tests/DI/Compiler.generatedFactory.phpt b/tests/DI/Compiler.generatedFactory.phpt index 81068a106..eac013e90 100644 --- a/tests/DI/Compiler.generatedFactory.phpt +++ b/tests/DI/Compiler.generatedFactory.phpt @@ -266,7 +266,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."); @@ -287,7 +288,8 @@ Assert::exception(function () { $builder->addFactoryDefinition('one') ->setImplement(Bad4::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Cannot implement Bad4::create(): factory method parameters (\$baz) are not matching Bad3::__construct() parameters (\$bar). Did you mean to use '\$bar' in factory method?"); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad4] +Cannot implement Bad4::create(): factory method parameters (\$baz) are not matching Bad3::__construct() parameters (\$bar). Did you mean to use '\$bar' in factory method?"); @@ -308,4 +310,5 @@ Assert::exception(function () { $builder->addFactoryDefinition('one') ->setImplement(Bad6::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad6): Cannot implement Bad6::create(): factory method parameters (\$baz) are not matching Bad5::__construct() parameters (\$xxx)."); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad6] +Cannot implement Bad6::create(): factory method parameters (\$baz) are not matching Bad5::__construct() parameters (\$xxx)."); diff --git a/tests/DI/ContainerBuilder.autowiring.novalue.phpt b/tests/DI/ContainerBuilder.autowiring.novalue.phpt index 1a9b7cca4..3aee26fda 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.create.error.phpt b/tests/DI/ContainerBuilder.create.error.phpt index 0af01c12c..858c22231 100644 --- a/tests/DI/ContainerBuilder.create.error.phpt +++ b/tests/DI/ContainerBuilder.create.error.phpt @@ -17,14 +17,16 @@ require __DIR__ . '/../bootstrap.php'; testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setType('X')->setCreator('Unknown'); -}, Nette\InvalidArgumentException::class, "Service 'one': Class or interface 'X' not found."); +}, Nette\InvalidArgumentException::class, "[Service 'one'] +Class or interface 'X' not found."); testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition(null)->setCreator('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."); testException('', function () { @@ -32,7 +34,8 @@ testException('', function () { $builder->addDefinition('one')->setCreator('@two'); $builder->addDefinition('two')->setCreator('Unknown'); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'two': Class 'Unknown' not found."); +}, Nette\InvalidStateException::class, "[Service 'two'] +Class 'Unknown' not found."); testException('', function () { @@ -40,28 +43,32 @@ testException('', function () { $builder->addDefinition('one')->setCreator(new Reference('two')); $builder->addDefinition('two')->setCreator('Unknown'); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'two': Class 'Unknown' not found."); +}, Nette\InvalidStateException::class, "[Service 'two'] +Class 'Unknown' not found."); testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('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."); testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('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."); testException('', 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."); @@ -74,7 +81,8 @@ testException('', function () { $builder = new DI\ContainerBuilder; $builder->addFactoryDefinition('one') ->setImplement(Bad4::class); -}, Nette\InvalidStateException::class, 'Return type of Bad4::create() is not declared.'); +}, Nette\InvalidStateException::class, "[Service 'one'] +Return type of Bad4::create() is not declared."); interface Bad5 @@ -87,7 +95,8 @@ testException('', 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 @@ -101,7 +110,8 @@ testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('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 @@ -115,7 +125,8 @@ testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('Bad7::create'); $builder->complete(); -}, Nette\DI\ServiceCreationException::class, "Service 'one': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'one'] +Unknown service type, specify it or declare return type of factory method."); class Bad8 @@ -129,7 +140,8 @@ testException('', 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 @@ -143,13 +155,17 @@ testException('fail in argument', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator(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()."); testException('fail in argument', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator(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 @@ -163,7 +179,8 @@ testException('abstract class cannot be instantiated', 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 @@ -177,7 +194,8 @@ testException('trait cannot be instantiated', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('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 @@ -201,7 +219,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()."); testException('forced autowiring fail', function () { @@ -211,7 +231,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()."); testException('autowiring fail in chain', function () { @@ -221,7 +243,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()."); testException('forced autowiring fail in chain', function () { @@ -231,7 +255,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()."); testException('autowiring fail in argument', function () { @@ -241,7 +267,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()."); testException('forced autowiring fail in argument', function () { @@ -251,7 +280,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()."); testException('autowiring fail in chain in argument', function () { @@ -261,7 +292,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()."); testException('forced autowiring fail in chain in argument', function () { @@ -271,7 +305,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()."); testException('forced autowiring fail in property passing', function () { @@ -284,7 +320,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."); testException('autowiring fail in rich property passing', function () { @@ -297,7 +335,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."); testException('autowiring fail in method calling', function () { @@ -310,7 +350,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()."); testException('forced autowiring fail in method calling', function () { @@ -323,7 +365,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."); testException('autowiring fail in rich method calling', function () { @@ -336,4 +380,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.error.phpt b/tests/DI/ContainerBuilder.error.phpt index 33c369aeb..9633cf6e1 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; @@ -21,7 +22,8 @@ $builder->addDefinition('one') Assert::exception( fn() => $builder->complete(), Nette\InvalidStateException::class, - "Service 'one' (type of stdClass): Expected function, method or property name, '1234' given.", + "[Service 'one' of type stdClass] +Expected function, method or property name, '1234' given.", ); @@ -48,5 +50,33 @@ $builder->addDefinition('one') Assert::exception( fn() => $builder->complete(), Nette\InvalidStateException::class, - "Service 'one' (type of stdClass): Missing argument for \$prop[].", + "[Service 'one' of type stdClass] +Missing argument for \$prop[].", +); + + + +$builder = new DI\ContainerBuilder; +$builder->addDefinition(null) + ->setFactory([new Statement('Unknown'), 'foo']); + +Assert::exception( + fn() => $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( + fn() => $builder->complete(), + Nette\DI\ServiceCreationException::class, + "[Service of type stdClass] +Class 'Unknown' not found.", ); diff --git a/tests/DI/ContainerBuilder.recursive.phpt b/tests/DI/ContainerBuilder.recursive.phpt index 8ee4fdb7a..cbd0f08f2 100644 --- a/tests/DI/ContainerBuilder.recursive.phpt +++ b/tests/DI/ContainerBuilder.recursive.phpt @@ -30,5 +30,6 @@ $builder->addDefinition('two') Assert::exception( fn() => createContainer($builder), Nette\DI\ServiceCreationException::class, - "Service 'two': Circular reference detected for services: one, two.", + "[Service 'two'] +Circular reference detected for services: one, two.", ); diff --git a/tests/DI/ContainerBuilder.resolveTypes.next.phpt b/tests/DI/ContainerBuilder.resolveTypes.next.phpt index f19770352..8ce3c0761 100644 --- a/tests/DI/ContainerBuilder.resolveTypes.next.phpt +++ b/tests/DI/ContainerBuilder.resolveTypes.next.phpt @@ -148,56 +148,64 @@ Assert::exception(function () { $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createScalarPhpDoc']), 'next']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createScalar']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createObjectPhpDoc']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createObject']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createObjectNullable']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createMixedPhpDoc']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createMixed']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createGeneric']), 'next']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Class 'T' not found. +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Class 'T' not found. Check the return type of Factory::createGeneric()."); Assert::exception(function () { @@ -205,4 +213,5 @@ Assert::exception(function () { $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createUnion']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createUnion() is expected to not be built-in/complex, 'stdClass|array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createUnion() is expected to not be built-in/complex, 'stdClass|array' given."); diff --git a/tests/DI/ContainerBuilder.resolveTypes.phpt b/tests/DI/ContainerBuilder.resolveTypes.phpt index 67cb3ea47..29b8b32d7 100644 --- a/tests/DI/ContainerBuilder.resolveTypes.phpt +++ b/tests/DI/ContainerBuilder.resolveTypes.phpt @@ -139,56 +139,64 @@ Assert::exception(function () { $builder->addDefinition('a') ->setCreator([Factory::class, 'createScalarPhpDoc']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createScalar']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createObjectPhpDoc']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createObject']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createObjectNullable']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createMixedPhpDoc']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createMixed']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createGeneric']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Class 'T' not found. +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Class 'T' not found. Check the return type of Factory::createGeneric()."); Assert::exception(function () { @@ -196,4 +204,5 @@ Assert::exception(function () { $builder->addDefinition('a') ->setCreator([Factory::class, 'createUnion']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createUnion() is expected to not be built-in/complex, 'stdClass|array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createUnion() is expected to not be built-in/complex, 'stdClass|array' given."); diff --git a/tests/DI/ContainerBuilder.selfdependency.phpt b/tests/DI/ContainerBuilder.selfdependency.phpt index be8c5b77b..360871d0f 100644 --- a/tests/DI/ContainerBuilder.selfdependency.phpt +++ b/tests/DI/ContainerBuilder.selfdependency.phpt @@ -28,5 +28,7 @@ $builder->addDefinition(null) Assert::exception( fn() => 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?', + '[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 adb04bebf..23c3ec302 100644 --- a/tests/DI/Definitions.AccessorDefinition.api.phpt +++ b/tests/DI/Definitions.AccessorDefinition.api.phpt @@ -54,49 +54,57 @@ interface Good1 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::exception(function () { $def = new AccessorDefinition; $def->setImplement(Bad6::class); -}, Nette\DI\ServiceCreationException::class, 'Return type of Bad6::get() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Return type of Bad6::get() is not declared.'); Assert::noError(function () { diff --git a/tests/DI/Definitions.AccessorDefinition.resolve.phpt b/tests/DI/Definitions.AccessorDefinition.resolve.phpt index 014e2caf0..d03e48290 100644 --- a/tests/DI/Definitions.AccessorDefinition.resolve.phpt +++ b/tests/DI/Definitions.AccessorDefinition.resolve.phpt @@ -28,13 +28,15 @@ 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 () { $def = new AccessorDefinition; $def->setImplement(Bad1::class); -}, Nette\DI\ServiceCreationException::class, 'Return type of Bad1::get() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Return type of Bad1::get() is not declared.'); Assert::noError(function () { @@ -63,4 +65,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 787435a5c..6a7a6c370 100644 --- a/tests/DI/Definitions.FactoryDefinition.api.phpt +++ b/tests/DI/Definitions.FactoryDefinition.api.phpt @@ -48,43 +48,50 @@ interface Good1 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::exception(function () { $def = new FactoryDefinition; $def->setImplement(Bad5::class); -}, Nette\DI\ServiceCreationException::class, 'Return type of Bad5::create() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Return type of Bad5::create() is not declared.'); Assert::noError(function () { diff --git a/tests/DI/Definitions.FactoryDefinition.resolve.phpt b/tests/DI/Definitions.FactoryDefinition.resolve.phpt index 97f280234..79410b22a 100644 --- a/tests/DI/Definitions.FactoryDefinition.resolve.phpt +++ b/tests/DI/Definitions.FactoryDefinition.resolve.phpt @@ -28,13 +28,15 @@ 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 () { $def = new FactoryDefinition; $def->setImplement(Bad1::class); -}, Nette\DI\ServiceCreationException::class, 'Return type of Bad1::create() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Return type of Bad1::create() is not declared.'); Assert::noError(function () { @@ -75,4 +77,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 c5dd8fc1f..6b5d6c122 100644 --- a/tests/DI/Definitions.ImportedDefinition.phpt +++ b/tests/DI/Definitions.ImportedDefinition.phpt @@ -16,14 +16,16 @@ require __DIR__ . '/../bootstrap.php'; testException('Unknown class', 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."); testException('Unknown type', 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 aaef81002..daa75622a 100644 --- a/tests/DI/Definitions.LocatorDefinition.api.phpt +++ b/tests/DI/Definitions.LocatorDefinition.api.phpt @@ -66,49 +66,57 @@ interface Good3 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad2::create() does not meet the requirements: is create*(), get*() or get($name) 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad3::get() does not meet the requirements: is create*(), get*() or get($name) 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad4::foo() does not meet the requirements: is create*(), get*() or get($name) 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad5::get() does not meet the requirements: is create*(), get*() or get($name) 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad6::get() does not meet the requirements: is create*(), get*() or get($name) and is non-static.'); Assert::noError(function () { diff --git a/tests/DI/Definitions.LocatorDefinition.resolve.phpt b/tests/DI/Definitions.LocatorDefinition.resolve.phpt index 4d1383ed0..7696e71bd 100644 --- a/tests/DI/Definitions.LocatorDefinition.resolve.phpt +++ b/tests/DI/Definitions.LocatorDefinition.resolve.phpt @@ -33,7 +33,8 @@ testException('', 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 c433a6473..2a413dcb9 100644 --- a/tests/DI/Definitions.ServiceDefinition.phpt +++ b/tests/DI/Definitions.ServiceDefinition.phpt @@ -17,7 +17,8 @@ require __DIR__ . '/../bootstrap.php'; testException('Unknown type', 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('type versus entity I.', function () { $def = new ServiceDefinition; diff --git a/tests/DI/InjectExtension.errors.phpt b/tests/DI/InjectExtension.errors.phpt index e2dc16c88..2d1d94172 100644 --- a/tests/DI/InjectExtension.errors.phpt +++ b/tests/DI/InjectExtension.errors.phpt @@ -58,7 +58,9 @@ services: create: ServiceA inject: yes '); -}, InvalidStateException::class, 'Service of type DateTimeImmutable required by ServiceA::$a 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::exception(function () { diff --git a/tests/DI/Resolver.autowireArguments.errors.phpt b/tests/DI/Resolver.autowireArguments.errors.phpt index 700a48bfb..1cf867667 100644 --- a/tests/DI/Resolver.autowireArguments.errors.phpt +++ b/tests/DI/Resolver.autowireArguments.errors.phpt @@ -21,7 +21,8 @@ Assert::exception( function () {}, ), Nette\DI\ServiceCreationException::class, - 'Service of type stdClass required by $x in {closure}() not found. Did you add it to configuration file?', + 'Service of type stdClass required by $x in {closure}() not found. +Did you add it to configuration file?', ); @@ -33,7 +34,8 @@ Assert::exception( function () {}, ), Nette\DI\ServiceCreationException::class, - "Class 'Foo' required by \$x in {closure}() not found. Check the parameter type and 'use' statements.", + "Class 'Foo' required by \$x in {closure}() not found. +Check the parameter type and 'use' statements.", ); @@ -69,7 +71,8 @@ Assert::exception( fn($type) => $type === Test::class ? new Test : null, ), Nette\DI\ServiceCreationException::class, - 'Service of type stdClass required by $arg in {closure}() not found. Did you add it to configuration file?', + 'Service of type stdClass required by $arg in {closure}() not found. +Did you add it to configuration file?', ); From fe8cc2c70a56070cbb3831a21fda2f210a8fec4d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 16 Jan 2023 04:12:22 +0100 Subject: [PATCH 05/26] annotations @return are no longer supported (BC break) --- src/DI/Helpers.php | 14 ---- src/DI/Resolver.php | 18 +--- .../ContainerBuilder.resolveTypes.next.phpt | 82 ------------------- tests/DI/ContainerBuilder.resolveTypes.phpt | 82 ------------------- tests/DI/Helpers.getReturnType.phpt | 30 ------- tests/DI/fixtures/Helpers.getReturnType.php | 49 ----------- 6 files changed, 4 insertions(+), 271 deletions(-) delete mode 100644 tests/DI/Helpers.getReturnType.phpt delete mode 100644 tests/DI/fixtures/Helpers.getReturnType.php diff --git a/src/DI/Helpers.php b/src/DI/Helpers.php index ee409f700..3597a3820 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -218,20 +218,6 @@ public static function parseAnnotation(\Reflector $ref, string $name): ?string } - public static function getReturnTypeAnnotation(\ReflectionFunctionAbstract $func): ?Type - { - $type = preg_replace('#[|\s].*#', '', (string) self::parseAnnotation($func, 'return')); - if (!$type || $type === 'object' || $type === 'mixed') { - return null; - } elseif ($func instanceof \ReflectionMethod) { - $type = $type === '$this' ? 'static' : $type; - $type = Reflection::expandClassName($type, $func->getDeclaringClass()); - } - - return Type::fromString($type); - } - - public static function ensureClassType( ?Type $type, string $hint, diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index a83f858fe..09c2fe1c8 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -122,20 +122,10 @@ public function resolveEntityType(Statement $statement): ?string $this->addDependency($reflection); - $type = Nette\Utils\Type::fromReflection($reflection) ?? ($annotation = Helpers::getReturnTypeAnnotation($reflection)); - if ($type && !in_array($type->getSingleName(), ['object', 'mixed'], strict: true)) { - 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)), - allowNullable: true, - ); - } - - return null; + $type = Nette\Utils\Type::fromReflection($reflection); + return $type && !in_array($type->getSingleName(), ['object', 'mixed'], strict: true) + ? Helpers::ensureClassType($type, sprintf('return type of %s()', Callback::toString($entity)), allowNullable: true) + : null; } elseif ($entity instanceof Reference) { // alias or factory return $this->resolveReferenceType($entity); diff --git a/tests/DI/ContainerBuilder.resolveTypes.next.phpt b/tests/DI/ContainerBuilder.resolveTypes.next.phpt index 8ce3c0761..7f078690c 100644 --- a/tests/DI/ContainerBuilder.resolveTypes.next.phpt +++ b/tests/DI/ContainerBuilder.resolveTypes.next.phpt @@ -24,52 +24,24 @@ class Lorem class Factory { - /** @return Lorem */ - public function createClassPhpDoc() - { - return []; - } - - public function createClass(): Lorem { return []; } - /** @return Lorem|null */ - public function createNullableClassPhpDoc() - { - return []; - } - - public function createNullableClass(): ?Lorem { return []; } - /** @return array */ - public function createScalarPhpDoc() - { - return []; - } - - public function createScalar(): array { return []; } - /** @return object */ - public function createObjectPhpDoc() - { - return (object) null; - } - - public function createObject(): object { return (object) null; @@ -82,13 +54,6 @@ class Factory } - /** @return mixed */ - public function createMixedPhpDoc() - { - return (object) null; - } - - public function createMixed(): mixed { return (object) null; @@ -115,13 +80,6 @@ class Factory require __DIR__ . '/../bootstrap.php'; -Assert::noError(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createClassPhpDoc']), 'next']); - $container = @createContainer($builder); // @return is deprecated -}); - Assert::noError(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -129,13 +87,6 @@ Assert::noError(function () { $container = createContainer($builder); }); -Assert::noError(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createNullableClassPhpDoc']), 'next']); - $container = @createContainer($builder); // @return is deprecated -}); - Assert::noError(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -143,14 +94,6 @@ Assert::noError(function () { $container = createContainer($builder); }); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createScalarPhpDoc']), 'next']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -159,14 +102,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createObjectPhpDoc']), 'next']); - $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Unknown service type, specify it or declare return type of factory method."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -183,14 +118,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Unknown service type, specify it or declare return type of factory method."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createMixedPhpDoc']), 'next']); - $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Unknown service type, specify it or declare return type of factory method."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -199,15 +126,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Unknown service type, specify it or declare return type of factory method."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createGeneric']), 'next']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Class 'T' not found. -Check the return type of Factory::createGeneric()."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') diff --git a/tests/DI/ContainerBuilder.resolveTypes.phpt b/tests/DI/ContainerBuilder.resolveTypes.phpt index 29b8b32d7..09c639bf5 100644 --- a/tests/DI/ContainerBuilder.resolveTypes.phpt +++ b/tests/DI/ContainerBuilder.resolveTypes.phpt @@ -15,52 +15,24 @@ use Tester\Assert; class Factory { - /** @return stdClass */ - public function createClassPhpDoc() - { - return []; - } - - public function createClass(): stdClass { return []; } - /** @return stdClass|null */ - public function createNullableClassPhpDoc() - { - return []; - } - - public function createNullableClass(): ?stdClass { return []; } - /** @return array */ - public function createScalarPhpDoc() - { - return []; - } - - public function createScalar(): array { return []; } - /** @return object */ - public function createObjectPhpDoc() - { - return (object) null; - } - - public function createObject(): object { return (object) null; @@ -73,13 +45,6 @@ class Factory } - /** @return mixed */ - public function createMixedPhpDoc() - { - return (object) null; - } - - public function createMixed(): mixed { return (object) null; @@ -106,13 +71,6 @@ class Factory require __DIR__ . '/../bootstrap.php'; -Assert::noError(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createClassPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}); - Assert::noError(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -120,13 +78,6 @@ Assert::noError(function () { $container = createContainer($builder); }); -Assert::noError(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createNullableClassPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}); - Assert::noError(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -134,14 +85,6 @@ Assert::noError(function () { $container = createContainer($builder); }); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createScalarPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -150,14 +93,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createObjectPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Unknown service type, specify it or declare return type of factory method."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -174,14 +109,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Unknown service type, specify it or declare return type of factory method."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createMixedPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Unknown service type, specify it or declare return type of factory method."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -190,15 +117,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Unknown service type, specify it or declare return type of factory method."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createGeneric']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Class 'T' not found. -Check the return type of Factory::createGeneric()."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') diff --git a/tests/DI/Helpers.getReturnType.phpt b/tests/DI/Helpers.getReturnType.phpt deleted file mode 100644 index 9a0a47cbf..000000000 --- a/tests/DI/Helpers.getReturnType.phpt +++ /dev/null @@ -1,30 +0,0 @@ - Date: Thu, 14 Dec 2023 13:48:45 +0100 Subject: [PATCH 06/26] annotations @var are no longer supported (BC break) --- src/DI/Extensions/InjectExtension.php | 14 +-- tests/DI/Container.inject.properties.phpt | 16 +-- tests/DI/InjectExtension.basic.phpt | 12 +- tests/DI/InjectExtension.errors.phpt | 8 +- ...Extension.getInjectProperties().php74.phpt | 39 ------- ...Extension.getInjectProperties().php80.phpt | 38 ------- ...InjectExtension.getInjectProperties().phpt | 104 ++++++------------ ...xtension.getInjectProperties().traits.phpt | 4 +- 8 files changed, 54 insertions(+), 181 deletions(-) delete mode 100644 tests/DI/InjectExtension.getInjectProperties().php74.phpt delete mode 100644 tests/DI/InjectExtension.getInjectProperties().php80.phpt diff --git a/src/DI/Extensions/InjectExtension.php b/src/DI/Extensions/InjectExtension.php index 534e3025f..8b0979010 100644 --- a/src/DI/Extensions/InjectExtension.php +++ b/src/DI/Extensions/InjectExtension.php @@ -112,19 +112,15 @@ public static function getInjectProperties(string $class): array { $res = []; foreach ((new \ReflectionClass($class))->getProperties() as $rp) { - $hasAttr = $rp->getAttributes(DI\Attributes\Inject::class); - if ($hasAttr || DI\Helpers::parseAnnotation($rp, 'inject') !== null) { + if ( + $rp->getAttributes(DI\Attributes\Inject::class) + || DI\Helpers::parseAnnotation($rp, 'inject') !== null + ) { if (!$rp->isPublic() || $rp->isStatic() || $rp->isReadOnly()) { throw new Nette\InvalidStateException(sprintf('Property %s for injection must not be static, readonly and must be public.', Reflection::toString($rp))); } - $type = Nette\Utils\Type::fromReflection($rp); - if (!$type && !$hasAttr && ($annotation = DI\Helpers::parseAnnotation($rp, 'var'))) { - $annotation = Reflection::expandClassName($annotation, Reflection::getPropertyDeclaringClass($rp)); - $type = Nette\Utils\Type::fromString($annotation); - } - - $res[$rp->getName()] = DI\Helpers::ensureClassType($type, 'type of property ' . Reflection::toString($rp)); + $res[$rp->getName()] = DI\Helpers::ensureClassType(Nette\Utils\Type::fromReflection($rp), 'type of property ' . Reflection::toString($rp)); } } diff --git a/tests/DI/Container.inject.properties.phpt b/tests/DI/Container.inject.properties.phpt index b633c0f8b..8b0203539 100644 --- a/tests/DI/Container.inject.properties.phpt +++ b/tests/DI/Container.inject.properties.phpt @@ -23,20 +23,17 @@ class Foo implements IFoo class Test1 { - /** @inject @var stdClass */ - public $varA; - - /** @var stdClass @inject */ - public $varB; + /** @inject */ + public stdClass $varA; } class Test2 extends Test1 { - /** @var stdClass @inject */ - public $varC; + /** @inject */ + public stdClass $varC; - /** @var IFoo @inject */ - public $varD; + /** @inject */ + public IFoo $varD; } @@ -52,6 +49,5 @@ $container = createContainer($builder); $test = new Test2; $container->callInjects($test); Assert::type(stdClass::class, $test->varA); -Assert::type(stdClass::class, $test->varB); Assert::type(stdClass::class, $test->varC); Assert::type(Foo::class, $test->varD); diff --git a/tests/DI/InjectExtension.basic.phpt b/tests/DI/InjectExtension.basic.phpt index 1b6d3fb52..8cb99fc8b 100644 --- a/tests/DI/InjectExtension.basic.phpt +++ b/tests/DI/InjectExtension.basic.phpt @@ -31,8 +31,8 @@ class ConcreteDependencyB extends AbstractDependency class ParentClass { - /** @var stdClass @inject */ - public $a; + /** @inject */ + public stdClass $a; public function injectA() @@ -47,11 +47,11 @@ class ParentClass class Service extends ParentClass { - /** @var stdClass @inject */ - public $c; + /** @inject */ + public stdClass $c; - /** @var AbstractDependency @inject */ - public $e; + /** @inject */ + public AbstractDependency $e; public function injectC() diff --git a/tests/DI/InjectExtension.errors.phpt b/tests/DI/InjectExtension.errors.phpt index 2d1d94172..0bd382707 100644 --- a/tests/DI/InjectExtension.errors.phpt +++ b/tests/DI/InjectExtension.errors.phpt @@ -16,15 +16,15 @@ require __DIR__ . '/../bootstrap.php'; class ServiceA { - /** @var DateTimeImmutable @inject */ - public $a; + /** @inject */ + public DateTimeImmutable $a; } class ServiceB { - /** @var Unknown @inject */ - public $a; + /** @inject */ + public Unknown $a; } diff --git a/tests/DI/InjectExtension.getInjectProperties().php74.phpt b/tests/DI/InjectExtension.getInjectProperties().php74.phpt deleted file mode 100644 index 33a5cb95e..000000000 --- a/tests/DI/InjectExtension.getInjectProperties().php74.phpt +++ /dev/null @@ -1,39 +0,0 @@ - A\AInjected::class, - 'varC' => A\AInjected::class, - ], InjectExtension::getInjectProperties(A\AClass::class)); -} diff --git a/tests/DI/InjectExtension.getInjectProperties().php80.phpt b/tests/DI/InjectExtension.getInjectProperties().php80.phpt deleted file mode 100644 index e24155f65..000000000 --- a/tests/DI/InjectExtension.getInjectProperties().php80.phpt +++ /dev/null @@ -1,38 +0,0 @@ - InjectExtension::getInjectProperties(AClass::class), - Nette\InvalidStateException::class, - "Type of property AClass::\$var is expected to not be nullable/built-in/complex, 'AClass|stdClass' given.", -); - -Assert::same([ - 'varA' => 'stdClass', -], InjectExtension::getInjectProperties(EClass::class)); diff --git a/tests/DI/InjectExtension.getInjectProperties().phpt b/tests/DI/InjectExtension.getInjectProperties().phpt index 79c00b86e..65bf65a04 100644 --- a/tests/DI/InjectExtension.getInjectProperties().phpt +++ b/tests/DI/InjectExtension.getInjectProperties().phpt @@ -6,93 +6,51 @@ declare(strict_types=1); -namespace A -{ - class AClass - { - /** @var AInjected @inject */ - public $varA; +use Nette\DI\Attributes\Inject; +use Nette\DI\Extensions\InjectExtension; +use Tester\Assert; + - /** @var B\BInjected @inject */ - public $varB; +class AClass +{ + /** @inject */ + public AInjected $varA; - /** @var AInjected @inject */ - public $varC; + /** @inject */ + public BInjected $varB; - /** @var AInjected */ - public $varD; - } + public $varD; - class AInjected - { - } + #[Inject] + public stdClass $varF; } -namespace A\B +class BadClass { - use A; - - class BClass extends A\AClass - { - /** @var BInjected @inject */ - public $varF; - } - - class BInjected - { - } + /** @inject */ + public AClass|stdClass $var; } -namespace C +class AInjected { - use A\AInjected; - use A\B; - use C\CInjected as CAlias; - - class CClass - { - /** @var AInjected @inject */ - public $var1; - - /** @var B\BInjected @inject */ - public $var2; - - /** @var CAlias @inject */ - public $var3; - - /** @var CInjected @inject */ - public $var4; - } - - class CInjected - { - } } -namespace { - use Nette\DI\Extensions\InjectExtension; - use Tester\Assert; +class BInjected +{ +} - require __DIR__ . '/../bootstrap.php'; +require __DIR__ . '/../bootstrap.php'; - Assert::same([ - 'varA' => A\AInjected::class, - 'varB' => A\B\BInjected::class, - 'varC' => A\AInjected::class, - ], InjectExtension::getInjectProperties(A\AClass::class)); - Assert::same([ - 'varA' => A\AInjected::class, - 'varB' => A\B\BInjected::class, - 'varC' => A\AInjected::class, - 'varF' => A\B\BInjected::class, - ], InjectExtension::getInjectProperties(A\B\BClass::class)); +Assert::same([ + 'varA' => AInjected::class, + 'varB' => BInjected::class, + 'varF' => stdClass::class, +], InjectExtension::getInjectProperties(AClass::class)); - Assert::same([ - 'var1' => A\AInjected::class, - 'var2' => A\B\BInjected::class, - 'var3' => C\CInjected::class, - 'var4' => C\CInjected::class, - ], InjectExtension::getInjectProperties(C\CClass::class)); -} +Assert::exception( + fn() => InjectExtension::getInjectProperties(BadClass::class), + Nette\InvalidStateException::class, + "Type of property BadClass::\$var is expected to not be nullable/built-in/complex, 'AClass|stdClass' given.", +); diff --git a/tests/DI/InjectExtension.getInjectProperties().traits.phpt b/tests/DI/InjectExtension.getInjectProperties().traits.phpt index 1fc6c211e..42b625067 100644 --- a/tests/DI/InjectExtension.getInjectProperties().traits.phpt +++ b/tests/DI/InjectExtension.getInjectProperties().traits.phpt @@ -19,8 +19,8 @@ namespace B trait BTrait { - /** @var AInjected @inject */ - public $varA; + /** @inject */ + public AInjected $varA; } } From cff4960cc928a843d4dce87158976626e90417f1 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 11 Dec 2023 17:12:33 +0100 Subject: [PATCH 07/26] removed support for three ... dots --- src/DI/Config/Adapters/NeonAdapter.php | 5 ----- tests/DI/NeonAdapter.preprocess.phpt | 11 ----------- 2 files changed, 16 deletions(-) diff --git a/src/DI/Config/Adapters/NeonAdapter.php b/src/DI/Config/Adapters/NeonAdapter.php index d806cd319..90c629c50 100644 --- a/src/DI/Config/Adapters/NeonAdapter.php +++ b/src/DI/Config/Adapters/NeonAdapter.php @@ -180,11 +180,6 @@ private function removeUnderscoreVisitor(Neon\Node $node): void if ($attr->value instanceof Neon\Node\LiteralNode && $attr->value->value === '_') { unset($node->attributes[$i]); $index = true; - - } elseif ($attr->value instanceof Neon\Node\LiteralNode && $attr->value->value === '...') { - trigger_error("Replace ... with _ in configuration file '$this->file'.", E_USER_DEPRECATED); - unset($node->attributes[$i]); - $index = true; } } } diff --git a/tests/DI/NeonAdapter.preprocess.phpt b/tests/DI/NeonAdapter.preprocess.phpt index 7b59be016..350dbd259 100644 --- a/tests/DI/NeonAdapter.preprocess.phpt +++ b/tests/DI/NeonAdapter.preprocess.phpt @@ -70,17 +70,6 @@ Assert::equal( ); -// ... deprecated -$data = @$adapter->load(Tester\FileMock::create(' -- Class(arg1, ..., [...]) -', 'neon')); - -Assert::equal( - [new Statement('Class', ['arg1', 2 => ['...']])], - $data, -); - - // @ escaping $data = @$adapter->load(Tester\FileMock::create(' - @@double From 3dc4e50363818e4321cb041a39290b74b48f9373 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 20 Dec 2022 00:31:03 +0100 Subject: [PATCH 08/26] removed compatibility for old class names --- src/DI/Config/Adapter.php | 3 -- src/DI/Definitions/ServiceDefinition.php | 3 -- src/DI/Definitions/Statement.php | 3 -- src/compatibility.php | 39 ------------------------ 4 files changed, 48 deletions(-) delete mode 100644 src/compatibility.php diff --git a/src/DI/Config/Adapter.php b/src/DI/Config/Adapter.php index 25e9cecd8..a9b7004b6 100644 --- a/src/DI/Config/Adapter.php +++ b/src/DI/Config/Adapter.php @@ -20,6 +20,3 @@ interface Adapter */ function load(string $file): array; } - - -class_exists(IAdapter::class); diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 671a93059..5efa41c60 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -215,6 +215,3 @@ public function __clone() $this->setup = unserialize(serialize($this->setup)); } } - - -class_exists(Nette\DI\ServiceDefinition::class); diff --git a/src/DI/Definitions/Statement.php b/src/DI/Definitions/Statement.php index cd80f6d44..62e141202 100644 --- a/src/DI/Definitions/Statement.php +++ b/src/DI/Definitions/Statement.php @@ -63,6 +63,3 @@ public function getEntity(): string|array|Definition|Reference|null return $this->entity; } } - - -class_exists(Nette\DI\Statement::class); diff --git a/src/compatibility.php b/src/compatibility.php deleted file mode 100644 index f0a0e53fe..000000000 --- a/src/compatibility.php +++ /dev/null @@ -1,39 +0,0 @@ - Date: Fri, 24 Sep 2021 14:07:27 +0200 Subject: [PATCH 09/26] deprecated magic properties (BC break) --- src/DI/Definitions/ServiceDefinition.php | 6 +++--- src/DI/Definitions/Statement.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 5efa41c60..0bf42d48e 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -16,9 +16,9 @@ /** * Definition of standard service. * - * @property string|null $class - * @property Statement $factory - * @property Statement[] $setup + * @property-deprecated string|null $class + * @property-deprecated Statement $factory + * @property-deprecated Statement[] $setup */ final class ServiceDefinition extends Definition { diff --git a/src/DI/Definitions/Statement.php b/src/DI/Definitions/Statement.php index 62e141202..8010b4b95 100644 --- a/src/DI/Definitions/Statement.php +++ b/src/DI/Definitions/Statement.php @@ -15,7 +15,7 @@ /** * Assignment or calling statement. * - * @property string|array|Definition|Reference|null $entity + * @property-deprecated string|array|Definition|Reference|null $entity */ final class Statement implements Nette\Schema\DynamicParameter { From e782f3ca8f4f42a6ec738811f9c692fdabb70f20 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 6 Feb 2024 00:52:18 +0100 Subject: [PATCH 10/26] LocatorDefinition: removed support for create($name) methods (BC break) --- src/DI/Definitions/LocatorDefinition.php | 8 +-- tests/DI/Compiler.generatedLocator.phpt | 23 +------ .../DI/Definitions.LocatorDefinition.api.phpt | 13 ---- ...tions.LocatorDefinition.render.create.phpt | 64 ------------------- 4 files changed, 4 insertions(+), 104 deletions(-) delete mode 100644 tests/DI/Definitions.LocatorDefinition.render.create.phpt diff --git a/src/DI/Definitions/LocatorDefinition.php b/src/DI/Definitions/LocatorDefinition.php index 4e7170e34..6c9d96628 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -35,7 +35,7 @@ public function setImplement(string $interface): static foreach ($methods as $method) { if ($method->isStatic() || !( - (preg_match('#^(get|create)$#', $method->name) && $method->getNumberOfParameters() === 1) + ($method->name === 'get' && $method->getNumberOfParameters() === 1) || (preg_match('#^(get|create)[A-Z]#', $method->name) && $method->getNumberOfParameters() === 0) )) { throw new Nette\InvalidArgumentException(sprintf( @@ -46,15 +46,13 @@ public function setImplement(string $interface): static )); } - if ($method->getNumberOfParameters() === 0) { + if ($method->name !== 'get') { Nette\DI\Helpers::ensureClassType( Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", $this->getDescriptor(), allowNullable: true, ); - } elseif (str_starts_with($method->name, 'create')) { - trigger_error(sprintf("Service '%s': Method %s::create(\$name) is deprecated, use createFoo().", $this->getName(), $interface), E_USER_DEPRECATED); } } @@ -155,7 +153,7 @@ public function generateMethod(Nette\PhpGenerator\Method $method, Nette\DI\PhpGe $methodInner->setBody('if (!isset($this->mapping[$name])) { ' . ($nullable ? 'return null;' : 'throw new Nette\DI\MissingServiceException("Service \'$name\' is not defined.");') . ' } -return $this->container->' . $m[1] . 'Service($this->mapping[$name]);') +return $this->container->getService($this->mapping[$name]);') ->addParameter('name'); } elseif (isset($this->references[$name])) { diff --git a/tests/DI/Compiler.generatedLocator.phpt b/tests/DI/Compiler.generatedLocator.phpt index 940f25432..d33b219c5 100644 --- a/tests/DI/Compiler.generatedLocator.phpt +++ b/tests/DI/Compiler.generatedLocator.phpt @@ -42,8 +42,7 @@ interface LocatorFactoryN } -// create($name) is deprecated -$container = @createContainer(new DI\Compiler, ' +$container = createContainer(new DI\Compiler, ' services: - LoremChild @@ -64,10 +63,7 @@ services: one: Locator(a: @lorem1, b: LoremChild) two: Locator(tagged: a) - three: LocatorFactory(a: @lorem1, b: LoremChild) - four: LocatorFactory(tagged: b) five: LocatorN(tagged: a) - six: LocatorFactoryN(tagged: a) seven: Locator(a: @lorem1) eight: Locator(a: LoremChild()) '); @@ -96,29 +92,12 @@ Assert::exception( "Service '3' is not defined.", ); -// factory -$three = $container->getService('three'); -Assert::type(Lorem::class, $three->create('a')); -Assert::type(LoremChild::class, $three->create('b')); -Assert::notSame($three->create('a'), $three->create('a')); - -// tagged factory -$four = $container->getService('four'); -Assert::type(Lorem::class, $four->create('3')); -Assert::notSame($container->getService('lorem3'), $four->create('3')); - // nullable accessor $five = $container->getService('five'); Assert::type(Lorem::class, $five->get('1')); Assert::type(Lorem::class, $five->get('2')); Assert::null($five->get('3')); -// nullable factory -$six = $container->getService('six'); -Assert::type(Lorem::class, $six->create('1')); -Assert::type(Lorem::class, $six->create('2')); -Assert::null($six->create('3')); - // accessor with one service $one = $container->getService('seven'); Assert::type(Lorem::class, $one->get('a')); diff --git a/tests/DI/Definitions.LocatorDefinition.api.phpt b/tests/DI/Definitions.LocatorDefinition.api.phpt index daa75622a..826eaa44e 100644 --- a/tests/DI/Definitions.LocatorDefinition.api.phpt +++ b/tests/DI/Definitions.LocatorDefinition.api.phpt @@ -50,11 +50,6 @@ interface Good1 public function get($name); } -interface Good2 -{ - public function create($name); -} - interface Good3 { public function createA(): stdClass; @@ -127,14 +122,6 @@ Assert::noError(function () { }); -Assert::noError(function () { - $def = new LocatorDefinition; - @$def->setImplement(Good2::class); // create($name) is deprecated - Assert::same(Good2::class, $def->getImplement()); - Assert::same(Good2::class, $def->getType()); -}); - - Assert::noError(function () { $def = new LocatorDefinition; $def->setImplement(Good3::class); diff --git a/tests/DI/Definitions.LocatorDefinition.render.create.phpt b/tests/DI/Definitions.LocatorDefinition.render.create.phpt deleted file mode 100644 index 833fdbf75..000000000 --- a/tests/DI/Definitions.LocatorDefinition.render.create.phpt +++ /dev/null @@ -1,64 +0,0 @@ -setName('abc'); - @$def->setImplement(Good::class); // create($name) is deprecated - $def->setReferences(['first' => '@a', 'second' => 'stdClass']); - - $builder = new Nette\DI\ContainerBuilder; - $builder->addDefinition('a')->setType(stdClass::class); - $resolver = new Nette\DI\Resolver($builder); - - $resolver->resolveDefinition($def); - $resolver->completeDefinition($def); - - $phpGenerator = new Nette\DI\PhpGenerator($builder); - $method = $phpGenerator->generateMethod($def); - - Assert::match( - <<<'XX' - public function createServiceAbc(): Good - { - return new class ($this) implements Good { - private $mapping = ['first' => 'a', 'second' => 'a']; - - - public function __construct( - private $container, - ) { - } - - - public function create($name): stdClass - { - if (!isset($this->mapping[$name])) { - throw new Nette\DI\MissingServiceException("Service '$name' is not defined."); - } - return $this->container->createService($this->mapping[$name]); - } - }; - } - XX, - $method->__toString(), - ); -}); From d579e30f9be91a1baff2840d021d274463022b20 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 6 Apr 2024 21:02:45 +0200 Subject: [PATCH 11/26] annotations @inject is deprecated (BC break) --- src/DI/Helpers.php | 3 +++ tests/DI/Container.inject.properties.phpt | 7 +++--- tests/DI/Helpers.parseAnnotation().phpt | 22 ++++++++----------- tests/DI/InjectExtension.basic.phpt | 7 +++--- tests/DI/InjectExtension.errors.phpt | 11 +++++----- ...InjectExtension.getInjectProperties().phpt | 6 ++--- ...xtension.getInjectProperties().traits.phpt | 3 ++- 7 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/DI/Helpers.php b/src/DI/Helpers.php index 3597a3820..16d6b44ba 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -202,6 +202,7 @@ public static function prefixServiceName(mixed $config, string $namespace): mixe /** * Returns an annotation value. + * @deprecated */ public static function parseAnnotation(\Reflector $ref, string $name): ?string { @@ -211,6 +212,8 @@ public static function parseAnnotation(\Reflector $ref, string $name): ?string $re = '#[\s*]@' . preg_quote($name, '#') . '(?=\s|$)(?:[ \t]+([^@\s]\S*))?#'; if ($ref->getDocComment() && preg_match($re, trim($ref->getDocComment(), '/*'), $m)) { + $alt = $name === 'inject' ? '#[Nette\DI\Attributes\Inject]' : 'alternative'; + trigger_error("Annotation @$name is deprecated, use $alt (used in " . Reflection::toString($ref) . ')', E_USER_DEPRECATED); return $m[1] ?? ''; } diff --git a/tests/DI/Container.inject.properties.phpt b/tests/DI/Container.inject.properties.phpt index 8b0203539..73324665c 100644 --- a/tests/DI/Container.inject.properties.phpt +++ b/tests/DI/Container.inject.properties.phpt @@ -7,6 +7,7 @@ declare(strict_types=1); use Nette\DI; +use Nette\DI\Attributes\Inject; use Tester\Assert; @@ -23,16 +24,16 @@ class Foo implements IFoo class Test1 { - /** @inject */ + #[Inject] public stdClass $varA; } class Test2 extends Test1 { - /** @inject */ + #[Inject] public stdClass $varC; - /** @inject */ + #[Inject] public IFoo $varD; } diff --git a/tests/DI/Helpers.parseAnnotation().phpt b/tests/DI/Helpers.parseAnnotation().phpt index 3406024bd..44115be43 100644 --- a/tests/DI/Helpers.parseAnnotation().phpt +++ b/tests/DI/Helpers.parseAnnotation().phpt @@ -1,9 +1,5 @@ Date: Wed, 15 Dec 2021 19:45:29 +0100 Subject: [PATCH 12/26] Resolver: processing of functions like not() moved to PhpGenerator --- src/DI/PhpGenerator.php | 9 +++++++++ src/DI/Resolver.php | 10 ---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/DI/PhpGenerator.php b/src/DI/PhpGenerator.php index 698e1c0e8..1f893abbf 100644 --- a/src/DI/PhpGenerator.php +++ b/src/DI/PhpGenerator.php @@ -112,6 +112,15 @@ public function formatStatement(Statement $statement): string case is_string($entity) && str_contains($entity, '?'): // PHP literal return $this->formatPhp($entity, $arguments); + case $entity === 'not': + return $this->formatPhp('!(?)', $arguments); + + case $entity === 'bool': + case $entity === 'int': + case $entity === 'float': + case $entity === 'string': + return $this->formatPhp('?::?(?, ?)', [Helpers::class, 'convertType', $arguments[0], $entity]); + case is_string($entity): // create class return $arguments ? $this->formatPhp("new $entity(...?:)", [$arguments]) diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index 09c2fe1c8..819fc34ee 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -192,13 +192,6 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo break; case $entity === 'not': - if (count($arguments) !== 1) { - throw new ServiceCreationException(sprintf('Function %s() expects 1 parameter, %s given.', $entity, count($arguments))); - } - - $entity = ['', '!']; - break; - case $entity === 'bool': case $entity === 'int': case $entity === 'float': @@ -206,9 +199,6 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo if (count($arguments) !== 1) { throw new ServiceCreationException(sprintf('Function %s() expects 1 parameter, %s given.', $entity, count($arguments))); } - - $arguments = [$arguments[0], $entity]; - $entity = [Helpers::class, 'convertType']; break; case is_string($entity): // create class From f9ca71dac3701007dcdfa9a0b10c4d402d7c8965 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 16 May 2024 23:26:10 +0200 Subject: [PATCH 13/26] readme: added jumbo --- readme.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 24d81e254..97f74641d 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,4 @@ -Nette Dependency Injection (DI) -=============================== +[![Nette Dependency Injection](https://github.com/nette/di/assets/194960/d368a458-bac1-48b1-9b4b-7929f4bb2f98)](https://doc.nette.org/dependency-injection) [![Downloads this Month](https://img.shields.io/packagist/dm/nette/di.svg)](https://packagist.org/packages/nette/di) [![Tests](https://github.com/nette/di/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/di/actions) @@ -7,6 +6,7 @@ Nette Dependency Injection (DI) [![Latest Stable Version](https://poser.pugx.org/nette/di/v/stable)](https://github.com/nette/di/releases) [![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/di/blob/master/license.md) +  Introduction ------------ @@ -17,6 +17,7 @@ Nette DI is one of the most interesting part of framework. It is compiled DI con Documentation can be found on the [website](https://doc.nette.org/dependency-injection). +  [Support Me](https://github.com/sponsors/dg) -------------------------------------------- @@ -27,6 +28,7 @@ Do you like Nette DI? Are you looking forward to the new features? Thank you! +  Installation ------------ @@ -39,6 +41,7 @@ composer require nette/di It requires PHP version 8.1 and supports PHP up to 8.3. +  Usage ----- @@ -177,6 +180,8 @@ $manager->distribute(...); Significant to Dependency Injection is that no class depends on the container. Thus it can be easily replaced with another one. For example with the container generated by Nette DI. +  + Nette DI ---------- @@ -218,6 +223,7 @@ During development it is useful to activate auto-refresh mode which automaticall $loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp', autoRebuild: true); ``` +  Services -------- @@ -291,6 +297,7 @@ class FooClass However, this method is not ideal, because the variable must be declared as public and there is no way how you can ensure that the passed object will be of the given type. We also lose the ability to handle the assigned dependency in our code and we violate the principles of encapsulation. +  Factories --------- From ed4a800039d4d6d914cbf65247fe8debc40c411f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Mat=C4=9Bj=C4=8Dek?= Date: Thu, 6 Jun 2024 16:12:14 +0200 Subject: [PATCH 14/26] SearchExtension: ignores classes that do not have autowired parameters --- src/DI/Extensions/SearchExtension.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/DI/Extensions/SearchExtension.php b/src/DI/Extensions/SearchExtension.php index eb872534d..5fcc435d4 100644 --- a/src/DI/Extensions/SearchExtension.php +++ b/src/DI/Extensions/SearchExtension.php @@ -110,6 +110,7 @@ public function findClasses(\stdClass $config): array && (!$rejectRE || !preg_match($rejectRE, $rc->name)) && (!$acceptParent || Arrays::some($acceptParent, fn($nm) => $rc->isSubclassOf($nm))) && (!$rejectParent || Arrays::every($rejectParent, fn($nm) => !$rc->isSubclassOf($nm))) + && (self::hasAutowireArguments($rc->getConstructor())) ) { $found[] = $rc->name; } @@ -156,4 +157,18 @@ private static function buildNameRegexp(array $masks): ?string return $res ? '#^(' . implode('|', $res) . ')$#i' : null; } + + + private static function hasAutowireArguments(?\ReflectionMethod $constructor): bool + { + if ($constructor !== null) { + try { + Nette\DI\Resolver::autowireArguments($constructor, [], static fn() => new \stdClass()); + } catch (Nette\DI\ServiceCreationException) { + return false; + } + } + + return true; + } } From 88c314a858b087cd163b522d6eeab14d75c42b50 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 4 Feb 2024 23:21:07 +0100 Subject: [PATCH 15/26] FactoryDefinition: result definition is added to container [WIP] --- src/DI/Definitions/FactoryDefinition.php | 5 +++++ src/DI/Extensions/DecoratorExtension.php | 18 ++---------------- src/DI/Extensions/InjectExtension.php | 9 ++------- src/DI/Extensions/ServicesExtension.php | 2 +- tests/DI/DecoratorExtension.factories.phpt | 16 ++++++++-------- tests/SearchExtension/all.phpt | 1 + tests/SearchExtension/batches.phpt | 1 + tests/SearchExtension/classes.phpt | 3 +++ tests/SearchExtension/duplicates.phpt | 1 + tests/SearchExtension/files.phpt | 4 ++++ tests/SearchExtension/parents.phpt | 2 ++ tests/SearchExtension/tags.phpt | 2 ++ 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/DI/Definitions/FactoryDefinition.php b/src/DI/Definitions/FactoryDefinition.php index 7c2e69caa..ff5e6c537 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -24,6 +24,7 @@ final class FactoryDefinition extends Definition private const MethodCreate = 'create'; private Definition $resultDefinition; + private ?string $reference = null; public function __construct() @@ -87,6 +88,10 @@ public function resolveType(Nette\DI\Resolver $resolver): void { if (!$this->getType()) { throw new ServiceCreationException('Type is missing in definition of service.'); + + } elseif ($this->reference === null) { + $this->resultDefinition->setAutowired(false); + $this->reference = $resolver->getContainerBuilder()->addDefinition(null, $this->resultDefinition)->getName(); } $type = Type::fromReflection(new \ReflectionMethod($this->getType(), self::MethodCreate)); diff --git a/src/DI/Extensions/DecoratorExtension.php b/src/DI/Extensions/DecoratorExtension.php index e5a4cc129..43c8aeaa9 100644 --- a/src/DI/Extensions/DecoratorExtension.php +++ b/src/DI/Extensions/DecoratorExtension.php @@ -50,11 +50,7 @@ public function beforeCompile(): void public function addSetups(string $type, array $setups): void { - foreach ($this->findByType($type) as $def) { - if ($def instanceof Definitions\FactoryDefinition) { - $def = $def->getResultDefinition(); - } - + foreach ($this->getContainerBuilder()->findByType($type) as $def) { foreach ($setups as $setup) { if (is_array($setup)) { $setup = new Definitions\Statement(key($setup), array_values($setup)); @@ -69,18 +65,8 @@ public function addSetups(string $type, array $setups): void public function addTags(string $type, array $tags): void { $tags = Nette\Utils\Arrays::normalize($tags, filling: true); - foreach ($this->findByType($type) as $def) { + foreach ($this->getContainerBuilder()->findByType($type) as $def) { $def->setTags($def->getTags() + $tags); } } - - - private function findByType(string $type): array - { - return array_filter( - $this->getContainerBuilder()->getDefinitions(), - fn(Definitions\Definition $def): bool => is_a($def->getType(), $type, true) - || ($def instanceof Definitions\FactoryDefinition && is_a($def->getResultType(), $type, allow_string: true)), - ); - } } diff --git a/src/DI/Extensions/InjectExtension.php b/src/DI/Extensions/InjectExtension.php index 4c40b30c0..d26ab92df 100644 --- a/src/DI/Extensions/InjectExtension.php +++ b/src/DI/Extensions/InjectExtension.php @@ -35,13 +35,8 @@ public function getConfigSchema(): Nette\Schema\Schema public function beforeCompile(): void { foreach ($this->getContainerBuilder()->getDefinitions() as $def) { - if ($def->getTag(self::TagInject)) { - $def = $def instanceof Definitions\FactoryDefinition - ? $def->getResultDefinition() - : $def; - if ($def instanceof Definitions\ServiceDefinition) { - $this->updateDefinition($def); - } + if ($def instanceof Definitions\ServiceDefinition && $def->getTag(self::TagInject)) { + $this->updateDefinition($def); } } } diff --git a/src/DI/Extensions/ServicesExtension.php b/src/DI/Extensions/ServicesExtension.php index 8bb50257d..38ab845be 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -169,7 +169,7 @@ private function updateFactoryDefinition(Definitions\FactoryDefinition $definiti } if (isset($config->inject)) { - $definition->addTag(InjectExtension::TagInject, $config->inject); + $resultDef->addTag(InjectExtension::TagInject, $config->inject); } } diff --git a/tests/DI/DecoratorExtension.factories.phpt b/tests/DI/DecoratorExtension.factories.phpt index ba99a41dc..6b9b2e92a 100644 --- a/tests/DI/DecoratorExtension.factories.phpt +++ b/tests/DI/DecoratorExtension.factories.phpt @@ -30,7 +30,7 @@ $compiler->addExtension('foo', new class extends DI\CompilerExtension { public function beforeCompile() { $this->getContainerBuilder() - ->addFactoryDefinition('bar') + ->addFactoryDefinition('fac1') ->setImplement(FooFactory::class); } }); @@ -45,16 +45,16 @@ decorator: FooFactory: tags: [a] services: - foo: {implement: FooFactory} + fac2: {implement: FooFactory} '); $builder = $compiler->getContainerBuilder(); -Assert::true($builder->getDefinition('foo')->getTag(DI\Extensions\InjectExtension::TagInject)); -Assert::true($builder->getDefinition('foo')->getTag('a')); -Assert::count(1, $builder->getDefinition('foo')->getResultDefinition()->getSetup()); +Assert::true($builder->getDefinition('fac1')->getTag('a')); +Assert::count(1, $builder->getDefinition('fac1')->getResultDefinition()->getSetup()); +Assert::true($builder->getDefinition('fac1')->getResultDefinition()->getTag(DI\Extensions\InjectExtension::TagInject)); -Assert::true($builder->getDefinition('bar')->getTag(DI\Extensions\InjectExtension::TagInject)); -Assert::true($builder->getDefinition('bar')->getTag('a')); -Assert::count(1, $builder->getDefinition('bar')->getResultDefinition()->getSetup()); +Assert::true($builder->getDefinition('fac2')->getTag('a')); +Assert::count(1, $builder->getDefinition('fac2')->getResultDefinition()->getSetup()); +Assert::true($builder->getDefinition('fac2')->getResultDefinition()->getTag(DI\Extensions\InjectExtension::TagInject)); diff --git a/tests/SearchExtension/all.phpt b/tests/SearchExtension/all.phpt index f2d066d9f..a950d7742 100644 --- a/tests/SearchExtension/all.phpt +++ b/tests/SearchExtension/all.phpt @@ -22,6 +22,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/batches.phpt b/tests/SearchExtension/batches.phpt index 26fda917b..4e4ac6d44 100644 --- a/tests/SearchExtension/batches.phpt +++ b/tests/SearchExtension/batches.phpt @@ -37,4 +37,5 @@ Assert::same([ 'Foo\\ClassBar' => ['foo' => true], 'InterfaceOk1' => ['ok' => true], 'InterfaceOk2' => ['ok' => true], + 'stdClass' => [], ], $services); diff --git a/tests/SearchExtension/classes.phpt b/tests/SearchExtension/classes.phpt index cea9cfac7..4d21f9087 100644 --- a/tests/SearchExtension/classes.phpt +++ b/tests/SearchExtension/classes.phpt @@ -23,6 +23,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -53,6 +54,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -102,4 +104,5 @@ Assert::same([ 'ExtendsStdClass', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/duplicates.phpt b/tests/SearchExtension/duplicates.phpt index d8b2eec98..c52f03a71 100644 --- a/tests/SearchExtension/duplicates.phpt +++ b/tests/SearchExtension/duplicates.phpt @@ -28,4 +28,5 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/files.phpt b/tests/SearchExtension/files.phpt index b72a55147..3fdea3b07 100644 --- a/tests/SearchExtension/files.phpt +++ b/tests/SearchExtension/files.phpt @@ -23,6 +23,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -41,6 +42,7 @@ Assert::same([ 'ExtendsStdClass', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -63,6 +65,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -83,4 +86,5 @@ Assert::same([ 'ExtendsStdClass', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/parents.phpt b/tests/SearchExtension/parents.phpt index bf8bf1254..c36fb78f2 100644 --- a/tests/SearchExtension/parents.phpt +++ b/tests/SearchExtension/parents.phpt @@ -23,6 +23,7 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); @@ -99,4 +100,5 @@ Assert::same([ 'Foo\\ClassBar', 'InterfaceOk1', 'InterfaceOk2', + 'stdClass', ], array_keys($services)); diff --git a/tests/SearchExtension/tags.phpt b/tests/SearchExtension/tags.phpt index 7c2002658..6064d0be1 100644 --- a/tests/SearchExtension/tags.phpt +++ b/tests/SearchExtension/tags.phpt @@ -24,6 +24,7 @@ Assert::same([ 'ExtendsStdClass' => ['a' => 1, 'b' => 2], 'InterfaceOk1' => ['a' => 1, 'b' => 2], 'InterfaceOk2' => ['a' => 1, 'b' => 2], + 'stdClass' => [], ], $services); @@ -43,4 +44,5 @@ Assert::same([ 'ExtendsStdClass' => [], 'InterfaceOk1' => [], 'InterfaceOk2' => [], + 'stdClass' => [], ], $services); From 14941613a65c8abe42ff5b87696cef9b53df1436 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 16 May 2024 15:27:40 +0200 Subject: [PATCH 16/26] Reapply "LocatorDefinition: deprecated support for create($name) method (BC break)" This reverts commit 87662ebfb4baabf4788666170b04ba1cde8ed043. --- src/DI/Definitions/LocatorDefinition.php | 4 +++- tests/DI/Compiler.generatedLocator.phpt | 3 ++- tests/DI/Definitions.LocatorDefinition.api.phpt | 12 ++++++------ .../Definitions.LocatorDefinition.render.create.phpt | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/DI/Definitions/LocatorDefinition.php b/src/DI/Definitions/LocatorDefinition.php index fe8b3aecd..f4fee5d66 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -39,7 +39,7 @@ public function setImplement(string $interface): static || (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.", + "Service '%s': Method %s::%s() does not meet the requirements: is create*(), get*() or get(\$name) and is non-static.", $this->getName(), $interface, $method->name, @@ -52,6 +52,8 @@ public function setImplement(string $interface): static "return type of $interface::$method->name()", allowNullable: true, ); + } elseif (str_starts_with($method->name, 'create')) { + trigger_error(sprintf("Service '%s': Method %s::create(\$name) is deprecated, use createFoo().", $this->getName(), $interface), E_USER_DEPRECATED); } } diff --git a/tests/DI/Compiler.generatedLocator.phpt b/tests/DI/Compiler.generatedLocator.phpt index 34816729e..940f25432 100644 --- a/tests/DI/Compiler.generatedLocator.phpt +++ b/tests/DI/Compiler.generatedLocator.phpt @@ -42,7 +42,8 @@ interface LocatorFactoryN } -$container = createContainer(new DI\Compiler, ' +// create($name) is deprecated +$container = @createContainer(new DI\Compiler, ' services: - LoremChild diff --git a/tests/DI/Definitions.LocatorDefinition.api.phpt b/tests/DI/Definitions.LocatorDefinition.api.phpt index 50a771bd0..aaef81002 100644 --- a/tests/DI/Definitions.LocatorDefinition.api.phpt +++ b/tests/DI/Definitions.LocatorDefinition.api.phpt @@ -84,31 +84,31 @@ Assert::exception(function () { 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*(), get*() or get(\$name) 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*(), get*() or get(\$name) 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*(), get*() or get(\$name) 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*(), get*() or get(\$name) 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*(), get*() or get(\$name) and is non-static."); Assert::noError(function () { @@ -121,7 +121,7 @@ Assert::noError(function () { Assert::noError(function () { $def = new LocatorDefinition; - $def->setImplement(Good2::class); + @$def->setImplement(Good2::class); // create($name) is deprecated Assert::same(Good2::class, $def->getImplement()); Assert::same(Good2::class, $def->getType()); }); diff --git a/tests/DI/Definitions.LocatorDefinition.render.create.phpt b/tests/DI/Definitions.LocatorDefinition.render.create.phpt index 4c649129f..833fdbf75 100644 --- a/tests/DI/Definitions.LocatorDefinition.render.create.phpt +++ b/tests/DI/Definitions.LocatorDefinition.render.create.phpt @@ -22,7 +22,7 @@ interface Good test('', function () { $def = new LocatorDefinition; $def->setName('abc'); - $def->setImplement(Good::class); + @$def->setImplement(Good::class); // create($name) is deprecated $def->setReferences(['first' => '@a', 'second' => 'stdClass']); $builder = new Nette\DI\ContainerBuilder; From 08ebd230ab701bd49f2f41fbc352e08ce3a068f9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 12 Sep 2021 23:09:50 +0200 Subject: [PATCH 17/26] opened 4.0-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f8342787f..d217ca664 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.0-dev" } } } From 92d6a813339116ce7e50c49b13d30f29eae0f200 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 11 Dec 2023 17:11:13 +0100 Subject: [PATCH 18/26] 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 | 13 ++- src/DI/Definitions/LocatorDefinition.php | 13 ++- src/DI/Definitions/ServiceDefinition.php | 11 ++ src/DI/Extensions/InjectExtension.php | 8 +- src/DI/Extensions/ServicesExtension.php | 9 +- src/DI/Helpers.php | 32 +++++- src/DI/PhpGenerator.php | 2 +- src/DI/Resolver.php | 68 ++++-------- tests/DI/Compiler.configOverride.phpt | 3 +- .../DI/Compiler.extensionOverride.errors.phpt | 3 +- tests/DI/Compiler.first-class-callable.phpt | 7 +- tests/DI/Compiler.functions.phpt | 4 +- tests/DI/Compiler.generatedFactory.phpt | 9 +- .../ContainerBuilder.autowiring.novalue.phpt | 6 +- tests/DI/ContainerBuilder.create.error.phpt | 104 +++++++++++++----- tests/DI/ContainerBuilder.error.phpt | 34 +++++- tests/DI/ContainerBuilder.recursive.phpt | 3 +- .../ContainerBuilder.resolveTypes.next.phpt | 27 +++-- tests/DI/ContainerBuilder.resolveTypes.phpt | 27 +++-- tests/DI/ContainerBuilder.selfdependency.phpt | 4 +- .../Definitions.AccessorDefinition.api.phpt | 24 ++-- ...efinitions.AccessorDefinition.resolve.phpt | 9 +- .../DI/Definitions.FactoryDefinition.api.phpt | 21 ++-- ...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 | 9 +- 32 files changed, 370 insertions(+), 175 deletions(-) diff --git a/src/DI/Definitions/AccessorDefinition.php b/src/DI/Definitions/AccessorDefinition.php index bd6c1a1a5..5063d2d26 100644 --- a/src/DI/Definitions/AccessorDefinition.php +++ b/src/DI/Definitions/AccessorDefinition.php @@ -28,8 +28,8 @@ public function setImplement(string $interface): static { 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, )); } @@ -44,19 +44,19 @@ public function setImplement(string $interface): static || 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, )); } - Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()"); + Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()", $this->getDescriptor()); return parent::setType($interface); } diff --git a/src/DI/Definitions/Definition.php b/src/DI/Definitions/Definition.php index ca96659b8..5d6bdadb4 100644 --- a/src/DI/Definitions/Definition.php +++ b/src/DI/Definitions/Definition.php @@ -46,6 +46,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 ?'; + } + } + + protected function setType(?string $type): static { if ($this->autowired && $this->notifier && $this->type !== $type) { @@ -56,8 +79,8 @@ protected function setType(?string $type): static $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 ff5e6c537..7ed5eb933 100644 --- a/src/DI/Definitions/FactoryDefinition.php +++ b/src/DI/Definitions/FactoryDefinition.php @@ -37,8 +37,8 @@ public function setImplement(string $interface): static { 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, )); } @@ -47,13 +47,13 @@ public function setImplement(string $interface): static $method = $rc->getMethods()[0] ?? null; if (!$method || $method->isStatic() || $method->name !== self::MethodCreate || 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, )); } - Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()"); + Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()", $this->getDescriptor()); return parent::setType($interface); } @@ -110,7 +110,8 @@ public function resolveType(Nette\DI\Resolver $resolver): void if (!$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 f4fee5d66..4e7170e34 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -25,12 +25,12 @@ final class LocatorDefinition extends Definition public function setImplement(string $interface): static { 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) { @@ -39,8 +39,8 @@ public function setImplement(string $interface): static || (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*(), get*() or get(\$name) and is non-static.", - $this->getName(), + "[%s]\nMethod %s::%s() does not meet the requirements: is create*(), get*() or get(\$name) and is non-static.", + $this->getDescriptor(), $interface, $method->name, )); @@ -50,6 +50,7 @@ public function setImplement(string $interface): static Nette\DI\Helpers::ensureClassType( Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", + $this->getDescriptor(), allowNullable: true, ); } elseif (str_starts_with($method->name, 'create')) { @@ -112,8 +113,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, )); diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 6cec76a86..671a93059 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -36,6 +36,17 @@ 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(); + } + + public function setType(?string $type): static { return parent::setType($type); diff --git a/src/DI/Extensions/InjectExtension.php b/src/DI/Extensions/InjectExtension.php index d26ab92df..534e3025f 100644 --- a/src/DI/Extensions/InjectExtension.php +++ b/src/DI/Extensions/InjectExtension.php @@ -62,7 +62,7 @@ private function updateDefinition(Definitions\ServiceDefinition $def): void } if ($builder) { - self::checkType($class, $property, $type, $builder); + self::checkType($class, $property, $type, $builder, $def); } array_unshift($setups, $inject); } @@ -143,7 +143,7 @@ public static function callInjects(DI\Container $container, object $service): vo } foreach (self::getInjectProperties($service::class) as $property => $type) { - self::checkType($service, $property, $type, $container); + self::checkType($service, $property, $type, $container, null); $service->$property = $container->getByType($type); } } @@ -154,11 +154,13 @@ private static function checkType( string $name, ?string $type, DI\Container|DI\ContainerBuilder $container, + ?Definitions\Definition $def, ): void { if (!$container->getByType($type, throw: false)) { throw new Nette\DI\MissingServiceException(sprintf( - 'Service of type %s required by %s not found. Did you add it to configuration file?', + "%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 38ab845be..8e35ea635 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -53,7 +53,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); @@ -68,7 +68,12 @@ private function loadDefinition(?string $name, \stdClass $config): void $this->{$methods[$config->defType]}($def, $config); $this->updateDefinition($def, $config); } catch (\Throwable $e) { - throw new Nette\DI\InvalidConfigurationException(($name ? "Service '$name': " : '') . $e->getMessage(), 0, $e); + $message = $e->getMessage(); + if ($name && !str_starts_with($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 610239b8d..ee409f700 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -232,17 +232,23 @@ 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->allows('null'))) { - throw new ServiceCreationException(sprintf("%s is expected to not be %sbuilt-in/complex, '%s' given.", ucfirst($hint), $allowNullable ? '' : 'nullable/', $type)); + throw new ServiceCreationException(sprintf("%s%s is expected to not be %sbuilt-in/complex, '%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; @@ -282,4 +288,22 @@ public static function convertType(mixed $value, string $type): mixed $type, )); } + + + public static function entityToString(string|array|Reference $entity, bool $inner = false): string + { + if (is_string($entity)) { + return $entity . ($inner ? '()' : ''); + + } elseif ($entity instanceof Reference) { + return '@' . $entity->getValue(); + + } else { + [$a, $b] = $entity; + return self::entityToString($a instanceof Statement ? $a->getEntity() : $a, inner: true) + . '::' + . $b + . (str_contains($b, '$') ? '' : '()'); + } + } } diff --git a/src/DI/PhpGenerator.php b/src/DI/PhpGenerator.php index bbb4b574c..13ad83863 100644 --- a/src/DI/PhpGenerator.php +++ b/src/DI/PhpGenerator.php @@ -96,7 +96,7 @@ public function generateMethod(Definitions\Definition $def): Php\Method return $method; } catch (\Throwable $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 3e6afc3ff..a83f858fe 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -303,9 +303,21 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo try { $arguments = $this->completeArguments($arguments); + } catch (ServiceCreationException $e) { - if (!str_contains($e->getMessage(), ' (used in')) { - $e->setMessage($e->getMessage() . " (used in {$this->entityToString($entity)})"); + if (!str_contains($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; @@ -442,25 +454,16 @@ public function addDependency(\ReflectionClass|\ReflectionFunctionAbstract|strin private function completeException(\Throwable $e, Definition $def): ServiceCreationException { - if ($e instanceof ServiceCreationException && str_starts_with($e->getMessage(), "Service '")) { + $message = $e->getMessage(); + if ($e instanceof ServiceCreationException && str_starts_with($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) @@ -468,33 +471,6 @@ private function completeException(\Throwable $e, Definition $def): ServiceCreat } - private function entityToString($entity): string - { - $referenceToText = fn(Reference $ref): string => $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 (!str_contains($entity[1], '$')) { - $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 { @@ -606,20 +582,20 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab } catch (MissingServiceException) { $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->isOptional()) { 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 ed31e4996..e0f5d2680 100644 --- a/tests/DI/Compiler.configOverride.phpt +++ b/tests/DI/Compiler.configOverride.phpt @@ -59,5 +59,6 @@ $compiler->addConfig([ Assert::exception( fn() => $compiler->setClassName($class)->compile(), DI\InvalidConfigurationException::class, - "Service 's3': missing original definition for alteration.", + "[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.first-class-callable.phpt b/tests/DI/Compiler.first-class-callable.phpt index c2601dff0..d4f51376e 100644 --- a/tests/DI/Compiler.first-class-callable.phpt +++ b/tests/DI/Compiler.first-class-callable.phpt @@ -50,7 +50,8 @@ Assert::exception(function () { $compiler = new DI\Compiler; $compiler->addConfig($loader->load(Tester\FileMock::create($config, 'neon'))); $compiler->compile(); -}, Nette\DI\ServiceCreationException::class, 'Service of type Closure: Cannot create closure for Service(...)'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Service] +Cannot create closure for Service(...)'); // Invalid callable 2 @@ -63,4 +64,6 @@ Assert::exception(function () { $compiler = new DI\Compiler; $compiler->addConfig($loader->load(Tester\FileMock::create($config, 'neon'))); $compiler->compile(); -}, Nette\DI\ServiceCreationException::class, 'Service of type Service: Cannot create closure for Service(...) (used in Service::__construct())'); +}, Nette\DI\ServiceCreationException::class, '[Service of type Service] +Cannot create closure for Service(...) +Related to Service::__construct().'); diff --git a/tests/DI/Compiler.functions.phpt b/tests/DI/Compiler.functions.phpt index 0afb97a42..08831b44c 100644 --- a/tests/DI/Compiler.functions.phpt +++ b/tests/DI/Compiler.functions.phpt @@ -93,5 +93,7 @@ Assert::exception( - Service(bool(123, 10)) '), Nette\InvalidStateException::class, - 'Service of type Service: Function bool() expects 1 parameter, 2 given. (used in Service::__construct())', + '[Service of type Service] +Function bool() expects 1 parameter, 2 given. +Related to Service::__construct().', ); diff --git a/tests/DI/Compiler.generatedFactory.phpt b/tests/DI/Compiler.generatedFactory.phpt index 81068a106..eac013e90 100644 --- a/tests/DI/Compiler.generatedFactory.phpt +++ b/tests/DI/Compiler.generatedFactory.phpt @@ -266,7 +266,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."); @@ -287,7 +288,8 @@ Assert::exception(function () { $builder->addFactoryDefinition('one') ->setImplement(Bad4::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Cannot implement Bad4::create(): factory method parameters (\$baz) are not matching Bad3::__construct() parameters (\$bar). Did you mean to use '\$bar' in factory method?"); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad4] +Cannot implement Bad4::create(): factory method parameters (\$baz) are not matching Bad3::__construct() parameters (\$bar). Did you mean to use '\$bar' in factory method?"); @@ -308,4 +310,5 @@ Assert::exception(function () { $builder->addFactoryDefinition('one') ->setImplement(Bad6::class); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'one' (type of Bad6): Cannot implement Bad6::create(): factory method parameters (\$baz) are not matching Bad5::__construct() parameters (\$xxx)."); +}, Nette\InvalidStateException::class, "[Service 'one' of type Bad6] +Cannot implement Bad6::create(): factory method parameters (\$baz) are not matching Bad5::__construct() parameters (\$xxx)."); diff --git a/tests/DI/ContainerBuilder.autowiring.novalue.phpt b/tests/DI/ContainerBuilder.autowiring.novalue.phpt index 1a9b7cca4..3aee26fda 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.create.error.phpt b/tests/DI/ContainerBuilder.create.error.phpt index 0af01c12c..858c22231 100644 --- a/tests/DI/ContainerBuilder.create.error.phpt +++ b/tests/DI/ContainerBuilder.create.error.phpt @@ -17,14 +17,16 @@ require __DIR__ . '/../bootstrap.php'; testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setType('X')->setCreator('Unknown'); -}, Nette\InvalidArgumentException::class, "Service 'one': Class or interface 'X' not found."); +}, Nette\InvalidArgumentException::class, "[Service 'one'] +Class or interface 'X' not found."); testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition(null)->setCreator('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."); testException('', function () { @@ -32,7 +34,8 @@ testException('', function () { $builder->addDefinition('one')->setCreator('@two'); $builder->addDefinition('two')->setCreator('Unknown'); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'two': Class 'Unknown' not found."); +}, Nette\InvalidStateException::class, "[Service 'two'] +Class 'Unknown' not found."); testException('', function () { @@ -40,28 +43,32 @@ testException('', function () { $builder->addDefinition('one')->setCreator(new Reference('two')); $builder->addDefinition('two')->setCreator('Unknown'); $builder->complete(); -}, Nette\InvalidStateException::class, "Service 'two': Class 'Unknown' not found."); +}, Nette\InvalidStateException::class, "[Service 'two'] +Class 'Unknown' not found."); testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('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."); testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('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."); testException('', 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."); @@ -74,7 +81,8 @@ testException('', function () { $builder = new DI\ContainerBuilder; $builder->addFactoryDefinition('one') ->setImplement(Bad4::class); -}, Nette\InvalidStateException::class, 'Return type of Bad4::create() is not declared.'); +}, Nette\InvalidStateException::class, "[Service 'one'] +Return type of Bad4::create() is not declared."); interface Bad5 @@ -87,7 +95,8 @@ testException('', 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 @@ -101,7 +110,8 @@ testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('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 @@ -115,7 +125,8 @@ testException('', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('Bad7::create'); $builder->complete(); -}, Nette\DI\ServiceCreationException::class, "Service 'one': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'one'] +Unknown service type, specify it or declare return type of factory method."); class Bad8 @@ -129,7 +140,8 @@ testException('', 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 @@ -143,13 +155,17 @@ testException('fail in argument', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator(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()."); testException('fail in argument', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator(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 @@ -163,7 +179,8 @@ testException('abstract class cannot be instantiated', 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 @@ -177,7 +194,8 @@ testException('trait cannot be instantiated', function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('one')->setCreator('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 @@ -201,7 +219,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()."); testException('forced autowiring fail', function () { @@ -211,7 +231,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()."); testException('autowiring fail in chain', function () { @@ -221,7 +243,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()."); testException('forced autowiring fail in chain', function () { @@ -231,7 +255,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()."); testException('autowiring fail in argument', function () { @@ -241,7 +267,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()."); testException('forced autowiring fail in argument', function () { @@ -251,7 +280,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()."); testException('autowiring fail in chain in argument', function () { @@ -261,7 +292,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()."); testException('forced autowiring fail in chain in argument', function () { @@ -271,7 +305,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()."); testException('forced autowiring fail in property passing', function () { @@ -284,7 +320,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."); testException('autowiring fail in rich property passing', function () { @@ -297,7 +335,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."); testException('autowiring fail in method calling', function () { @@ -310,7 +350,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()."); testException('forced autowiring fail in method calling', function () { @@ -323,7 +365,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."); testException('autowiring fail in rich method calling', function () { @@ -336,4 +380,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.error.phpt b/tests/DI/ContainerBuilder.error.phpt index 33c369aeb..9633cf6e1 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; @@ -21,7 +22,8 @@ $builder->addDefinition('one') Assert::exception( fn() => $builder->complete(), Nette\InvalidStateException::class, - "Service 'one' (type of stdClass): Expected function, method or property name, '1234' given.", + "[Service 'one' of type stdClass] +Expected function, method or property name, '1234' given.", ); @@ -48,5 +50,33 @@ $builder->addDefinition('one') Assert::exception( fn() => $builder->complete(), Nette\InvalidStateException::class, - "Service 'one' (type of stdClass): Missing argument for \$prop[].", + "[Service 'one' of type stdClass] +Missing argument for \$prop[].", +); + + + +$builder = new DI\ContainerBuilder; +$builder->addDefinition(null) + ->setFactory([new Statement('Unknown'), 'foo']); + +Assert::exception( + fn() => $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( + fn() => $builder->complete(), + Nette\DI\ServiceCreationException::class, + "[Service of type stdClass] +Class 'Unknown' not found.", ); diff --git a/tests/DI/ContainerBuilder.recursive.phpt b/tests/DI/ContainerBuilder.recursive.phpt index 8ee4fdb7a..cbd0f08f2 100644 --- a/tests/DI/ContainerBuilder.recursive.phpt +++ b/tests/DI/ContainerBuilder.recursive.phpt @@ -30,5 +30,6 @@ $builder->addDefinition('two') Assert::exception( fn() => createContainer($builder), Nette\DI\ServiceCreationException::class, - "Service 'two': Circular reference detected for services: one, two.", + "[Service 'two'] +Circular reference detected for services: one, two.", ); diff --git a/tests/DI/ContainerBuilder.resolveTypes.next.phpt b/tests/DI/ContainerBuilder.resolveTypes.next.phpt index f19770352..8ce3c0761 100644 --- a/tests/DI/ContainerBuilder.resolveTypes.next.phpt +++ b/tests/DI/ContainerBuilder.resolveTypes.next.phpt @@ -148,56 +148,64 @@ Assert::exception(function () { $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createScalarPhpDoc']), 'next']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createScalar']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createObjectPhpDoc']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createObject']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createObjectNullable']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createMixedPhpDoc']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createMixed']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createGeneric']), 'next']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Class 'T' not found. +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Class 'T' not found. Check the return type of Factory::createGeneric()."); Assert::exception(function () { @@ -205,4 +213,5 @@ Assert::exception(function () { $builder->addDefinition('a') ->setCreator([new Statement([Factory::class, 'createUnion']), 'next']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createUnion() is expected to not be built-in/complex, 'stdClass|array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createUnion() is expected to not be built-in/complex, 'stdClass|array' given."); diff --git a/tests/DI/ContainerBuilder.resolveTypes.phpt b/tests/DI/ContainerBuilder.resolveTypes.phpt index 67cb3ea47..29b8b32d7 100644 --- a/tests/DI/ContainerBuilder.resolveTypes.phpt +++ b/tests/DI/ContainerBuilder.resolveTypes.phpt @@ -139,56 +139,64 @@ Assert::exception(function () { $builder->addDefinition('a') ->setCreator([Factory::class, 'createScalarPhpDoc']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createScalar']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createObjectPhpDoc']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createObject']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createObjectNullable']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createMixedPhpDoc']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createMixed']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Unknown service type, specify it or declare return type of factory method."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Unknown service type, specify it or declare return type of factory method."); Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') ->setCreator([Factory::class, 'createGeneric']); $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "Service 'a': Class 'T' not found. +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Class 'T' not found. Check the return type of Factory::createGeneric()."); Assert::exception(function () { @@ -196,4 +204,5 @@ Assert::exception(function () { $builder->addDefinition('a') ->setCreator([Factory::class, 'createUnion']); $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createUnion() is expected to not be built-in/complex, 'stdClass|array' given."); +}, Nette\DI\ServiceCreationException::class, "[Service 'a'] +Return type of Factory::createUnion() is expected to not be built-in/complex, 'stdClass|array' given."); diff --git a/tests/DI/ContainerBuilder.selfdependency.phpt b/tests/DI/ContainerBuilder.selfdependency.phpt index be8c5b77b..360871d0f 100644 --- a/tests/DI/ContainerBuilder.selfdependency.phpt +++ b/tests/DI/ContainerBuilder.selfdependency.phpt @@ -28,5 +28,7 @@ $builder->addDefinition(null) Assert::exception( fn() => 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?', + '[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 adb04bebf..23c3ec302 100644 --- a/tests/DI/Definitions.AccessorDefinition.api.phpt +++ b/tests/DI/Definitions.AccessorDefinition.api.phpt @@ -54,49 +54,57 @@ interface Good1 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::exception(function () { $def = new AccessorDefinition; $def->setImplement(Bad6::class); -}, Nette\DI\ServiceCreationException::class, 'Return type of Bad6::get() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Return type of Bad6::get() is not declared.'); Assert::noError(function () { diff --git a/tests/DI/Definitions.AccessorDefinition.resolve.phpt b/tests/DI/Definitions.AccessorDefinition.resolve.phpt index 014e2caf0..d03e48290 100644 --- a/tests/DI/Definitions.AccessorDefinition.resolve.phpt +++ b/tests/DI/Definitions.AccessorDefinition.resolve.phpt @@ -28,13 +28,15 @@ 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 () { $def = new AccessorDefinition; $def->setImplement(Bad1::class); -}, Nette\DI\ServiceCreationException::class, 'Return type of Bad1::get() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Return type of Bad1::get() is not declared.'); Assert::noError(function () { @@ -63,4 +65,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 787435a5c..6a7a6c370 100644 --- a/tests/DI/Definitions.FactoryDefinition.api.phpt +++ b/tests/DI/Definitions.FactoryDefinition.api.phpt @@ -48,43 +48,50 @@ interface Good1 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::exception(function () { $def = new FactoryDefinition; $def->setImplement(Bad5::class); -}, Nette\DI\ServiceCreationException::class, 'Return type of Bad5::create() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Return type of Bad5::create() is not declared.'); Assert::noError(function () { diff --git a/tests/DI/Definitions.FactoryDefinition.resolve.phpt b/tests/DI/Definitions.FactoryDefinition.resolve.phpt index 97f280234..79410b22a 100644 --- a/tests/DI/Definitions.FactoryDefinition.resolve.phpt +++ b/tests/DI/Definitions.FactoryDefinition.resolve.phpt @@ -28,13 +28,15 @@ 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 () { $def = new FactoryDefinition; $def->setImplement(Bad1::class); -}, Nette\DI\ServiceCreationException::class, 'Return type of Bad1::create() is not declared.'); +}, Nette\DI\ServiceCreationException::class, '[Service ?] +Return type of Bad1::create() is not declared.'); Assert::noError(function () { @@ -75,4 +77,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 c5dd8fc1f..6b5d6c122 100644 --- a/tests/DI/Definitions.ImportedDefinition.phpt +++ b/tests/DI/Definitions.ImportedDefinition.phpt @@ -16,14 +16,16 @@ require __DIR__ . '/../bootstrap.php'; testException('Unknown class', 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."); testException('Unknown type', 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 aaef81002..daa75622a 100644 --- a/tests/DI/Definitions.LocatorDefinition.api.phpt +++ b/tests/DI/Definitions.LocatorDefinition.api.phpt @@ -66,49 +66,57 @@ interface Good3 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad2::create() does not meet the requirements: is create*(), get*() or get($name) 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad3::get() does not meet the requirements: is create*(), get*() or get($name) 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad4::foo() does not meet the requirements: is create*(), get*() or get($name) 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad5::get() does not meet the requirements: is create*(), get*() or get($name) 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*(), get*() or get(\$name) and is non-static."); +}, Nette\InvalidArgumentException::class, '[Service ?] +Method Bad6::get() does not meet the requirements: is create*(), get*() or get($name) and is non-static.'); Assert::noError(function () { diff --git a/tests/DI/Definitions.LocatorDefinition.resolve.phpt b/tests/DI/Definitions.LocatorDefinition.resolve.phpt index 4d1383ed0..7696e71bd 100644 --- a/tests/DI/Definitions.LocatorDefinition.resolve.phpt +++ b/tests/DI/Definitions.LocatorDefinition.resolve.phpt @@ -33,7 +33,8 @@ testException('', 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 c433a6473..2a413dcb9 100644 --- a/tests/DI/Definitions.ServiceDefinition.phpt +++ b/tests/DI/Definitions.ServiceDefinition.phpt @@ -17,7 +17,8 @@ require __DIR__ . '/../bootstrap.php'; testException('Unknown type', 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('type versus entity I.', function () { $def = new ServiceDefinition; diff --git a/tests/DI/InjectExtension.errors.phpt b/tests/DI/InjectExtension.errors.phpt index e2dc16c88..2d1d94172 100644 --- a/tests/DI/InjectExtension.errors.phpt +++ b/tests/DI/InjectExtension.errors.phpt @@ -58,7 +58,9 @@ services: create: ServiceA inject: yes '); -}, InvalidStateException::class, 'Service of type DateTimeImmutable required by ServiceA::$a 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::exception(function () { diff --git a/tests/DI/Resolver.autowireArguments.errors.phpt b/tests/DI/Resolver.autowireArguments.errors.phpt index 700a48bfb..1cf867667 100644 --- a/tests/DI/Resolver.autowireArguments.errors.phpt +++ b/tests/DI/Resolver.autowireArguments.errors.phpt @@ -21,7 +21,8 @@ Assert::exception( function () {}, ), Nette\DI\ServiceCreationException::class, - 'Service of type stdClass required by $x in {closure}() not found. Did you add it to configuration file?', + 'Service of type stdClass required by $x in {closure}() not found. +Did you add it to configuration file?', ); @@ -33,7 +34,8 @@ Assert::exception( function () {}, ), Nette\DI\ServiceCreationException::class, - "Class 'Foo' required by \$x in {closure}() not found. Check the parameter type and 'use' statements.", + "Class 'Foo' required by \$x in {closure}() not found. +Check the parameter type and 'use' statements.", ); @@ -69,7 +71,8 @@ Assert::exception( fn($type) => $type === Test::class ? new Test : null, ), Nette\DI\ServiceCreationException::class, - 'Service of type stdClass required by $arg in {closure}() not found. Did you add it to configuration file?', + 'Service of type stdClass required by $arg in {closure}() not found. +Did you add it to configuration file?', ); From 12830603cb6bd965d1c620af6cd8c3804aaf90ed Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 16 Jan 2023 04:12:22 +0100 Subject: [PATCH 19/26] annotations @return are no longer supported (BC break) --- src/DI/Helpers.php | 14 ---- src/DI/Resolver.php | 18 +--- .../ContainerBuilder.resolveTypes.next.phpt | 82 ------------------- tests/DI/ContainerBuilder.resolveTypes.phpt | 82 ------------------- tests/DI/Helpers.getReturnType.phpt | 30 ------- tests/DI/fixtures/Helpers.getReturnType.php | 49 ----------- 6 files changed, 4 insertions(+), 271 deletions(-) delete mode 100644 tests/DI/Helpers.getReturnType.phpt delete mode 100644 tests/DI/fixtures/Helpers.getReturnType.php diff --git a/src/DI/Helpers.php b/src/DI/Helpers.php index ee409f700..3597a3820 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -218,20 +218,6 @@ public static function parseAnnotation(\Reflector $ref, string $name): ?string } - public static function getReturnTypeAnnotation(\ReflectionFunctionAbstract $func): ?Type - { - $type = preg_replace('#[|\s].*#', '', (string) self::parseAnnotation($func, 'return')); - if (!$type || $type === 'object' || $type === 'mixed') { - return null; - } elseif ($func instanceof \ReflectionMethod) { - $type = $type === '$this' ? 'static' : $type; - $type = Reflection::expandClassName($type, $func->getDeclaringClass()); - } - - return Type::fromString($type); - } - - public static function ensureClassType( ?Type $type, string $hint, diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index a83f858fe..09c2fe1c8 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -122,20 +122,10 @@ public function resolveEntityType(Statement $statement): ?string $this->addDependency($reflection); - $type = Nette\Utils\Type::fromReflection($reflection) ?? ($annotation = Helpers::getReturnTypeAnnotation($reflection)); - if ($type && !in_array($type->getSingleName(), ['object', 'mixed'], strict: true)) { - 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)), - allowNullable: true, - ); - } - - return null; + $type = Nette\Utils\Type::fromReflection($reflection); + return $type && !in_array($type->getSingleName(), ['object', 'mixed'], strict: true) + ? Helpers::ensureClassType($type, sprintf('return type of %s()', Callback::toString($entity)), allowNullable: true) + : null; } elseif ($entity instanceof Reference) { // alias or factory return $this->resolveReferenceType($entity); diff --git a/tests/DI/ContainerBuilder.resolveTypes.next.phpt b/tests/DI/ContainerBuilder.resolveTypes.next.phpt index 8ce3c0761..7f078690c 100644 --- a/tests/DI/ContainerBuilder.resolveTypes.next.phpt +++ b/tests/DI/ContainerBuilder.resolveTypes.next.phpt @@ -24,52 +24,24 @@ class Lorem class Factory { - /** @return Lorem */ - public function createClassPhpDoc() - { - return []; - } - - public function createClass(): Lorem { return []; } - /** @return Lorem|null */ - public function createNullableClassPhpDoc() - { - return []; - } - - public function createNullableClass(): ?Lorem { return []; } - /** @return array */ - public function createScalarPhpDoc() - { - return []; - } - - public function createScalar(): array { return []; } - /** @return object */ - public function createObjectPhpDoc() - { - return (object) null; - } - - public function createObject(): object { return (object) null; @@ -82,13 +54,6 @@ class Factory } - /** @return mixed */ - public function createMixedPhpDoc() - { - return (object) null; - } - - public function createMixed(): mixed { return (object) null; @@ -115,13 +80,6 @@ class Factory require __DIR__ . '/../bootstrap.php'; -Assert::noError(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createClassPhpDoc']), 'next']); - $container = @createContainer($builder); // @return is deprecated -}); - Assert::noError(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -129,13 +87,6 @@ Assert::noError(function () { $container = createContainer($builder); }); -Assert::noError(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createNullableClassPhpDoc']), 'next']); - $container = @createContainer($builder); // @return is deprecated -}); - Assert::noError(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -143,14 +94,6 @@ Assert::noError(function () { $container = createContainer($builder); }); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createScalarPhpDoc']), 'next']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -159,14 +102,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createObjectPhpDoc']), 'next']); - $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Unknown service type, specify it or declare return type of factory method."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -183,14 +118,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Unknown service type, specify it or declare return type of factory method."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createMixedPhpDoc']), 'next']); - $container = createContainer($builder); -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Unknown service type, specify it or declare return type of factory method."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -199,15 +126,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Unknown service type, specify it or declare return type of factory method."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([new Statement([Factory::class, 'createGeneric']), 'next']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Class 'T' not found. -Check the return type of Factory::createGeneric()."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') diff --git a/tests/DI/ContainerBuilder.resolveTypes.phpt b/tests/DI/ContainerBuilder.resolveTypes.phpt index 29b8b32d7..09c639bf5 100644 --- a/tests/DI/ContainerBuilder.resolveTypes.phpt +++ b/tests/DI/ContainerBuilder.resolveTypes.phpt @@ -15,52 +15,24 @@ use Tester\Assert; class Factory { - /** @return stdClass */ - public function createClassPhpDoc() - { - return []; - } - - public function createClass(): stdClass { return []; } - /** @return stdClass|null */ - public function createNullableClassPhpDoc() - { - return []; - } - - public function createNullableClass(): ?stdClass { return []; } - /** @return array */ - public function createScalarPhpDoc() - { - return []; - } - - public function createScalar(): array { return []; } - /** @return object */ - public function createObjectPhpDoc() - { - return (object) null; - } - - public function createObject(): object { return (object) null; @@ -73,13 +45,6 @@ class Factory } - /** @return mixed */ - public function createMixedPhpDoc() - { - return (object) null; - } - - public function createMixed(): mixed { return (object) null; @@ -106,13 +71,6 @@ class Factory require __DIR__ . '/../bootstrap.php'; -Assert::noError(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createClassPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}); - Assert::noError(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -120,13 +78,6 @@ Assert::noError(function () { $container = createContainer($builder); }); -Assert::noError(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createNullableClassPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}); - Assert::noError(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -134,14 +85,6 @@ Assert::noError(function () { $container = createContainer($builder); }); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createScalarPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Return type of Factory::createScalarPhpDoc() is expected to not be built-in/complex, 'array' given."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -150,14 +93,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Return type of Factory::createScalar() is expected to not be built-in/complex, 'array' given."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createObjectPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Unknown service type, specify it or declare return type of factory method."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -174,14 +109,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Unknown service type, specify it or declare return type of factory method."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createMixedPhpDoc']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Unknown service type, specify it or declare return type of factory method."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') @@ -190,15 +117,6 @@ Assert::exception(function () { }, Nette\DI\ServiceCreationException::class, "[Service 'a'] Unknown service type, specify it or declare return type of factory method."); -Assert::exception(function () { - $builder = new DI\ContainerBuilder; - $builder->addDefinition('a') - ->setCreator([Factory::class, 'createGeneric']); - $container = @createContainer($builder); // @return is deprecated -}, Nette\DI\ServiceCreationException::class, "[Service 'a'] -Class 'T' not found. -Check the return type of Factory::createGeneric()."); - Assert::exception(function () { $builder = new DI\ContainerBuilder; $builder->addDefinition('a') diff --git a/tests/DI/Helpers.getReturnType.phpt b/tests/DI/Helpers.getReturnType.phpt deleted file mode 100644 index 9a0a47cbf..000000000 --- a/tests/DI/Helpers.getReturnType.phpt +++ /dev/null @@ -1,30 +0,0 @@ - Date: Thu, 14 Dec 2023 13:48:45 +0100 Subject: [PATCH 20/26] annotations @var are no longer supported (BC break) --- src/DI/Extensions/InjectExtension.php | 14 +-- tests/DI/Container.inject.properties.phpt | 16 +-- tests/DI/InjectExtension.basic.phpt | 12 +- tests/DI/InjectExtension.errors.phpt | 8 +- ...Extension.getInjectProperties().php74.phpt | 39 ------- ...Extension.getInjectProperties().php80.phpt | 38 ------- ...InjectExtension.getInjectProperties().phpt | 104 ++++++------------ ...xtension.getInjectProperties().traits.phpt | 4 +- 8 files changed, 54 insertions(+), 181 deletions(-) delete mode 100644 tests/DI/InjectExtension.getInjectProperties().php74.phpt delete mode 100644 tests/DI/InjectExtension.getInjectProperties().php80.phpt diff --git a/src/DI/Extensions/InjectExtension.php b/src/DI/Extensions/InjectExtension.php index 534e3025f..8b0979010 100644 --- a/src/DI/Extensions/InjectExtension.php +++ b/src/DI/Extensions/InjectExtension.php @@ -112,19 +112,15 @@ public static function getInjectProperties(string $class): array { $res = []; foreach ((new \ReflectionClass($class))->getProperties() as $rp) { - $hasAttr = $rp->getAttributes(DI\Attributes\Inject::class); - if ($hasAttr || DI\Helpers::parseAnnotation($rp, 'inject') !== null) { + if ( + $rp->getAttributes(DI\Attributes\Inject::class) + || DI\Helpers::parseAnnotation($rp, 'inject') !== null + ) { if (!$rp->isPublic() || $rp->isStatic() || $rp->isReadOnly()) { throw new Nette\InvalidStateException(sprintf('Property %s for injection must not be static, readonly and must be public.', Reflection::toString($rp))); } - $type = Nette\Utils\Type::fromReflection($rp); - if (!$type && !$hasAttr && ($annotation = DI\Helpers::parseAnnotation($rp, 'var'))) { - $annotation = Reflection::expandClassName($annotation, Reflection::getPropertyDeclaringClass($rp)); - $type = Nette\Utils\Type::fromString($annotation); - } - - $res[$rp->getName()] = DI\Helpers::ensureClassType($type, 'type of property ' . Reflection::toString($rp)); + $res[$rp->getName()] = DI\Helpers::ensureClassType(Nette\Utils\Type::fromReflection($rp), 'type of property ' . Reflection::toString($rp)); } } diff --git a/tests/DI/Container.inject.properties.phpt b/tests/DI/Container.inject.properties.phpt index b633c0f8b..8b0203539 100644 --- a/tests/DI/Container.inject.properties.phpt +++ b/tests/DI/Container.inject.properties.phpt @@ -23,20 +23,17 @@ class Foo implements IFoo class Test1 { - /** @inject @var stdClass */ - public $varA; - - /** @var stdClass @inject */ - public $varB; + /** @inject */ + public stdClass $varA; } class Test2 extends Test1 { - /** @var stdClass @inject */ - public $varC; + /** @inject */ + public stdClass $varC; - /** @var IFoo @inject */ - public $varD; + /** @inject */ + public IFoo $varD; } @@ -52,6 +49,5 @@ $container = createContainer($builder); $test = new Test2; $container->callInjects($test); Assert::type(stdClass::class, $test->varA); -Assert::type(stdClass::class, $test->varB); Assert::type(stdClass::class, $test->varC); Assert::type(Foo::class, $test->varD); diff --git a/tests/DI/InjectExtension.basic.phpt b/tests/DI/InjectExtension.basic.phpt index 1b6d3fb52..8cb99fc8b 100644 --- a/tests/DI/InjectExtension.basic.phpt +++ b/tests/DI/InjectExtension.basic.phpt @@ -31,8 +31,8 @@ class ConcreteDependencyB extends AbstractDependency class ParentClass { - /** @var stdClass @inject */ - public $a; + /** @inject */ + public stdClass $a; public function injectA() @@ -47,11 +47,11 @@ class ParentClass class Service extends ParentClass { - /** @var stdClass @inject */ - public $c; + /** @inject */ + public stdClass $c; - /** @var AbstractDependency @inject */ - public $e; + /** @inject */ + public AbstractDependency $e; public function injectC() diff --git a/tests/DI/InjectExtension.errors.phpt b/tests/DI/InjectExtension.errors.phpt index 2d1d94172..0bd382707 100644 --- a/tests/DI/InjectExtension.errors.phpt +++ b/tests/DI/InjectExtension.errors.phpt @@ -16,15 +16,15 @@ require __DIR__ . '/../bootstrap.php'; class ServiceA { - /** @var DateTimeImmutable @inject */ - public $a; + /** @inject */ + public DateTimeImmutable $a; } class ServiceB { - /** @var Unknown @inject */ - public $a; + /** @inject */ + public Unknown $a; } diff --git a/tests/DI/InjectExtension.getInjectProperties().php74.phpt b/tests/DI/InjectExtension.getInjectProperties().php74.phpt deleted file mode 100644 index 33a5cb95e..000000000 --- a/tests/DI/InjectExtension.getInjectProperties().php74.phpt +++ /dev/null @@ -1,39 +0,0 @@ - A\AInjected::class, - 'varC' => A\AInjected::class, - ], InjectExtension::getInjectProperties(A\AClass::class)); -} diff --git a/tests/DI/InjectExtension.getInjectProperties().php80.phpt b/tests/DI/InjectExtension.getInjectProperties().php80.phpt deleted file mode 100644 index e24155f65..000000000 --- a/tests/DI/InjectExtension.getInjectProperties().php80.phpt +++ /dev/null @@ -1,38 +0,0 @@ - InjectExtension::getInjectProperties(AClass::class), - Nette\InvalidStateException::class, - "Type of property AClass::\$var is expected to not be nullable/built-in/complex, 'AClass|stdClass' given.", -); - -Assert::same([ - 'varA' => 'stdClass', -], InjectExtension::getInjectProperties(EClass::class)); diff --git a/tests/DI/InjectExtension.getInjectProperties().phpt b/tests/DI/InjectExtension.getInjectProperties().phpt index 79c00b86e..65bf65a04 100644 --- a/tests/DI/InjectExtension.getInjectProperties().phpt +++ b/tests/DI/InjectExtension.getInjectProperties().phpt @@ -6,93 +6,51 @@ declare(strict_types=1); -namespace A -{ - class AClass - { - /** @var AInjected @inject */ - public $varA; +use Nette\DI\Attributes\Inject; +use Nette\DI\Extensions\InjectExtension; +use Tester\Assert; + - /** @var B\BInjected @inject */ - public $varB; +class AClass +{ + /** @inject */ + public AInjected $varA; - /** @var AInjected @inject */ - public $varC; + /** @inject */ + public BInjected $varB; - /** @var AInjected */ - public $varD; - } + public $varD; - class AInjected - { - } + #[Inject] + public stdClass $varF; } -namespace A\B +class BadClass { - use A; - - class BClass extends A\AClass - { - /** @var BInjected @inject */ - public $varF; - } - - class BInjected - { - } + /** @inject */ + public AClass|stdClass $var; } -namespace C +class AInjected { - use A\AInjected; - use A\B; - use C\CInjected as CAlias; - - class CClass - { - /** @var AInjected @inject */ - public $var1; - - /** @var B\BInjected @inject */ - public $var2; - - /** @var CAlias @inject */ - public $var3; - - /** @var CInjected @inject */ - public $var4; - } - - class CInjected - { - } } -namespace { - use Nette\DI\Extensions\InjectExtension; - use Tester\Assert; +class BInjected +{ +} - require __DIR__ . '/../bootstrap.php'; +require __DIR__ . '/../bootstrap.php'; - Assert::same([ - 'varA' => A\AInjected::class, - 'varB' => A\B\BInjected::class, - 'varC' => A\AInjected::class, - ], InjectExtension::getInjectProperties(A\AClass::class)); - Assert::same([ - 'varA' => A\AInjected::class, - 'varB' => A\B\BInjected::class, - 'varC' => A\AInjected::class, - 'varF' => A\B\BInjected::class, - ], InjectExtension::getInjectProperties(A\B\BClass::class)); +Assert::same([ + 'varA' => AInjected::class, + 'varB' => BInjected::class, + 'varF' => stdClass::class, +], InjectExtension::getInjectProperties(AClass::class)); - Assert::same([ - 'var1' => A\AInjected::class, - 'var2' => A\B\BInjected::class, - 'var3' => C\CInjected::class, - 'var4' => C\CInjected::class, - ], InjectExtension::getInjectProperties(C\CClass::class)); -} +Assert::exception( + fn() => InjectExtension::getInjectProperties(BadClass::class), + Nette\InvalidStateException::class, + "Type of property BadClass::\$var is expected to not be nullable/built-in/complex, 'AClass|stdClass' given.", +); diff --git a/tests/DI/InjectExtension.getInjectProperties().traits.phpt b/tests/DI/InjectExtension.getInjectProperties().traits.phpt index 1fc6c211e..42b625067 100644 --- a/tests/DI/InjectExtension.getInjectProperties().traits.phpt +++ b/tests/DI/InjectExtension.getInjectProperties().traits.phpt @@ -19,8 +19,8 @@ namespace B trait BTrait { - /** @var AInjected @inject */ - public $varA; + /** @inject */ + public AInjected $varA; } } From 8bf9be575e36646db3f2c06d6b083e9332e95b85 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 11 Dec 2023 17:12:33 +0100 Subject: [PATCH 21/26] removed support for three ... dots --- src/DI/Config/Adapters/NeonAdapter.php | 5 ----- tests/DI/NeonAdapter.preprocess.phpt | 11 ----------- 2 files changed, 16 deletions(-) diff --git a/src/DI/Config/Adapters/NeonAdapter.php b/src/DI/Config/Adapters/NeonAdapter.php index d806cd319..90c629c50 100644 --- a/src/DI/Config/Adapters/NeonAdapter.php +++ b/src/DI/Config/Adapters/NeonAdapter.php @@ -180,11 +180,6 @@ private function removeUnderscoreVisitor(Neon\Node $node): void if ($attr->value instanceof Neon\Node\LiteralNode && $attr->value->value === '_') { unset($node->attributes[$i]); $index = true; - - } elseif ($attr->value instanceof Neon\Node\LiteralNode && $attr->value->value === '...') { - trigger_error("Replace ... with _ in configuration file '$this->file'.", E_USER_DEPRECATED); - unset($node->attributes[$i]); - $index = true; } } } diff --git a/tests/DI/NeonAdapter.preprocess.phpt b/tests/DI/NeonAdapter.preprocess.phpt index 7b59be016..350dbd259 100644 --- a/tests/DI/NeonAdapter.preprocess.phpt +++ b/tests/DI/NeonAdapter.preprocess.phpt @@ -70,17 +70,6 @@ Assert::equal( ); -// ... deprecated -$data = @$adapter->load(Tester\FileMock::create(' -- Class(arg1, ..., [...]) -', 'neon')); - -Assert::equal( - [new Statement('Class', ['arg1', 2 => ['...']])], - $data, -); - - // @ escaping $data = @$adapter->load(Tester\FileMock::create(' - @@double From 2c035576a8385ffcbc8c5d6f69cab86c0f364d75 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 20 Dec 2022 00:31:03 +0100 Subject: [PATCH 22/26] removed compatibility for old class names --- src/DI/Config/Adapter.php | 3 -- src/DI/Definitions/ServiceDefinition.php | 3 -- src/DI/Definitions/Statement.php | 3 -- src/compatibility.php | 39 ------------------------ 4 files changed, 48 deletions(-) delete mode 100644 src/compatibility.php diff --git a/src/DI/Config/Adapter.php b/src/DI/Config/Adapter.php index 25e9cecd8..a9b7004b6 100644 --- a/src/DI/Config/Adapter.php +++ b/src/DI/Config/Adapter.php @@ -20,6 +20,3 @@ interface Adapter */ function load(string $file): array; } - - -class_exists(IAdapter::class); diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 671a93059..5efa41c60 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -215,6 +215,3 @@ public function __clone() $this->setup = unserialize(serialize($this->setup)); } } - - -class_exists(Nette\DI\ServiceDefinition::class); diff --git a/src/DI/Definitions/Statement.php b/src/DI/Definitions/Statement.php index cd80f6d44..62e141202 100644 --- a/src/DI/Definitions/Statement.php +++ b/src/DI/Definitions/Statement.php @@ -63,6 +63,3 @@ public function getEntity(): string|array|Definition|Reference|null return $this->entity; } } - - -class_exists(Nette\DI\Statement::class); diff --git a/src/compatibility.php b/src/compatibility.php deleted file mode 100644 index f0a0e53fe..000000000 --- a/src/compatibility.php +++ /dev/null @@ -1,39 +0,0 @@ - Date: Fri, 24 Sep 2021 14:07:27 +0200 Subject: [PATCH 23/26] deprecated magic properties (BC break) --- src/DI/Definitions/ServiceDefinition.php | 6 +++--- src/DI/Definitions/Statement.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 5efa41c60..0bf42d48e 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -16,9 +16,9 @@ /** * Definition of standard service. * - * @property string|null $class - * @property Statement $factory - * @property Statement[] $setup + * @property-deprecated string|null $class + * @property-deprecated Statement $factory + * @property-deprecated Statement[] $setup */ final class ServiceDefinition extends Definition { diff --git a/src/DI/Definitions/Statement.php b/src/DI/Definitions/Statement.php index 62e141202..8010b4b95 100644 --- a/src/DI/Definitions/Statement.php +++ b/src/DI/Definitions/Statement.php @@ -15,7 +15,7 @@ /** * Assignment or calling statement. * - * @property string|array|Definition|Reference|null $entity + * @property-deprecated string|array|Definition|Reference|null $entity */ final class Statement implements Nette\Schema\DynamicParameter { From 4db1c1828cb075939476fe50c029a8846a19eadd Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 6 Feb 2024 00:52:18 +0100 Subject: [PATCH 24/26] LocatorDefinition: removed support for create($name) methods (BC break) --- src/DI/Definitions/LocatorDefinition.php | 8 +-- tests/DI/Compiler.generatedLocator.phpt | 23 +------ .../DI/Definitions.LocatorDefinition.api.phpt | 13 ---- ...tions.LocatorDefinition.render.create.phpt | 64 ------------------- 4 files changed, 4 insertions(+), 104 deletions(-) delete mode 100644 tests/DI/Definitions.LocatorDefinition.render.create.phpt diff --git a/src/DI/Definitions/LocatorDefinition.php b/src/DI/Definitions/LocatorDefinition.php index 4e7170e34..6c9d96628 100644 --- a/src/DI/Definitions/LocatorDefinition.php +++ b/src/DI/Definitions/LocatorDefinition.php @@ -35,7 +35,7 @@ public function setImplement(string $interface): static foreach ($methods as $method) { if ($method->isStatic() || !( - (preg_match('#^(get|create)$#', $method->name) && $method->getNumberOfParameters() === 1) + ($method->name === 'get' && $method->getNumberOfParameters() === 1) || (preg_match('#^(get|create)[A-Z]#', $method->name) && $method->getNumberOfParameters() === 0) )) { throw new Nette\InvalidArgumentException(sprintf( @@ -46,15 +46,13 @@ public function setImplement(string $interface): static )); } - if ($method->getNumberOfParameters() === 0) { + if ($method->name !== 'get') { Nette\DI\Helpers::ensureClassType( Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", $this->getDescriptor(), allowNullable: true, ); - } elseif (str_starts_with($method->name, 'create')) { - trigger_error(sprintf("Service '%s': Method %s::create(\$name) is deprecated, use createFoo().", $this->getName(), $interface), E_USER_DEPRECATED); } } @@ -155,7 +153,7 @@ public function generateMethod(Nette\PhpGenerator\Method $method, Nette\DI\PhpGe $methodInner->setBody('if (!isset($this->mapping[$name])) { ' . ($nullable ? 'return null;' : 'throw new Nette\DI\MissingServiceException("Service \'$name\' is not defined.");') . ' } -return $this->container->' . $m[1] . 'Service($this->mapping[$name]);') +return $this->container->getService($this->mapping[$name]);') ->addParameter('name'); } elseif (isset($this->references[$name])) { diff --git a/tests/DI/Compiler.generatedLocator.phpt b/tests/DI/Compiler.generatedLocator.phpt index 940f25432..d33b219c5 100644 --- a/tests/DI/Compiler.generatedLocator.phpt +++ b/tests/DI/Compiler.generatedLocator.phpt @@ -42,8 +42,7 @@ interface LocatorFactoryN } -// create($name) is deprecated -$container = @createContainer(new DI\Compiler, ' +$container = createContainer(new DI\Compiler, ' services: - LoremChild @@ -64,10 +63,7 @@ services: one: Locator(a: @lorem1, b: LoremChild) two: Locator(tagged: a) - three: LocatorFactory(a: @lorem1, b: LoremChild) - four: LocatorFactory(tagged: b) five: LocatorN(tagged: a) - six: LocatorFactoryN(tagged: a) seven: Locator(a: @lorem1) eight: Locator(a: LoremChild()) '); @@ -96,29 +92,12 @@ Assert::exception( "Service '3' is not defined.", ); -// factory -$three = $container->getService('three'); -Assert::type(Lorem::class, $three->create('a')); -Assert::type(LoremChild::class, $three->create('b')); -Assert::notSame($three->create('a'), $three->create('a')); - -// tagged factory -$four = $container->getService('four'); -Assert::type(Lorem::class, $four->create('3')); -Assert::notSame($container->getService('lorem3'), $four->create('3')); - // nullable accessor $five = $container->getService('five'); Assert::type(Lorem::class, $five->get('1')); Assert::type(Lorem::class, $five->get('2')); Assert::null($five->get('3')); -// nullable factory -$six = $container->getService('six'); -Assert::type(Lorem::class, $six->create('1')); -Assert::type(Lorem::class, $six->create('2')); -Assert::null($six->create('3')); - // accessor with one service $one = $container->getService('seven'); Assert::type(Lorem::class, $one->get('a')); diff --git a/tests/DI/Definitions.LocatorDefinition.api.phpt b/tests/DI/Definitions.LocatorDefinition.api.phpt index daa75622a..826eaa44e 100644 --- a/tests/DI/Definitions.LocatorDefinition.api.phpt +++ b/tests/DI/Definitions.LocatorDefinition.api.phpt @@ -50,11 +50,6 @@ interface Good1 public function get($name); } -interface Good2 -{ - public function create($name); -} - interface Good3 { public function createA(): stdClass; @@ -127,14 +122,6 @@ Assert::noError(function () { }); -Assert::noError(function () { - $def = new LocatorDefinition; - @$def->setImplement(Good2::class); // create($name) is deprecated - Assert::same(Good2::class, $def->getImplement()); - Assert::same(Good2::class, $def->getType()); -}); - - Assert::noError(function () { $def = new LocatorDefinition; $def->setImplement(Good3::class); diff --git a/tests/DI/Definitions.LocatorDefinition.render.create.phpt b/tests/DI/Definitions.LocatorDefinition.render.create.phpt deleted file mode 100644 index 833fdbf75..000000000 --- a/tests/DI/Definitions.LocatorDefinition.render.create.phpt +++ /dev/null @@ -1,64 +0,0 @@ -setName('abc'); - @$def->setImplement(Good::class); // create($name) is deprecated - $def->setReferences(['first' => '@a', 'second' => 'stdClass']); - - $builder = new Nette\DI\ContainerBuilder; - $builder->addDefinition('a')->setType(stdClass::class); - $resolver = new Nette\DI\Resolver($builder); - - $resolver->resolveDefinition($def); - $resolver->completeDefinition($def); - - $phpGenerator = new Nette\DI\PhpGenerator($builder); - $method = $phpGenerator->generateMethod($def); - - Assert::match( - <<<'XX' - public function createServiceAbc(): Good - { - return new class ($this) implements Good { - private $mapping = ['first' => 'a', 'second' => 'a']; - - - public function __construct( - private $container, - ) { - } - - - public function create($name): stdClass - { - if (!isset($this->mapping[$name])) { - throw new Nette\DI\MissingServiceException("Service '$name' is not defined."); - } - return $this->container->createService($this->mapping[$name]); - } - }; - } - XX, - $method->__toString(), - ); -}); From 965c20381666e21fb0f14a8d2c5bc5fb68b27483 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 6 Apr 2024 21:02:45 +0200 Subject: [PATCH 25/26] annotations @inject is deprecated (BC break) --- src/DI/Helpers.php | 3 +++ tests/DI/Container.inject.properties.phpt | 7 +++--- tests/DI/Helpers.parseAnnotation().phpt | 22 ++++++++----------- tests/DI/InjectExtension.basic.phpt | 7 +++--- tests/DI/InjectExtension.errors.phpt | 11 +++++----- ...InjectExtension.getInjectProperties().phpt | 6 ++--- ...xtension.getInjectProperties().traits.phpt | 3 ++- 7 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/DI/Helpers.php b/src/DI/Helpers.php index 3597a3820..16d6b44ba 100644 --- a/src/DI/Helpers.php +++ b/src/DI/Helpers.php @@ -202,6 +202,7 @@ public static function prefixServiceName(mixed $config, string $namespace): mixe /** * Returns an annotation value. + * @deprecated */ public static function parseAnnotation(\Reflector $ref, string $name): ?string { @@ -211,6 +212,8 @@ public static function parseAnnotation(\Reflector $ref, string $name): ?string $re = '#[\s*]@' . preg_quote($name, '#') . '(?=\s|$)(?:[ \t]+([^@\s]\S*))?#'; if ($ref->getDocComment() && preg_match($re, trim($ref->getDocComment(), '/*'), $m)) { + $alt = $name === 'inject' ? '#[Nette\DI\Attributes\Inject]' : 'alternative'; + trigger_error("Annotation @$name is deprecated, use $alt (used in " . Reflection::toString($ref) . ')', E_USER_DEPRECATED); return $m[1] ?? ''; } diff --git a/tests/DI/Container.inject.properties.phpt b/tests/DI/Container.inject.properties.phpt index 8b0203539..73324665c 100644 --- a/tests/DI/Container.inject.properties.phpt +++ b/tests/DI/Container.inject.properties.phpt @@ -7,6 +7,7 @@ declare(strict_types=1); use Nette\DI; +use Nette\DI\Attributes\Inject; use Tester\Assert; @@ -23,16 +24,16 @@ class Foo implements IFoo class Test1 { - /** @inject */ + #[Inject] public stdClass $varA; } class Test2 extends Test1 { - /** @inject */ + #[Inject] public stdClass $varC; - /** @inject */ + #[Inject] public IFoo $varD; } diff --git a/tests/DI/Helpers.parseAnnotation().phpt b/tests/DI/Helpers.parseAnnotation().phpt index 3406024bd..44115be43 100644 --- a/tests/DI/Helpers.parseAnnotation().phpt +++ b/tests/DI/Helpers.parseAnnotation().phpt @@ -1,9 +1,5 @@ Date: Wed, 15 Dec 2021 19:45:29 +0100 Subject: [PATCH 26/26] Resolver: processing of functions like not() moved to PhpGenerator --- src/DI/PhpGenerator.php | 9 +++++++++ src/DI/Resolver.php | 10 ---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/DI/PhpGenerator.php b/src/DI/PhpGenerator.php index 13ad83863..e475cd085 100644 --- a/src/DI/PhpGenerator.php +++ b/src/DI/PhpGenerator.php @@ -113,6 +113,15 @@ public function formatStatement(Statement $statement): string case is_string($entity) && str_contains($entity, '?'): // PHP literal return $this->formatPhp($entity, $arguments); + case $entity === 'not': + return $this->formatPhp('!(?)', $arguments); + + case $entity === 'bool': + case $entity === 'int': + case $entity === 'float': + case $entity === 'string': + return $this->formatPhp('?::?(?, ?)', [Helpers::class, 'convertType', $arguments[0], $entity]); + case is_string($entity): // create class return $arguments ? $this->formatPhp("new $entity(...?:)", [$arguments]) diff --git a/src/DI/Resolver.php b/src/DI/Resolver.php index 09c2fe1c8..819fc34ee 100644 --- a/src/DI/Resolver.php +++ b/src/DI/Resolver.php @@ -192,13 +192,6 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo break; case $entity === 'not': - if (count($arguments) !== 1) { - throw new ServiceCreationException(sprintf('Function %s() expects 1 parameter, %s given.', $entity, count($arguments))); - } - - $entity = ['', '!']; - break; - case $entity === 'bool': case $entity === 'int': case $entity === 'float': @@ -206,9 +199,6 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo if (count($arguments) !== 1) { throw new ServiceCreationException(sprintf('Function %s() expects 1 parameter, %s given.', $entity, count($arguments))); } - - $arguments = [$arguments[0], $entity]; - $entity = [Helpers::class, 'convertType']; break; case is_string($entity): // create class