Skip to content

Commit

Permalink
improved error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 31, 2021
1 parent a1c22cb commit e14684d
Show file tree
Hide file tree
Showing 21 changed files with 96 additions and 58 deletions.
5 changes: 4 additions & 1 deletion src/DI/Autowiring.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ public function getByType(string $type, bool $throw = false): ?string
$types = $this->highPriority;
if (empty($types[$type])) {
if ($throw) {
throw new MissingServiceException("Service of type '$type' not found.");
if (!class_exists($type) && !interface_exists($type)) {
throw new MissingServiceException(sprintf("Service of type '%s' not found. Check the class name because it cannot be found.", $type));
}
throw new MissingServiceException(sprintf('Service of type %s not found. Did you add it to configuration file?', $type));
}
return null;

Expand Down
2 changes: 1 addition & 1 deletion src/DI/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ public function getByType(string $type, bool $throw = true)

} elseif ($throw) {
if (!class_exists($type) && !interface_exists($type)) {
throw new MissingServiceException("Service of type '$type' not found. Check class name because it cannot be found.");
throw new MissingServiceException(sprintf("Service of type '%s' not found. Check the class name because it cannot be found.", $type));
}
foreach ($this->methods as $method => $foo) {
$methodType = (new \ReflectionMethod(static::class, $method))->getReturnType()->getName();
Expand Down
8 changes: 6 additions & 2 deletions src/DI/Definitions/AccessorDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,13 @@ public function complete(Nette\DI\Resolver $resolver): void
$returnType = Nette\DI\Helpers::getReturnType($method);

if (!$returnType) {
throw new ServiceCreationException("Method $interface::get() has not return type hint or annotation @return.");
throw new ServiceCreationException(sprintf('Method %s::get() has no return type or annotation @return.', $interface));
} elseif (!class_exists($returnType) && !interface_exists($returnType)) {
throw new ServiceCreationException("Check a type hint or annotation @return of the $interface::get() method, class '$returnType' cannot be found.");
throw new ServiceCreationException(sprintf(
"Class '%s' not found.\nCheck the return type or annotation @return of the %s::get() method.",
$returnType,
$interface
));
}
$this->setReference($returnType);
}
Expand Down
15 changes: 12 additions & 3 deletions src/DI/Definitions/FactoryDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,13 @@ public function resolveType(Nette\DI\Resolver $resolver): void
$method = new \ReflectionMethod($interface, self::METHOD_CREATE);
$returnType = Nette\DI\Helpers::getReturnType($method);
if (!$returnType) {
throw new ServiceCreationException("Method $interface::create() has not return type hint or annotation @return.");
throw new ServiceCreationException(sprintf('Method %s::create() has no return type or annotation @return.', $interface));
} elseif (!class_exists($returnType) && !interface_exists($returnType)) {
throw new ServiceCreationException("Check a type hint or annotation @return of the $interface::create() method, class '$returnType' cannot be found.");
throw new ServiceCreationException(sprintf(
"Class '%s' not found.\nCheck the return type or annotation @return of the %s::create() method.",
$returnType,
$interface
));
}
$resultDef->setType($returnType);
}
Expand Down Expand Up @@ -237,7 +241,12 @@ private function completeParameters(Nette\DI\Resolver $resolver): void
if ($methodHint !== $ctorHint
&& !is_a((string) reset($methodHint), (string) reset($ctorHint), true)
) {
throw new ServiceCreationException("Type hint for \${$param->name} in $interface::create() doesn't match type hint in $class constructor.");
throw new ServiceCreationException(sprintf(
"Type of \$%s in %s::create() doesn't match type in %s constructor.",
$param->name,
$interface,
$class
));
}
$this->resultDefinition->getFactory()->arguments[$ctorParam->getPosition()] = Nette\DI\ContainerBuilder::literal('$' . $ctorParam->name);

Expand Down
16 changes: 13 additions & 3 deletions src/DI/Extensions/InjectExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,21 @@ private static function checkType($class, string $name, ?string $type, $containe
{
$propName = Reflection::toString(new \ReflectionProperty($class, $name));
if (!$type) {
throw new Nette\InvalidStateException("Property $propName has no type hint.");
throw new Nette\InvalidStateException(sprintf('Property %s has no type.', $propName));

} elseif (!class_exists($type) && !interface_exists($type)) {
throw new Nette\InvalidStateException("Class or interface '$type' used in type hint at $propName not found. Check type and 'use' statements.");
throw new Nette\InvalidStateException(sprintf(
"Class '%s' required by %s not found. Check the property type and 'use' statements.",
$type,
$propName
));

} elseif ($container && !$container->getByType($type, false)) {
throw new Nette\DI\MissingServiceException("Service of type $type used in type hint at $propName not found. Did you add it to configuration file?");
throw new Nette\DI\MissingServiceException(sprintf(
'Service of type %s required by %s not found. Did you add it to configuration file?',
$type,
$propName
));
}
}
}
34 changes: 23 additions & 11 deletions src/DI/Resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public function resolveEntityType(Statement $statement): ?string

