Skip to content

Commit b58b33f

Browse files
committed
exception messages use [Service ...]\n format [WIP]
added Definition::getDescriptor(), Helpers::entityToString()
1 parent 8a28ff6 commit b58b33f

32 files changed

+379
-173
lines changed

src/DI/Definitions/AccessorDefinition.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public function setImplement(string $interface)
3030
{
3131
if (!interface_exists($interface)) {
3232
throw new Nette\InvalidArgumentException(sprintf(
33-
"Service '%s': Interface '%s' not found.",
34-
$this->getName(),
33+
"[%s]\nInterface '%s' not found.",
34+
$this->getDescriptor(),
3535
$interface
3636
));
3737
}
@@ -46,20 +46,20 @@ public function setImplement(string $interface)
4646
|| count($rc->getMethods()) > 1
4747
) {
4848
throw new Nette\InvalidArgumentException(sprintf(
49-
"Service '%s': Interface %s must have just one non-static method get().",
50-
$this->getName(),
49+
"[%s]\nInterface %s must have just one non-static method get().",
50+
$this->getDescriptor(),
5151
$interface
5252
));
5353
} elseif ($method->getNumberOfParameters()) {
5454
throw new Nette\InvalidArgumentException(sprintf(
55-
"Service '%s': Method %s::get() must have no parameters.",
56-
$this->getName(),
55+
"[%s]\nMethod %s::get() must have no parameters.",
56+
$this->getDescriptor(),
5757
$interface
5858
));
5959
}
6060

6161
try {
62-
Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()");
62+
Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::get()", $this->getDescriptor());
6363
} catch (Nette\DI\ServiceCreationException $e) {
6464
trigger_error($e->getMessage(), E_USER_DEPRECATED);
6565
}

src/DI/Definitions/Definition.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,29 @@ final public function getName(): ?string
5656
}
5757

5858

