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 29, 2021
1 parent 089abc7 commit 4396986
Show file tree
Hide file tree
Showing 23 changed files with 111 additions and 74 deletions.
5 changes: 4 additions & 1 deletion src/DI/Autowiring.php
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("Service of type '$type' not found. Check the class name because it cannot be found.");
}
throw new MissingServiceException("Service of type $type not found.");
}
return null;

Expand Down
2 changes: 1 addition & 1 deletion src/DI/Container.php
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("Service of type '$type' not found. Check the class name because it cannot be found.");
}
foreach ($this->methods as $method => $foo) {
$methodType = (new \ReflectionMethod(static::class, $method))->getReturnType()->getName();
Expand Down
4 changes: 2 additions & 2 deletions src/DI/Definitions/AccessorDefinition.php
Expand Up @@ -90,9 +90,9 @@ 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("Method $interface::get() has no return type or annotation @return.");
} 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("Class '$returnType' not found.\nCheck the return type or annotation @return of the $interface::get() method.");
}
$this->setReference($returnType);
}
Expand Down
6 changes: 3 additions & 3 deletions src/DI/Definitions/FactoryDefinition.php
Expand Up @@ -182,9 +182,9 @@ 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("Method $interface::create() has no return type or annotation @return.");
} 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("Class '$returnType' not found.\nCheck the return type or annotation @return of the $interface::create() method.");
}
$resultDef->setType($returnType);
}
Expand Down Expand Up @@ -237,7 +237,7 @@ 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("Type of \${$param->name} in $interface::create() doesn't match type in $class constructor.");
}
$this->resultDefinition->getFactory()->arguments[$ctorParam->getPosition()] = Nette\DI\ContainerBuilder::literal('$' . $ctorParam->name);

Expand Down
18 changes: 12 additions & 6 deletions src/DI/Extensions/InjectExtension.php
Expand Up @@ -62,7 +62,7 @@ private function updateDefinition(Definitions\ServiceDefinition $def): void
unset($setups[$key]);
}
}
self::checkType($class, $property, $type, $builder);
self::checkType($class, $property, $type, $builder, $def);
array_unshift($setups, $inject);
}

Expand Down Expand Up @@ -154,15 +154,21 @@ public static function callInjects(DI\Container $container, $service): void
* @param object|string $class
* @param DI\Container|DI\ContainerBuilder|null $container
*/
private static function checkType($class, string $name, ?string $type, $container): void
{
private static function checkType(
$class,
string $name,
?string $type,
$container,
Definitions\Definition $def = null
): void {
$propName = Reflection::toString(new \ReflectionProperty($class, $name));
$desc = $def ? $def->getDescriptiveName() . "\n" : '';
if (!$type) {
throw new Nette\InvalidStateException("Property $propName has no type hint.");
throw new Nette\InvalidStateException($desc . "Property $propName has no type.");
} 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($desc . "Class '$type' required by $propName not found.\nCheck the property type and 'use' statements.");
} 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($desc . "Service of type $type required by $propName not found.\nDid you add it to configuration file?");
}
}
}
22 changes: 12 additions & 10 deletions src/DI/Resolver.php
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 @@ -139,7 +139,7 @@ public function resolveEntityType(Statement $statement): ?string
throw new ServiceCreationException(
interface_exists($entity)
? "Interface $entity can not be used as 'factory', did you mean 'implement'?"
: "Class $entity not found."
: "Class '$entity' not found."
);
}
return $entity;
Expand Down Expand Up @@ -205,7 +205,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("Class '$entity' not found.");
} 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 @@ -275,8 +275,8 @@ public function completeStatement(Statement $statement, bool $currentServiceAllo
try {
$arguments = $this->completeArguments($arguments);
} catch (ServiceCreationException $e) {
if (!strpos($e->getMessage(), ' (used in')) {
$e->setMessage($e->getMessage() . " (used in {$this->entityToString($entity)})");
if (!strpos($e->getMessage(), "\nRelated to")) {
$e->setMessage($e->getMessage() . "\nRelated to {$this->entityToString($entity)}.");
}
throw $e;
}
Expand Down Expand Up @@ -548,14 +548,14 @@ 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() . "\nRequired 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("Service of type $type required by $desc not found.\nDid you add it to configuration file?");
} else {
throw new ServiceCreationException("Class $type needed by $desc not found. Check type hint and 'use' statements.");
throw new ServiceCreationException("Class '$type' required by $desc not found.\nCheck the parameter type and 'use' statements.");
}

} elseif (
Expand All @@ -579,8 +579,10 @@ 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.");
$tmp = count($types) > 1
? 'union type and no default value'
: 'no class type or default value';
throw new ServiceCreationException("Parameter $desc has $tmp, so its value must be specified.");
}
}
}
3 changes: 2 additions & 1 deletion tests/DI/Compiler.functions.phpt
Expand Up @@ -86,4 +86,5 @@ Assert::exception(function () {
- Service(bool(123, 10))
');
}, Nette\InvalidStateException::class, '[Service of type Service]
Function bool() expects at most 1 parameter, 2 given. (used in __construct())');
Function bool() expects at most 1 parameter, 2 given.
Related to __construct().');
2 changes: 1 addition & 1 deletion tests/DI/Compiler.generatedFactory.phpt
Expand Up @@ -295,7 +295,7 @@ Assert::exception(function () {
->setFactory('Bad1');
$builder->complete();
}, Nette\InvalidStateException::class, "[Service 'one' of type Bad2]
Type hint for \$bar in create() doesn't match type hint in Bad1 constructor.");
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
@@ -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
@@ -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
4 changes: 2 additions & 2 deletions tests/DI/Compiler.missingDefinition.phpt
Expand Up @@ -14,7 +14,7 @@ Assert::throws(function () {
services:
-
');
}, Nette\InvalidStateException::class, '[Service ?]
}, Nette\DI\ServiceCreationException::class, '[Service ?]
Factory and type are missing in definition of service.');


Expand All @@ -23,5 +23,5 @@ Assert::throws(function () {
services:
foo:
');
}, Nette\InvalidStateException::class, "[Service 'foo']
}, Nette\DI\ServiceCreationException::class, "[Service 'foo']
Factory and type are missing in definition of service.");
2 changes: 1 addition & 1 deletion tests/DI/Container.getByType.phpt
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
Expand Up @@ -25,7 +25,7 @@ Assert::exception(function () {
$builder->addDefinition('foo')->setType('Foo');
$container = createContainer($builder);
}, Nette\DI\ServiceCreationException::class, "[Service 'foo' of type Foo]
Parameter \$x in __construct() has no class type hint and no default value, so its value must be specified.");
Parameter \$x in __construct() has no class type or default value, so its value must be specified.");


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


class Bar2
Expand Down

0 comments on commit 4396986

Please sign in to comment.