$type = Helpers::getReturnType($reflection);
if ($type && !class_exists($type) && !interface_exists($type)) {
throw new ServiceCreationException(sprintf("Class or interface '%s' not found. Is return type of %s() correct?", $type, Nette\Utils\Callback::toString($entity)));
throw new ServiceCreationException(sprintf("Class or interface '%s' not found. Check the return type of %s() method.", $type, Nette\Utils\Callback::toString($entity)));
}
return $type;

Expand All @@ -136,11 +136,12 @@ public function resolveEntityType(Statement $statement): ?string

} elseif (is_string($entity)) { // class
if (!class_exists($entity)) {
throw new ServiceCreationException(
throw new ServiceCreationException(sprintf(
interface_exists($entity)
? "Interface $entity can not be used as 'factory', did you mean 'implement'?"
: "Class $entity not found."
);
? "Interface %s can not be used as 'factory', did you mean 'implement'?"
: "Class '%s' not found.",
$entity
));
}
return $entity;
}
Expand Down Expand Up @@ -205,7 +206,7 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo

case is_string($entity): // create class
if (!class_exists($entity)) {
throw new ServiceCreationException("Class $entity not found.");
throw new ServiceCreationException(sprintf("Class '%s' not found.", $entity));
} elseif ((new ReflectionClass($entity))->isAbstract()) {
throw new ServiceCreationException("Class $entity is abstract.");
} elseif (($rm = (new ReflectionClass($entity))->getConstructor()) !== null && !$rm->isPublic()) {
Expand Down Expand Up @@ -556,14 +557,22 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab
} catch (MissingServiceException $e) {
$res = null;
} catch (ServiceCreationException $e) {
throw new ServiceCreationException("{$e->getMessage()} (needed by $desc)", 0, $e);
throw new ServiceCreationException("{$e->getMessage()} (required by $desc)", 0, $e);
}
if ($res !== null || $parameter->allowsNull()) {
return $res;
} elseif (class_exists($type) || interface_exists($type)) {
throw new ServiceCreationException("Service of type $type needed by $desc not found. Did you add it to configuration file?");
throw new ServiceCreationException(sprintf(
'Service of type %s required by %s not found. Did you add it to configuration file?',
$type,
$desc
));
} else {
throw new ServiceCreationException("Class $type needed by $desc not found. Check type hint and 'use' statements.");
throw new ServiceCreationException(sprintf(
"Class '%s' required by %s not found. Check the parameter type and 'use' statements.",
$type,
$desc
));
}

} elseif (
Expand All @@ -587,8 +596,11 @@ private static function autowireArgument(\ReflectionParameter $parameter, callab
: null;

} else {
$tmp = count($types) > 1 ? 'union' : 'no class';
throw new ServiceCreationException("Parameter $desc has $tmp type hint and no default value, so its value must be specified.");
throw new ServiceCreationException(sprintf(
'Parameter %s has %s, so its value must be specified.',
$desc,
count($types) > 1 ? 'union type and no default value' : 'no class type or default value'
));
}
}
}
2 changes: 1 addition & 1 deletion tests/DI/Compiler.generatedFactory.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ Assert::exception(function () {
->getResultDefinition()
->setFactory('Bad1');
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'one' (type of Bad2): Type hint for \$bar in create() doesn't match type hint in Bad1 constructor.");
}, Nette\InvalidStateException::class, "Service 'one' (type of Bad2): Type of \$bar in create() doesn't match type in Bad1 constructor.");



Expand Down
2 changes: 1 addition & 1 deletion tests/DI/Compiler.generatedFactory.polymorphism.phpt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

/**
* Test: Nette\DI\Compiler: generated services factories from interfaces with class type hints in parameters.
* Test: Nette\DI\Compiler: generated services factories from interfaces with class type in parameters.
*/

declare(strict_types=1);
Expand Down
2 changes: 1 addition & 1 deletion tests/DI/Compiler.generatedFactory.scalarParameters.phpt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