59+
final public function isAnonymous(): bool
60+
{
61+
return !$this->name || ctype_digit($this->name);
62+
}
63+
64+
65+
public function getDescriptor(): string
66+
{
67+
if (!$this->isAnonymous()) {
68+
return "Service '$this->name'" . ($this->type ? " of type $this->type" : '');
69+
70+
} elseif ($this->type) {
71+
return "Service of type $this->type";
72+
73+
} elseif ($this->name) {
74+
return "Service '$this->name'";
75+
76+
} else {
77+
return 'Service ?';
78+
}
79+
}
80+
81+
5982
/** @return static */
6083
protected function setType(?string $type)
6184
{
@@ -67,8 +90,8 @@ protected function setType(?string $type)
6790
$this->type = null;
6891
} elseif (!class_exists($type) && !interface_exists($type)) {
6992
throw new Nette\InvalidArgumentException(sprintf(
70-
"Service '%s': Class or interface '%s' not found.",
71-
$this->name,
93+
"[%s]\nClass or interface '%s' not found.",
94+
$this->getDescriptor(),
7295
$type
7396
));
7497
} else {

src/DI/Definitions/FactoryDefinition.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ public function setImplement(string $interface)
4242
{
4343
if (!interface_exists($interface)) {
4444
throw new Nette\InvalidArgumentException(sprintf(
45-
"Service '%s': Interface '%s' not found.",
46-
$this->getName(),
45+
"[%s]\nInterface '%s' not found.",
46+
$this->getDescriptor(),
4747
$interface
4848
));
4949
}
@@ -52,14 +52,14 @@ public function setImplement(string $interface)
5252
$method = $rc->getMethods()[0] ?? null;
5353
if (!$method || $method->isStatic() || $method->name !== self::METHOD_CREATE || count($rc->getMethods()) > 1) {
5454
throw new Nette\InvalidArgumentException(sprintf(
55-
"Service '%s': Interface %s must have just one non-static method create().",
56-
$this->getName(),
55+
"[%s]\nInterface %s must have just one non-static method create().",
56+
$this->getDescriptor(),
5757
$interface
5858
));
5959
}
6060

6161
try {
62-
Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()");
62+
Helpers::ensureClassType(Type::fromReflection($method), "return type of $interface::create()", $this->getDescriptor());
6363
} catch (Nette\DI\ServiceCreationException $e) {
6464
trigger_error($e->getMessage(), E_USER_DEPRECATED);
6565
}
@@ -107,8 +107,8 @@ public function setParameters(array $params)
107107
}
108108

109109
trigger_error(sprintf(
110-
"Service '%s': Option 'parameters' is deprecated and should be removed. The %s should be replaced with %s in configuration.",
111-
$this->getName(),
110+
"[%s]\nOption 'parameters' is deprecated and should be removed. The %s should be replaced with %s in configuration.",
111+
$this->getDescriptor(),
112112
implode(', ', $old),
113113
implode(', ', $new)
114114
), E_USER_DEPRECATED);
@@ -150,7 +150,8 @@ public function resolveType(Nette\DI\Resolver $resolver): void
150150

151151
if ($type && !$type->allows($resultDef->getType())) {
152152
throw new ServiceCreationException(sprintf(
153-
'Factory for %s cannot create incompatible %s type.',
153+
"[%s]\nFactory for %s cannot create incompatible %s type.",
154+
$this->getDescriptor(),
154155
$type,
155156
$resultDef->getType()
156157
));

src/DI/Definitions/LocatorDefinition.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ final class LocatorDefinition extends Definition
2828
public function setImplement(string $interface)
2929
{
3030
if (!interface_exists($interface)) {
31-
throw new Nette\InvalidArgumentException(sprintf("Service '%s': Interface '%s' not found.", $this->getName(), $interface));
31+
throw new Nette\InvalidArgumentException(sprintf("[%s]\nInterface '%s' not found.", $this->getDescriptor(), $interface));
3232
}
3333

3434
$methods = (new \ReflectionClass($interface))->getMethods();
3535
if (!$methods) {
36-
throw new Nette\InvalidArgumentException(sprintf("Service '%s': Interface %s must have at least one method.", $this->getName(), $interface));
36+
throw new Nette\InvalidArgumentException(sprintf("[%s]\nInterface %s must have at least one method.", $this->getDescriptor(), $interface));
3737
}
3838

3939
foreach ($methods as $method) {
@@ -42,16 +42,16 @@ public function setImplement(string $interface)
4242
|| (preg_match('#^(get|create)[A-Z]#', $method->name) && $method->getNumberOfParameters() === 0)
4343
)) {
4444
throw new Nette\InvalidArgumentException(sprintf(
45-
"Service '%s': Method %s::%s() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static.",
46-
$this->getName(),
45+
"[%s]\nMethod %s::%s() does not meet the requirements: is create(\$name), get(\$name), create*() or get*() and is non-static.",
46+
$this->getDescriptor(),
4747
$interface,
4848
$method->name
4949
));
5050
}
5151

5252
if ($method->getNumberOfParameters() === 0) {
5353
try {
54-
Nette\DI\Helpers::ensureClassType(Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", true);
54+
Nette\DI\Helpers::ensureClassType(Nette\Utils\Type::fromReflection($method), "return type of $interface::$method->name()", $this->getDescriptor(), true);
5555
} catch (Nette\DI\ServiceCreationException $e) {
5656
trigger_error($e->getMessage(), E_USER_DEPRECATED);
5757
}
@@ -115,8 +115,8 @@ public function complete(Nette\DI\Resolver $resolver): void
115115
foreach ($resolver->getContainerBuilder()->findByTag($this->tagged) as $name => $tag) {
116116
if (isset($this->references[$tag])) {
117117
trigger_error(sprintf(
118-
"Service '%s': duplicated tag '%s' with value '%s'.",
119-
$this->getName(),
118+
"[%s]\nDuplicated tag '%s' with value '%s'.",
119+
$this->getDescriptor(),
120120
$this->tagged,
121121
$tag
122122
), E_USER_NOTICE);

src/DI/Definitions/ServiceDefinition.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ public function __construct()
3535
}
3636

3737

38+
public function getDescriptor(): string
39+
{
40+
$entity = $this->getEntity();
41+
if ($entity && $this->isAnonymous()) {
42+
return 'Service ' . (is_string($entity) ? "of type $entity" : Nette\DI\Helpers::entityToString($entity));
43+
}
44+
45+
return parent::getDescriptor();
46+
}
47+
48+
3849
/** @return static */
3950
public function setType(?string $type)
4051
{

src/DI/Extensions/InjectExtension.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ private function updateDefinition(Definitions\ServiceDefinition $def): void
6363
}
6464
}
6565

66+
self::checkType($class, $property, $type, $builder, $def);
6667
array_unshift($setups, $inject);
6768
}
6869

@@ -155,7 +156,30 @@ public static function callInjects(DI\Container $container, $service): void
155156
}
156157

157158
foreach (self::getInjectProperties(get_class($service)) as $property => $type) {
159+
self::checkType($service, $property, $type, $container);
158160
$service->$property = $container->getByType($type);
159161
}
160162
}
163+
164+
165+
/**
166+
* @param object|string $class
167+
* @param DI\Container|DI\ContainerBuilder|null $container
168+
*/
169+
private static function checkType(
170+
$class,
171+
string $name,
172+
?string $type,
173+
$container,
174+
Definitions\Definition $def = null
175+
): void {
176+
if ($container && !$container->getByType($type, false)) {
177+
throw new Nette\DI\MissingServiceException(sprintf(
178+
"%sService of type %s required by %s not found.\nDid you add it to configuration file?",
179+
$def ? '[' . $def->getDescriptor() . "]\n" : '',
180+
$type,
181+
Reflection::toString(new \ReflectionProperty($class, $name))
182+
));
183+
}
184+
}
161185
}

