Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SearchExtension: ignores classes that do not have autowired parameters #316

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
26a4eec
FactoryDefinition: result definition is added to container [WIP]
dg Feb 4, 2024
c337c4e
Reapply "LocatorDefinition: deprecated support for create($name) meth…
dg May 16, 2024
7e11757
opened 4.0-dev
dg Sep 12, 2021
c4129fc
exception messages use [Service ...]\n format [WIP]
dg Dec 11, 2023
fe8cc2c
annotations @return are no longer supported (BC break)
dg Jan 16, 2023
2b38910
annotations @var are no longer supported (BC break)
dg Dec 14, 2023
cff4960
removed support for three ... dots
dg Dec 11, 2023
3dc4e50
removed compatibility for old class names
dg Dec 19, 2022
f86cef1
deprecated magic properties (BC break)
dg Sep 24, 2021
e782f3c
LocatorDefinition: removed support for create($name) methods (BC break)
dg Feb 5, 2024
d579e30
annotations @inject is deprecated (BC break)
dg Apr 6, 2024
7a40f39
Resolver: processing of functions like not() moved to PhpGenerator
dg Dec 15, 2021
f9ca71d
readme: added jumbo
dg May 16, 2024
ed4a800
SearchExtension: ignores classes that do not have autowired parameters
h4kuna Jun 6, 2024
88c314a
FactoryDefinition: result definition is added to container [WIP]
dg Feb 4, 2024
1494161
Reapply "LocatorDefinition: deprecated support for create($name) meth…
dg May 16, 2024
08ebd23
opened 4.0-dev
dg Sep 12, 2021
92d6a81
exception messages use [Service ...]\n format [WIP]
dg Dec 11, 2023
1283060
annotations @return are no longer supported (BC break)
dg Jan 16, 2023
74e57c3
annotations @var are no longer supported (BC break)
dg Dec 14, 2023
8bf9be5
removed support for three ... dots
dg Dec 11, 2023
2c03557
removed compatibility for old class names
dg Dec 19, 2022
666abf5
deprecated magic properties (BC break)
dg Sep 24, 2021
4db1c18
LocatorDefinition: removed support for create($name) methods (BC break)
dg Feb 5, 2024
965c203
annotations @inject is deprecated (BC break)
dg Apr 6, 2024
5152375
Resolver: processing of functions like not() moved to PhpGenerator
dg Dec 15, 2021
193df4b
Merge branch 'nette:master' into search-check-autowired-parameters
h4kuna Nov 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
"dev-master": "4.0-dev"
}
}
}
3 changes: 0 additions & 3 deletions src/DI/Config/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,3 @@ interface Adapter
*/
function load(string $file): array;
}


class_exists(IAdapter::class);
5 changes: 0 additions & 5 deletions src/DI/Config/Adapters/NeonAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/DI/Definitions/AccessorDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
));
}
Expand All @@ -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);
}

Expand Down
27 changes: 25 additions & 2 deletions src/DI/Definitions/Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down
18 changes: 12 additions & 6 deletions src/DI/Definitions/FactoryDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ final class FactoryDefinition extends Definition
private const MethodCreate = 'create';

private Definition $resultDefinition;
private ?string $reference = null;


public function __construct()
Expand All @@ -36,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,
));
}
Expand All @@ -46,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);
}

Expand Down Expand Up @@ -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));
Expand All @@ -105,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(),
));
Expand Down
19 changes: 10 additions & 9 deletions src/DI/Definitions/LocatorDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,32 @@ 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) {
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(
"Service '%s': Method %s::%s() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static.",
$this->getName(),
"[%s]\nMethod %s::%s() does not meet the requirements: is create*(), get*() or get(\$name) and is non-static.",
$this->getDescriptor(),
$interface,
$method->name,
));
}

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,
);
}
Expand Down Expand Up @@ -110,8 +111,8 @@ public function complete(Nette\DI\Resolver $resolver): void
foreach ($resolver->getContainerBuilder()->findByTag($this->tagged) as $name => $tag) {
if (isset($this->references[$tag])) {
trigger_error(sprintf(
"Service '%s': duplicated tag '%s' with value '%s'.",
$this->getName(),
"[%s]\nDuplicated tag '%s' with value '%s'.",
$this->getDescriptor(),
$this->tagged,
$tag,
));
Expand Down Expand Up @@ -152,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])) {
Expand Down
20 changes: 14 additions & 6 deletions src/DI/Definitions/ServiceDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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);
Expand Down Expand Up @@ -204,6 +215,3 @@ public function __clone()
$this->setup = unserialize(serialize($this->setup));
}
}


class_exists(Nette\DI\ServiceDefinition::class);
5 changes: 1 addition & 4 deletions src/DI/Definitions/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -63,6 +63,3 @@ public function getEntity(): string|array|Definition|Reference|null
return $this->entity;
}
}


class_exists(Nette\DI\Statement::class);
18 changes: 2 additions & 16 deletions src/DI/Extensions/DecoratorExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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)),
);
}
}
31 changes: 12 additions & 19 deletions src/DI/Extensions/InjectExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand All @@ -67,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);
}
Expand Down Expand Up @@ -117,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));
}
}

Expand All @@ -148,7 +139,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);
}
}
Expand All @@ -159,11 +150,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)),
));
Expand Down
Loading
Loading