Skip to content

Commit

Permalink
exception messages use [Service ...]\n format
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 29, 2021
1 parent 2db3144 commit 089abc7
Show file tree
Hide file tree
Showing 28 changed files with 234 additions and 120 deletions.
6 changes: 3 additions & 3 deletions src/DI/Definitions/AccessorDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ final class AccessorDefinition extends Definition
public function setImplement(string $type)
{
if (!interface_exists($type)) {
throw new Nette\InvalidArgumentException("Service '{$this->getName()}': Interface '$type' not found.");
throw new Nette\InvalidArgumentException($this->getDescriptiveName() . "\nInterface '$type' not found.");
}
$rc = new \ReflectionClass($type);

Expand All @@ -40,9 +40,9 @@ public function setImplement(string $type)
|| $method->getName() !== self::METHOD_GET
|| count($rc->getMethods()) > 1
) {
throw new Nette\InvalidArgumentException("Service '{$this->getName()}': Interface $type must have just one non-static method get().");
throw new Nette\InvalidArgumentException($this->getDescriptiveName() . "\nInterface $type must have just one non-static method get().");
} elseif ($method->getNumberOfParameters()) {
throw new Nette\InvalidArgumentException("Service '{$this->getName()}': Method $type::get() must have no parameters.");
throw new Nette\InvalidArgumentException($this->getDescriptiveName() . "\nMethod $type::get() must have no parameters.");
}
return parent::setType($type);
}
Expand Down
22 changes: 21 additions & 1 deletion src/DI/Definitions/Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ final public function getName(): ?string
}


final public function isAnonymous(): bool
{
return !$this->name || ctype_digit($this->name);
}


public function getDescriptiveName(): string
{
if (!$this->isAnonymous()) {
return "[Service '$this->name'" . ($this->type ? " of type $this->type" : '') . ']';

} elseif ($this->type) {
return "[Service of type $this->type]";

} else {
return '[Service ?]';
}
}


/** @return static */
protected function setType(?string $type)
{
Expand All @@ -64,7 +84,7 @@ protected function setType(?string $type)
if ($type === null) {
$this->type = null;
} elseif (!class_exists($type) && !interface_exists($type)) {
throw new Nette\InvalidArgumentException("Service '$this->name': Class or interface '$type' not found.");
throw new Nette\InvalidArgumentException($this->getDescriptiveName() . "\nClass or interface '$type' not found.");
} else {
$this->type = Nette\DI\Helpers::normalizeClass($type);
}
Expand Down
4 changes: 2 additions & 2 deletions src/DI/Definitions/FactoryDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ public function __construct()
public function setImplement(string $type)
{
if (!interface_exists($type)) {
throw new Nette\InvalidArgumentException("Service '{$this->getName()}': Interface '$type' not found.");
throw new Nette\InvalidArgumentException($this->getDescriptiveName() . "\nInterface '$type' not found.");
}
$rc = new \ReflectionClass($type);
$method = $rc->getMethods()[0] ?? null;
if (!$method || $method->isStatic() || $method->name !== self::METHOD_CREATE || count($rc->getMethods()) > 1) {
throw new Nette\InvalidArgumentException("Service '{$this->getName()}': Interface $type must have just one non-static method create().");
throw new Nette\InvalidArgumentException($this->getDescriptiveName() . "\nInterface $type must have just one non-static method create().");
}
return parent::setType($type);
}
Expand Down
16 changes: 7 additions & 9 deletions src/DI/Definitions/LocatorDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,22 @@ final class LocatorDefinition extends Definition
public function setImplement(string $type)
{
if (!interface_exists($type)) {
throw new Nette\InvalidArgumentException(sprintf("Service '%s': Interface '%s' not found.", $this->getName(), $type));
throw new Nette\InvalidArgumentException($this->getDescriptiveName() . "\nInterface '$type' not found.");
}
$methods = (new \ReflectionClass($type))->getMethods();
if (!$methods) {
throw new Nette\InvalidArgumentException(sprintf("Service '%s': Interface %s must have at least one method.", $this->getName(), $type));
throw new Nette\InvalidArgumentException($this->getDescriptiveName() . "\nInterface $type must have at least one method.");
}

foreach ($methods as $method) {
if ($method->isStatic() || !(
(preg_match('#^(get|create)$#', $method->name) && $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(),
$type,
$method->name
));
throw new Nette\InvalidArgumentException(
$this->getDescriptiveName()
. "\nMethod $type::{$method->name}() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static.",
);
}
}
return parent::setType($type);
Expand Down Expand Up @@ -104,7 +102,7 @@ public function complete(Nette\DI\Resolver $resolver): void
$this->references = [];
foreach ($resolver->getContainerBuilder()->findByTag($this->tagged) as $name => $tag) {
if (isset($this->references[$tag])) {
trigger_error("Service '{$this->getName()}': duplicated tag '$this->tagged' with value '$tag'.", E_USER_NOTICE);
trigger_error($this->getDescriptiveName() . "\nDuplicated tag '$this->tagged' with value '$tag'.", E_USER_NOTICE);
}
$this->references[$tag] = new Reference($name);
}
Expand Down
18 changes: 18 additions & 0 deletions src/DI/Definitions/ServiceDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,24 @@ public function __clone()
$this->factory = unserialize(serialize($this->factory));
$this->setup = unserialize(serialize($this->setup));
}


public function getDescriptiveName(): string
{
$entity = $this->getEntity();
if ($entity && $this->isAnonymous() && !$this->getType()) {
if ($entity instanceof Reference) {
$entity = '@' . $entity->getValue();
} elseif (is_array($entity)) {
if ($entity[0] instanceof Reference) {
$entity[0] = '@' . $entity[0]->getValue();
}
$entity = is_string($entity[0]) ? implode('::', $entity) : $entity[1];
}
return "[Service $entity]";
}
return parent::getDescriptiveName();
}
}


