Skip to content

Commit

Permalink
refactoring, union/intersection types renamed to 'complex'
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Jan 16, 2023
1 parent bb08328 commit dad71aa
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src/DI/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ public static function ensureClassType(?Type $type, string $hint): string
{
if (!$type) {
throw new ServiceCreationException(sprintf('%s is not declared.', ucfirst($hint)));
} elseif (!$type->isClass() || $type->isUnion()) {
throw new ServiceCreationException(sprintf("%s is not expected to be nullable/union/intersection/built-in, '%s' given.", ucfirst($hint), $type));
} elseif (!$type->isClass() || $type->allows('null')) {
throw new ServiceCreationException(sprintf("%s is expected to not be nullable/built-in/complex, '%s' given.", ucfirst($hint), $type));
}

$class = $type->getSingleName();
Expand Down
38 changes: 22 additions & 16 deletions src/DI/Resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -625,17 +625,10 @@ public static function autowireArguments(
*/
private static function autowireArgument(\ReflectionParameter $parameter, callable $getter)
{
$method = $parameter->getDeclaringFunction();
$desc = Reflection::toString($parameter);
$type = Nette\Utils\Type::fromReflection($parameter);

if ($parameter->getType() instanceof \ReflectionIntersectionType) {
throw new ServiceCreationException(sprintf(
'Parameter %s has intersection type, so its value must be specified.',
$desc
));

} elseif ($type && $type->isClass()) {
if ($type && $type->isClass()) {
$class = $type->getSingleName();
try {
$res = $getter($class, true);
Expand All @@ -660,13 +653,8 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab
$desc
));
}
} elseif (
$method instanceof \ReflectionMethod
&& $type && $type->getSingleName() === 'array'
&& preg_match('#@param[ \t]+(?|([\w\\\\]+)\[\]|array<int,\s*([\w\\\\]+)>)[ \t]+\$' . $parameter->name . '#', (string) $method->getDocComment(), $m)
&& ($itemType = Reflection::expandClassName($m[1], $method->getDeclaringClass()))
&& (class_exists($itemType) || interface_exists($itemType))
) {

} elseif ($itemType = self::isArrayOf($parameter, $type)) {
return $getter($itemType, false);

} elseif (
Expand All @@ -684,8 +672,26 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab
throw new ServiceCreationException(sprintf(
'Parameter %s has %s, so its value must be specified.',
$desc,
$type && $type->isUnion() ? 'union type and no default value' : 'no class type or default value'
$type && !$type->isSingle() ? 'complex type and no default value' : 'no class type or default value'
));
}
}


private static function isArrayOf(\ReflectionParameter $parameter, ?Nette\Utils\Type $type): ?string
{
$method = $parameter->getDeclaringFunction();
return $method instanceof \ReflectionMethod
&& $type
&& $type->getSingleName() === 'array'
&& preg_match(
'#@param[ \t]+(?|([\w\\\\]+)\[\]|array<int,\s*([\w\\\\]+)>)[ \t]+\$' . $parameter->name . '#',
(string) $method->getDocComment(),
$m
)
&& ($itemType = Reflection::expandClassName($m[1], $method->getDeclaringClass()))
&& (class_exists($itemType) || interface_exists($itemType))
? $itemType
: null;
}
}
2 changes: 1 addition & 1 deletion tests/DI/Container.dynamic.php80.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ $container = new Container;
Assert::exception(function () use ($container) {
@$container->addService('six', function (): stdClass|Closure {}); // @ triggers service should be defined as "imported"
$container->getService('six');
}, Nette\InvalidStateException::class, "Return type of closure is not expected to be nullable/union/intersection/built-in, 'stdClass|Closure' given.");
}, Nette\InvalidStateException::class, "Return type of closure is expected to not be nullable/built-in/complex, 'stdClass|Closure' given.");
2 changes: 1 addition & 1 deletion tests/DI/ContainerBuilder.resolveTypes.php80.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ Assert::exception(function () {
$builder->addDefinition('a')
->setFactory([Factory::class, 'createUnion']);
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createUnion() is not expected to be nullable/union/intersection/built-in, 'stdClass|array' given.");
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createUnion() is expected to not be nullable/built-in/complex, 'stdClass|array' given.");
8 changes: 4 additions & 4 deletions tests/DI/ContainerBuilder.resolveTypes.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -119,21 +119,21 @@ Assert::exception(function () {
$builder->addDefinition('a')
->setFactory([Factory::class, 'createNullableClass']);
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createNullableClass() is not expected to be nullable/union/intersection/built-in, '?stdClass' given.");
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createNullableClass() is expected to not be nullable/built-in/complex, '?stdClass' given.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('a')
->setFactory([Factory::class, 'createScalarPhpDoc']);
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalarPhpDoc() is not expected to be nullable/union/intersection/built-in, 'array' given.");
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalarPhpDoc() is expected to not be nullable/built-in/complex, 'array' given.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('a')
->setFactory([Factory::class, 'createScalar']);
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalar() is not expected to be nullable/union/intersection/built-in, 'array' given.");
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createScalar() is expected to not be nullable/built-in/complex, 'array' given.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
Expand All @@ -154,7 +154,7 @@ Assert::exception(function () {
$builder->addDefinition('a')
->setFactory([Factory::class, 'createObjectNullable']);
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createObjectNullable() is not expected to be nullable/union/intersection/built-in, '?object' given.");
}, Nette\DI\ServiceCreationException::class, "Service 'a': Return type of Factory::createObjectNullable() is expected to not be nullable/built-in/complex, '?object' given.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
Expand Down
2 changes: 1 addition & 1 deletion tests/DI/InjectExtension.getInjectProperties().php80.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class EClass

Assert::exception(function () {
InjectExtension::getInjectProperties(AClass::class);
}, Nette\InvalidStateException::class, "Type of property AClass::\$var is not expected to be nullable/union/intersection/built-in, 'AClass|stdClass' given.");
}, Nette\InvalidStateException::class, "Type of property AClass::\$var is expected to not be nullable/built-in/complex, 'AClass|stdClass' given.");

Assert::same([
'varA' => 'stdClass',
Expand Down
2 changes: 1 addition & 1 deletion tests/DI/Resolver.autowireArguments.80.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Assert::exception(function () {
[],
function () {}
);
}, Nette\InvalidStateException::class, 'Parameter $x in {closure}%a?% has union type and no default value, so its value must be specified.');
}, Nette\InvalidStateException::class, 'Parameter $x in {closure}%a?% has complex type and no default value, so its value must be specified.');

// nullable union
Assert::same(
Expand Down
2 changes: 1 addition & 1 deletion tests/DI/Resolver.autowireArguments.81.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Assert::exception(function () {
[],
function () {}
);
}, Nette\InvalidStateException::class, 'Parameter $x in {closure}%a?% has intersection type, so its value must be specified.');
}, Nette\InvalidStateException::class, 'Parameter $x in {closure}%a?% has complex type and no default value, so its value must be specified.');

// object as default
Assert::same(
Expand Down
33 changes: 33 additions & 0 deletions tests/DI/Resolver.autowireArguments.82.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/**
* Test: Nette\DI\Resolver::autowireArguments()
* @phpVersion 8.2
*/

declare(strict_types=1);

use Nette\DI\Resolver;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


interface Foo
{
}

class Test
{
}


// disjunctive normal form types
Assert::exception(function () {
Resolver::autowireArguments(
new ReflectionFunction(function ((Foo&Test)|string $x) {}),
[],
function () {}
);
}, Nette\InvalidStateException::class, 'Parameter $x in {closure}() has complex type and no default value, so its value must be specified.');

0 comments on commit dad71aa

Please sign in to comment.