/**
* Test: Nette\DI\Compiler: generated services factories from interfaces with scalar type hints in parameters.
* Test: Nette\DI\Compiler: generated services factories from interfaces with scalar type in parameters.
*/

declare(strict_types=1);
Expand Down
2 changes: 1 addition & 1 deletion tests/DI/Container.getByType.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Assert::null($container->getByType('unknown', false));

Assert::exception(function () use ($container) {
$container->getByType('unknown');
}, Nette\DI\MissingServiceException::class, "Service of type 'unknown' not found. Check class name because it cannot be found.");
}, Nette\DI\MissingServiceException::class, "Service of type 'unknown' not found. Check the class name because it cannot be found.");

Assert::exception(function () use ($container) {
$container->getByType('Exception');
Expand Down
4 changes: 2 additions & 2 deletions tests/DI/ContainerBuilder.autowiring.novalue.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Assert::exception(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('foo')->setType('Foo');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'foo' (type of Foo): Parameter \$x in __construct() has no class type hint and no default value, so its value must be specified.");
}, Nette\DI\ServiceCreationException::class, "Service 'foo' (type of Foo): Parameter \$x in __construct() has no class type or default value, so its value must be specified.");


class Bar
Expand All @@ -38,7 +38,7 @@ Assert::exception(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('foo')->setType('Bar');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'foo' (type of Bar): Parameter \$x in __construct() has no class type hint and no default value, so its value must be specified.");
}, Nette\DI\ServiceCreationException::class, "Service 'foo' (type of Bar): Parameter \$x in __construct() has no class type or default value, so its value must be specified.");


class Bar2
Expand Down
20 changes: 10 additions & 10 deletions tests/DI/ContainerBuilder.factory.error.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,23 @@ Assert::exception(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition(null)->setFactory('Unknown');
$builder->complete();
}, Nette\DI\ServiceCreationException::class, 'Service (Unknown::__construct()): Class Unknown not found.');
}, Nette\DI\ServiceCreationException::class, "Service (Unknown::__construct()): Class 'Unknown' not found.");


Assert::exception(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('one')->setFactory('@two');
$builder->addDefinition('two')->setFactory('Unknown');
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'two': Class Unknown not found.");
}, Nette\InvalidStateException::class, "Service 'two': Class 'Unknown' not found.");


Assert::exception(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('one')->setFactory(new Reference('two'));
$builder->addDefinition('two')->setFactory('Unknown');
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'two': Class Unknown not found.");
}, Nette\InvalidStateException::class, "Service 'two': Class 'Unknown' not found.");


Assert::exception(function () {
Expand Down Expand Up @@ -76,7 +76,7 @@ Assert::exception(function () {
$builder->addFactoryDefinition('one')
->setImplement('Bad4');
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Method create() has not return type hint or annotation @return.");
}, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Method create() has no return type or annotation @return.");


interface Bad5
Expand Down Expand Up @@ -146,7 +146,7 @@ Assert::exception(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('one')->setFactory('Good', [new Statement('Unknown')]);
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'one' (type of Good): Class Unknown not found. (used in __construct())");
}, Nette\InvalidStateException::class, "Service 'one' (type of Good): Class 'Unknown' not found. (used in __construct())");

// fail in argument
Assert::exception(function () {
Expand Down Expand Up @@ -208,7 +208,7 @@ services:
b: stdClass
bad: ConstructorParam
');
}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of ConstructorParam): Multiple services of type stdClass found: a, b (needed by \$x in __construct())");
}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of ConstructorParam): Multiple services of type stdClass found: a, b (required by \$x in __construct())");


// forced autowiring fail
Expand All @@ -230,7 +230,7 @@ services:
b: stdClass
bad: MethodParam()::foo()
');
}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of MethodParam): Multiple services of type stdClass found: a, b (needed by \$x in foo())");
}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of MethodParam): Multiple services of type stdClass found: a, b (required by \$x in foo())");


// forced autowiring fail in chain
Expand All @@ -252,7 +252,7 @@ services:
b: stdClass
bad: Good(ConstructorParam())
');
}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (needed by \$x in ConstructorParam::__construct()) (used in __construct())");
}, 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 __construct())");


// forced autowiring fail in argument
Expand All @@ -274,7 +274,7 @@ services:
b: stdClass
bad: Good(MethodParam()::foo())
');
}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of Good): Multiple services of type stdClass found: a, b (needed by \$x in MethodParam::foo()) (used in __construct())");
}, 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 __construct())");