Expand Down
2 changes: 1 addition & 1 deletion src/DI/Extensions/DIExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ private function restrictTypes(Nette\PhpGenerator\ClassType $class): void
private function initializeTaggedServices(): void
{
foreach (array_filter($this->getContainerBuilder()->findByTag('run')) as $name => $on) {
trigger_error("Tag 'run' used in service '$name' definition is deprecated.", E_USER_DEPRECATED);
trigger_error("[Service '$name']\nTag 'run' is deprecated.", E_USER_DEPRECATED);
$this->initialization->addBody('$this->getService(?);', [$name]);
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/DI/Extensions/ServicesExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,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);
Expand All @@ -69,8 +69,13 @@ private function loadDefinition(?string $name, \stdClass $config): void
];
$this->{$methods[$config->defType]}($def, $config);
$this->updateDefinition($def, $config);

} catch (\Exception $e) {
throw new Nette\DI\InvalidConfigurationException(($name ? "Service '$name': " : '') . $e->getMessage(), 0, $e);
$message = $e->getMessage();
if ($name && !Nette\Utils\Strings::startsWith($message, '[Service ')) {
$message = "[Service '$name']\n$message";
}
throw new Nette\DI\InvalidConfigurationException($message, 0, $e);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/DI/PhpGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public function generateMethod(Definitions\Definition $def): Php\Method
return $method;

} catch (\Exception $e) {
throw new ServiceCreationException("Service '$name': " . $e->getMessage(), 0, $e);
throw new ServiceCreationException($this->getDescriptiveName() . "\n" . $e->getMessage(), 0, $e);
}
}

Expand Down
32 changes: 12 additions & 20 deletions src/DI/Resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -412,24 +412,15 @@ public function addDependency($dep)

