Skip to content

Commit 5dd0883

Browse files
committed
Improve exception messages for invalid class references
1 parent 1d19f89 commit 5dd0883

3 files changed

Lines changed: 65 additions & 46 deletions

File tree

src/Container.php

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,14 @@ public function __invoke(ServerRequestInterface $request, ?callable $next = null
7777
public function callable(string $class): callable
7878
{
7979
return function (ServerRequestInterface $request, ?callable $next = null) use ($class) {
80-
// Check `$class` references a valid class name that can be autoloaded
81-
if (\is_array($this->container) && !\class_exists($class, true) && !interface_exists($class, false) && !trait_exists($class, false)) {
82-
throw new \BadMethodCallException('Request handler class ' . $class . ' not found');
83-
}
84-
8580
try {
8681
if ($this->container instanceof ContainerInterface) {
8782
$handler = $this->container->get($class);
8883
} else {
8984
$handler = $this->loadObject($class);
9085
}
9186
} catch (\Throwable $e) {
92-
throw new \BadMethodCallException(
87+
throw new \Error(
9388
'Request handler class ' . $class . ' failed to load: ' . $e->getMessage(),
9489
0,
9590
$e
@@ -100,8 +95,8 @@ public function callable(string $class): callable
10095
// This initial version is intentionally limited to checking the method name only.
10196
// A follow-up version will likely use reflection to check request handler argument types.
10297
if (!is_callable($handler)) {
103-
throw new \BadMethodCallException(
104-
'Request handler class ' . \explode("\0", $class)[0] . ' has no public __invoke() method'
98+
throw new \Error(
99+
'Request handler ' . \explode("\0", $class)[0] . ' has no public __invoke() method'
105100
);
106101
}
107102

@@ -180,7 +175,7 @@ public function getErrorHandler(): ErrorHandler
180175
* @param class-string<T> $name
181176
* @return T
182177
* @throws \TypeError if container config or factory returns an unexpected type
183-
* @throws \Error|\BadMethodCallException if object of type $name can not be loaded
178+
* @throws \Error if object of type $name can not be loaded
184179
* @throws \Throwable if container factory function throws unexpected exception
185180
*/
186181
private function loadObject(string $name, int $depth = 64) /*: object (PHP 7.2+) */
@@ -238,7 +233,7 @@ private function loadObject(string $name, int $depth = 64) /*: object (PHP 7.2+)
238233

239234
// Check `$name` references a valid class name that can be autoloaded
240235
if (!\class_exists($name, true) && !interface_exists($name, false) && !trait_exists($name, false)) {
241-
throw new \BadMethodCallException('Class ' . $name . ' not found');
236+
throw new \Error('Class ' . $name . ' not found');
242237
}
243238

244239
$class = new \ReflectionClass($name);
@@ -251,7 +246,7 @@ private function loadObject(string $name, int $depth = 64) /*: object (PHP 7.2+)
251246
} elseif ($class->isTrait()) {
252247
$modifier = 'trait';
253248
}
254-
throw new \BadMethodCallException('Cannot instantiate ' . $modifier . ' '. $name);
249+
throw new \Error('Cannot instantiate ' . $modifier . ' '. $name);
255250
}
256251

257252
// build list of constructor parameters based on parameter types
@@ -266,7 +261,7 @@ private function loadObject(string $name, int $depth = 64) /*: object (PHP 7.2+)
266261
/**
267262
* @return list<mixed>
268263
* @throws \TypeError if container config or factory returns an unexpected type
269-
* @throws \Error|\BadMethodCallException if either parameter can not be loaded
264+
* @throws \Error if either parameter can not be loaded
270265
* @throws \Throwable if container factory function throws unexpected exception
271266
*/
272267
private function loadFunctionParams(\ReflectionFunctionAbstract $function, int $depth, bool $allowVariables, string $for): array
@@ -282,7 +277,7 @@ private function loadFunctionParams(\ReflectionFunctionAbstract $function, int $
282277
/**
283278
* @return mixed
284279
* @throws \TypeError if container config or factory returns an unexpected type
285-
* @throws \Error|\BadMethodCallException if $parameter can not be loaded
280+
* @throws \Error if $parameter can not be loaded
286281
* @throws \Throwable if container factory function throws unexpected exception
287282
*/
288283
private function loadParameter(\ReflectionParameter $parameter, int $depth, bool $allowVariables, string $for) /*: mixed (PHP 8.0+) */
@@ -355,7 +350,7 @@ private function hasVariable(string $name): bool
355350
/**
356351
* @return object|string|int|float|bool|null
357352
* @throws \TypeError if container factory returns an unexpected type
358-
* @throws \Error|\BadMethodCallException if $name can not be loaded
353+
* @throws \Error if $name can not be loaded
359354
* @throws \Throwable if container factory function throws unexpected exception
360355
*/
361356
private function loadVariable(string $name, int $depth = 64) /*: object|string|int|float|bool|null (PHP 8.0+) */

tests/AppTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,7 @@ public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhe
16051605

16061606
$this->assertStringContainsString("<title>Error 500: Internal Server Error</title>\n", (string) $response->getBody());
16071607
$this->assertStringContainsString("<p>The requested page failed to load, please try again later.</p>\n", (string) $response->getBody());
1608-
$this->assertStringMatchesFormat("%a<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got uncaught <code>BadMethodCallException</code> with message <code>Request handler class UnknownClass not found</code> in <code title=\"See %s\">Container.php:%d</code>.</p>\n%a", (string) $response->getBody());
1608+
$this->assertStringMatchesFormat("%a<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got uncaught <code>Error</code> with message <code>Request handler class UnknownClass failed to load: Class UnknownClass not found</code> in <code title=\"See %s\">Container.php:%d</code>.</p>\n%a", (string) $response->getBody());
16091609
}
16101610

16111611
public function provideInvalidClasses(): \Generator
@@ -1692,7 +1692,7 @@ public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhe
16921692

16931693
$this->assertStringContainsString("<title>Error 500: Internal Server Error</title>\n", (string) $response->getBody());
16941694
$this->assertStringContainsString("<p>The requested page failed to load, please try again later.</p>\n", (string) $response->getBody());
1695-
$this->assertStringMatchesFormat("%a<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got uncaught <code>BadMethodCallException</code> with message <code>Request handler class " . $class . " failed to load: $error</code> in <code title=\"See %s\">Container.php:%d</code>.</p>\n%a", (string) $response->getBody());
1695+
$this->assertStringMatchesFormat("%a<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got uncaught <code>Error</code> with message <code>Request handler class $class failed to load: $error</code> in <code title=\"See %s\">Container.php:%d</code>.</p>\n%a", (string) $response->getBody());
16961696
}
16971697

16981698
public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerClassRequiresUnexpectedCallableParameter(): void
@@ -1739,7 +1739,7 @@ public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhe
17391739

17401740
$this->assertStringContainsString("<title>Error 500: Internal Server Error</title>\n", (string) $response->getBody());
17411741
$this->assertStringContainsString("<p>The requested page failed to load, please try again later.</p>\n", (string) $response->getBody());
1742-
$this->assertStringMatchesFormat("%a<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got uncaught <code>BadMethodCallException</code> with message <code>Request handler class class@anonymous has no public __invoke() method</code> in <code title=\"See %s\">Container.php:%d</code>.</p>\n%a", (string) $response->getBody());
1742+
$this->assertStringMatchesFormat("%a<p>Expected request handler to return <code>Psr\Http\Message\ResponseInterface</code> but got uncaught <code>Error</code> with message <code>Request handler class@anonymous has no public __invoke() method</code> in <code title=\"See %s\">Container.php:%d</code>.</p>\n%a", (string) $response->getBody());
17431743
}
17441744

17451745
public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhenHandlerReturnsPromiseWhichFulfillsWithWrongValue(): void

0 commit comments

Comments
 (0)