src/DI/Extensions/ServicesExtension.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ private function loadDefinition(?string $name, \stdClass $config): void
5555
$this->getContainerBuilder()->removeDefinition($name);
5656
return;
5757
} elseif (!empty($config->alteration) && !$this->getContainerBuilder()->hasDefinition($name)) {
58-
throw new Nette\DI\InvalidConfigurationException('missing original definition for alteration.');
58+
throw new Nette\DI\InvalidConfigurationException('Missing original definition for alteration.');
5959
}
6060

6161
$def = $this->retrieveDefinition($name, $config);
@@ -70,7 +70,12 @@ private function loadDefinition(?string $name, \stdClass $config): void
7070
$this->{$methods[$config->defType]}($def, $config);
7171
$this->updateDefinition($def, $config);
7272
} catch (\Throwable $e) {
73-
throw new Nette\DI\InvalidConfigurationException(($name ? "Service '$name': " : '') . $e->getMessage(), 0, $e);
73+
$message = $e->getMessage();
74+
if ($name && !Nette\Utils\Strings::startsWith($message, '[Service ')) {
75+
$message = "[Service '$name']\n$message";
76+
}
77+
78+
throw new Nette\DI\InvalidConfigurationException($message, 0, $e);
7479
}
7580
}
7681

src/DI/Helpers.php

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,17 +230,22 @@ public static function getReturnTypeAnnotation(\ReflectionFunctionAbstract $func
230230
}
231231

232232

233-
public static function ensureClassType(?Type $type, string $hint, bool $allowNullable = false): string
234-
{
233+
public static function ensureClassType(
234+
?Type $type,
235+
string $hint,
236+
string $descriptor = '',
237+
bool $allowNullable = false
238+
): string {
239+
$descriptor = $descriptor ? "[$descriptor]\n" : '';
235240
if (!$type) {
236-
throw new ServiceCreationException(sprintf('%s is not declared.', ucfirst($hint)));
241+
throw new ServiceCreationException(sprintf('%s%s is not declared.', $descriptor, ucfirst($hint)));
237242
} elseif (!$type->isClass() || (!$allowNullable && $type->isUnion())) {
238-
throw new ServiceCreationException(sprintf("%s is not expected to be %sunion/intersection/built-in, '%s' given.", ucfirst($hint), $allowNullable ? '' : 'nullable/', $type));
243+
throw new ServiceCreationException(sprintf("%s%s is not expected to be %sunion/intersection/built-in, '%s' given.", $descriptor, ucfirst($hint), $allowNullable ? '' : 'nullable/', $type));
239244
}
240245

241246
$class = $type->getSingleName();
242247
if (!class_exists($class) && !interface_exists($class)) {
243-
throw new ServiceCreationException(sprintf("Class '%s' not found.\nCheck the %s.", $class, $hint));
248+
throw new ServiceCreationException(sprintf("%sClass '%s' not found.\nCheck the %s.", $descriptor, $class, $hint));
244249
}
245250

246251
return $class;
@@ -282,4 +287,25 @@ public static function convertType($value, string $type)
282287
$type
283288
));
284289
}
290+
291+
292+
/**
293+
* @param string|array|Reference $entity
294+
*/
295+
public static function entityToString($entity, bool $inner = false): string
296+
{
297+
if (is_string($entity)) {
298+
return $entity . ($inner ? '()' : '');
299+
300+
} elseif ($entity instanceof Reference) {
301+
return '@' . $entity->getValue();
302+
303+
} elseif (is_array($entity)) {
304+
[$a, $b] = $entity;
305+
return self::entityToString($a instanceof Statement ? $a->entity : $a, true)
306+
. '::'
307+
. $b
308+
. (strpos($b, '$') === false ? '()' : '');
309+
}
310+
}
285311
}

src/DI/PhpGenerator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public function generateMethod(Definitions\Definition $def): Php\Method
104104
return $method;
105105

106106
} catch (\Throwable $e) {
107-
throw new ServiceCreationException("Service '$name': " . $e->getMessage(), 0, $e);
107+
throw new ServiceCreationException(sprintf("[%s]\n%s", $def->getDescriptor(), $e->getMessage()), 0, $e);
108108
}
109109
}
110110

0 commit comments

Comments
 (0)