Skip to content

Commit 1200c1a

Browse files
loks0nclaude
andcommitted
Seed context 'match' to null at the top of runInternal
Previously 'match' was only set after Http::match() ran (or by execute() when called directly). Code that read it earlier — request hooks, the global error path triggered by an exception in a request hook, top-level introspection in app/http.php — got "Failed to find resource" from getResource('match'). Set it to null up front, alongside 'request' and 'response', so inject('match') and getResource('match') always return at least null during a request. The real RouteMatch overwrites it once routing resolves. Added testMatchIsNullDuringEarlyErrorBeforeRouting covering the "error in onRequest hook -> global error handler reads 'match'" path that motivated this fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e6ffc47 commit 1200c1a

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

src/Http/Http.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,11 @@ private function runInternal(Request $request, Response $response): static
787787

788788
$this->setContext('request', fn() => $request);
789789
$this->setContext('response', fn() => $response);
790+
// Seed 'match' to null up front so requestHooks, the global error
791+
// path, and any pre-routing code can read it without tripping the
792+
// "Failed to find resource" error from getResource('match'). It
793+
// gets overwritten with the real RouteMatch once match() runs.
794+
$this->setContext('match', fn() => null);
790795

791796
try {
792797
foreach (self::$requestHooks as $hook) {

tests/HttpTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,35 @@ public function testShutdownHookCanInjectResolvedArguments(): void
395395
$this->assertSame('action:abc123,200|shutdown:fileId=abc123,width=200', $result);
396396
}
397397

398+
public function testMatchIsNullDuringEarlyErrorBeforeRouting(): void
399+
{
400+
$_SERVER['REQUEST_METHOD'] = 'GET';
401+
$_SERVER['REQUEST_URI'] = '/whatever';
402+
403+
Http::onRequest()
404+
->action(function (): void {
405+
throw new \RuntimeException('boom');
406+
});
407+
408+
$seen = new \stdClass();
409+
$seen->matchWasSet = false;
410+
$seen->matchValue = 'sentinel';
411+
412+
Http::error()
413+
->inject('match')
414+
->action(function (?RouteMatch $match) use ($seen): void {
415+
$seen->matchWasSet = true;
416+
$seen->matchValue = $match;
417+
});
418+
419+
ob_start();
420+
@$this->http->run(new Request(), new Response());
421+
ob_end_clean();
422+
423+
$this->assertTrue($seen->matchWasSet, 'global error hook should have run');
424+
$this->assertNull($seen->matchValue, "'match' should be null on the context before routing");
425+
}
426+
398427
/**
399428
* @return \Iterator<string, array<int, string>>
400429
*/

0 commit comments

Comments
 (0)