private function completeException(\Exception $e, Definition $def): ServiceCreationException
{
if ($e instanceof ServiceCreationException && Strings::startsWith($e->getMessage(), "Service '")) {
$message = $e->getMessage();
if ($e instanceof ServiceCreationException && Strings::startsWith($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::", '', $message);
}
$message .= $type
? str_replace("$type::", '', $e->getMessage())
: $e->getMessage();
$message = $def->getDescriptiveName() . "\n" . $message;

return $e instanceof ServiceCreationException
? $e->setMessage($message)
Expand All @@ -439,25 +430,26 @@ private function completeException(\Exception $e, Definition $def): ServiceCreat

private function entityToString($entity): string
{
$referenceToText = function (Reference $ref): string {
$referenceToText = function (Reference $ref): ?string {
return $ref->isSelf() && $this->currentService
? '@' . $this->currentService->getName()
? null
: '@' . $ref->getValue();
};

if (is_string($entity)) {
return $entity . '::__construct()';

} elseif ($entity instanceof Reference) {
$entity = $referenceToText($entity);
return $referenceToText($entity);

} elseif (is_array($entity)) {
if (strpos($entity[1], '$') === false) {
$entity[1] .= '()';
}
if ($entity[0] instanceof Reference) {
$entity[0] = $referenceToText($entity[0]);
} elseif (!is_string($entity[0])) {
return $entity[1];
}
return implode('::', $entity);
return is_string($entity[0]) ? implode('::', $entity) : $entity[1];
}
return (string) $entity;
}
Expand Down
3 changes: 2 additions & 1 deletion tests/DI/Compiler.configOverride.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ $compiler->addConfig([
Assert::exception(function () use ($compiler, $class) {
$compiler->setClassName($class)
->compile();
}, DI\InvalidConfigurationException::class, "Service 's3': missing original definition for alteration.");
}, DI\InvalidConfigurationException::class, "[Service 's3']
Missing original definition for alteration.");
3 changes: 2 additions & 1 deletion tests/DI/Compiler.extensionOverride.errors.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
3 changes: 2 additions & 1 deletion tests/DI/Compiler.functions.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,5 @@ Assert::exception(function () {
services:
- Service(bool(123, 10))
');
}, Nette\InvalidStateException::class, 'Service of type Service: Function bool() expects at most 1 parameter, 2 given. (used in __construct())');
}, Nette\InvalidStateException::class, '[Service of type Service]
Function bool() expects at most 1 parameter, 2 given. (used in __construct())');
9 changes: 6 additions & 3 deletions tests/DI/Compiler.generatedFactory.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ 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' of type Bad2]
Type hint for \$bar in create() doesn't match type hint in Bad1 constructor.");



Expand All @@ -317,7 +318,8 @@ Assert::exception(function () {
->getResultDefinition()
->setFactory('Bad3');
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'one' (type of Bad4): Unused parameter \$baz when implementing method create(), did you mean \$bar?");
}, Nette\InvalidStateException::class, "[Service 'one' of type Bad4]
Unused parameter \$baz when implementing method create(), did you mean \$bar?");



Expand All @@ -340,7 +342,8 @@ Assert::exception(function () {
->getResultDefinition()
->setFactory('Bad5');
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'one' (type of Bad6): Unused parameter \$baz when implementing method create().");
}, Nette\InvalidStateException::class, "[Service 'one' of type Bad6]
Unused parameter \$baz when implementing method create().");



Expand Down
6 changes: 4 additions & 2 deletions tests/DI/Compiler.missingDefinition.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ Assert::throws(function () {
services:
-
');
}, Nette\InvalidStateException::class, 'Factory and type are missing in definition of service.');
}, Nette\InvalidStateException::class, '[Service ?]
Factory and type are missing in definition of service.');


Assert::throws(function () {
createContainer(new DI\Compiler, '
services:
foo:
');
}, Nette\InvalidStateException::class, "Service 'foo': Factory and type are missing in definition of service.");
}, Nette\InvalidStateException::class, "[Service 'foo']
Factory and type are missing in definition of service.");
6 changes: 4 additions & 2 deletions tests/DI/ContainerBuilder.autowiring.novalue.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ 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' of type Foo]
Parameter \$x in __construct() has no class type hint and no default value, so its value must be specified.");


class Bar
Expand All @@ -38,7 +39,8 @@ 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' of type Bar]
Parameter \$x in __construct() has no class type hint and no default value, so its value must be specified.");


class Bar2
Expand Down
6 changes: 4 additions & 2 deletions tests/DI/ContainerBuilder.error.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ $builder->addDefinition('one')

Assert::exception(function () use ($builder) {
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'one' (type of stdClass): Expected function, method or property name, '1234' given.");
}, Nette\InvalidStateException::class, "[Service 'one' of type stdClass]
Expected function, method or property name, '1234' given.");



Expand All @@ -43,4 +44,5 @@ $builder->addDefinition('one')

Assert::exception(function () use ($builder) {
$builder->complete();
}, Nette\InvalidStateException::class, "Service 'one' (type of stdClass): Missing argument for \$prop[].");
}, Nette\InvalidStateException::class, "[Service 'one' of type stdClass]
Missing argument for \$prop[].");

0 comments on commit 089abc7

Please sign in to comment.