// forced autowiring fail in chain in argument
Expand Down Expand Up @@ -327,7 +327,7 @@ services:
setup:
- foo
');
}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of MethodParam): Multiple services of type stdClass found: a, b (needed by \$x in foo())");
}, Nette\DI\ServiceCreationException::class, "Service 'bad' (type of MethodParam): Multiple services of type stdClass found: a, b (required by \$x in foo())");


// forced autowiring fail in method calling
Expand Down
12 changes: 6 additions & 6 deletions tests/DI/ContainerBuilder.factory.resolveBuiltinTypes.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ namespace
$builder->addDefinition('a')
->setFactory('@factory::createArray');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'a': Class or interface 'array' not found. Is return type of A\\Factory::createArray() correct?");
}, Nette\DI\ServiceCreationException::class, "Service 'a': Class or interface 'array' not found. Check the return type of A\\Factory::createArray() method.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
Expand All @@ -91,7 +91,7 @@ namespace
$builder->addDefinition('c')
->setFactory('@factory::createCallable');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'c': Class or interface 'callable' not found. Is return type of A\\Factory::createCallable() correct?");
}, Nette\DI\ServiceCreationException::class, "Service 'c': Class or interface 'callable' not found. Check the return type of A\\Factory::createCallable() method.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
Expand All @@ -100,7 +100,7 @@ namespace
$builder->addDefinition('s')
->setFactory('@factory::createString');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 's': Class or interface 'string' not found. Is return type of A\\Factory::createString() correct?");
}, Nette\DI\ServiceCreationException::class, "Service 's': Class or interface 'string' not found. Check the return type of A\\Factory::createString() method.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
Expand All @@ -109,7 +109,7 @@ namespace
$builder->addDefinition('i')
->setFactory('@factory::createInt');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'i': Class or interface 'int' not found. Is return type of A\\Factory::createInt() correct?");
}, Nette\DI\ServiceCreationException::class, "Service 'i': Class or interface 'int' not found. Check the return type of A\\Factory::createInt() method.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
Expand All @@ -118,7 +118,7 @@ namespace
$builder->addDefinition('b')
->setFactory('@factory::createBool');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'b': Class or interface 'bool' not found. Is return type of A\\Factory::createBool() correct?");
}, Nette\DI\ServiceCreationException::class, "Service 'b': Class or interface 'bool' not found. Check the return type of A\\Factory::createBool() method.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
Expand All @@ -127,7 +127,7 @@ namespace
$builder->addDefinition('f')
->setFactory('@factory::createFloat');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "Service 'f': Class or interface 'float' not found. Is return type of A\\Factory::createFloat() correct?");
}, Nette\DI\ServiceCreationException::class, "Service 'f': Class or interface 'float' not found. Check the return type of A\\Factory::createFloat() method.");

Assert::exception(function () {
$builder = new DI\ContainerBuilder;
Expand Down
4 changes: 2 additions & 2 deletions tests/DI/ContainerBuilder.getByType.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Assert::null($builder->getByType('Child'));

Assert::exception(function () use ($builder) {
$builder->getByType('Child', true);
}, Nette\DI\MissingServiceException::class, "Service of type 'Child' not found.");
}, Nette\DI\MissingServiceException::class, 'Service of type Child not found. Did you add it to configuration file?');

Assert::same('two', $builder->getByType('Service2'));

Expand All @@ -57,7 +57,7 @@ Assert::exception(function () use ($builder) {

Assert::exception(function () use ($builder) {
$builder->getByType('unknown', true);
}, Nette\DI\MissingServiceException::class, "Service of type 'unknown' not found.");
}, Nette\DI\MissingServiceException::class, "Service of type 'unknown' not found. Check the class name because it cannot be found.");

Assert::null($builder->getByType('unknown'));

Expand Down
2 changes: 1 addition & 1 deletion tests/DI/ContainerBuilder.getDefinitionByType.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Assert::same($definitionOne, $definition);

Assert::exception(function () use ($builder) {
$builder->getDefinitionByType('unknown');
}, Nette\DI\MissingServiceException::class, "Service of type 'unknown' not found.");
}, Nette\DI\MissingServiceException::class, "Service of type 'unknown' not found. Check the class name because it cannot be found.");

Assert::exception(function () use ($builder) {
$builder->getDefinitionByType(SplFileInfo::class);
Expand Down
Loading

0 comments on commit e14684d

Please sign in to comment.