@@ -99,18 +99,14 @@ class Http
9999 protected static array $ requestHooks = [];
100100
101101 /**
102- * Route
102+ * Per-request context keys for the matched route + matched template key.
103103 *
104- * Memory cached result for chosen route
104+ * The match result lives in the request-scoped context() container rather
105+ * than on $this so it does not race when the Http instance is shared
106+ * across coroutines.
105107 */
106- protected ?Route $ route = null ;
107-
108- /**
109- * Matched route key (template after placeholder substitution).
110- *
111- * Cached alongside $route for $fresh=false re-matches.
112- */
113- protected string $ matchedPath = '' ;
108+ private const string CONTEXT_ROUTE = 'route ' ;
109+ private const string CONTEXT_MATCHED_PATH = 'matchedPath ' ;
114110
115111 /**
116112 * Compression
@@ -423,15 +419,21 @@ public static function getRoutes(): array
423419 */
424420 public function getRoute (): ?Route
425421 {
426- return $ this ->route ?? null ;
422+ $ context = $ this ->context ();
423+ if (!$ context ->has (self ::CONTEXT_ROUTE )) {
424+ return null ;
425+ }
426+
427+ $ route = $ context ->get (self ::CONTEXT_ROUTE );
428+ return $ route instanceof Route ? $ route : null ;
427429 }
428430
429431 /**
430432 * Set the current route
431433 */
432434 public function setRoute (Route $ route ): self
433435 {
434- $ this ->route = $ route ;
436+ $ this ->context ()-> set ( self :: CONTEXT_ROUTE , fn () => $ route, []) ;
435437
436438 return $ this ;
437439 }
@@ -556,12 +558,23 @@ public function match(Request $request, bool $fresh = true): ?Route
556558 * matched against. Returning the matched key separately avoids mutating
557559 * the shared Route instance, which would race under coroutines.
558560 *
561+ * Caches the result in the per-request context so re-matches with
562+ * $fresh=false hit memory without re-running Router::match.
563+ *
559564 * @return array{0: Route, 1: string}|null
560565 */
561566 private function matchInternal (Request $ request , bool $ fresh = true ): ?array
562567 {
563- if (null !== $ this ->route && !$ fresh ) {
564- return [$ this ->route , $ this ->matchedPath ];
568+ $ context = $ this ->context ();
569+
570+ if (!$ fresh && $ context ->has (self ::CONTEXT_ROUTE )) {
571+ $ route = $ context ->get (self ::CONTEXT_ROUTE );
572+ if ($ route instanceof Route) {
573+ $ matchedPath = $ context ->has (self ::CONTEXT_MATCHED_PATH )
574+ ? (string ) $ context ->get (self ::CONTEXT_MATCHED_PATH )
575+ : '' ;
576+ return [$ route , $ matchedPath ];
577+ }
565578 }
566579
567580 $ url = parse_url ($ request ->getURI (), PHP_URL_PATH );
@@ -572,12 +585,12 @@ private function matchInternal(Request $request, bool $fresh = true): ?array
572585 $ match = Router::match ($ method , $ url );
573586
574587 if ($ match === null ) {
575- $ this ->route = null ;
576- $ this ->matchedPath = '' ;
577588 return null ;
578589 }
579590
580- [$ this ->route , $ this ->matchedPath ] = $ match ;
591+ [$ route , $ matchedPath ] = $ match ;
592+ $ context ->set (self ::CONTEXT_ROUTE , fn () => $ route , []);
593+ $ context ->set (self ::CONTEXT_MATCHED_PATH , fn () => $ matchedPath , []);
581594
582595 return $ match ;
583596 }
@@ -746,11 +759,14 @@ public function run(Request $request, Response $response): static
746759 $ start = microtime (true );
747760 $ result = $ this ->runInternal ($ request , $ response );
748761
762+ $ context = $ this ->context ();
763+ $ matchedRoute = $ context ->has (self ::CONTEXT_ROUTE ) ? $ context ->get (self ::CONTEXT_ROUTE ) : null ;
764+
749765 $ requestDuration = microtime (true ) - $ start ;
750766 $ attributes = [
751767 'url.scheme ' => $ request ->getProtocol (),
752768 'http.request.method ' => $ request ->getMethod (),
753- 'http.route ' => $ this -> route ? ->getPath(),
769+ 'http.route ' => $ matchedRoute instanceof Route ? $ matchedRoute ->getPath () : null ,
754770 'http.response.status_code ' => $ response ->getStatusCode (),
755771 ];
756772 $ this ->requestDuration ->record ($ requestDuration , $ attributes );
@@ -821,8 +837,6 @@ private function runInternal(Request $request, Response $response): static
821837 $ matchedPath = $ match [1 ] ?? '' ;
822838 $ groups = ($ route instanceof Route) ? $ route ->getGroups () : [];
823839
824- $ this ->context ()->set ('route ' , fn () => $ route , []);
825-
826840 if (self ::REQUEST_METHOD_HEAD === $ method ) {
827841 $ method = self ::REQUEST_METHOD_GET ;
828842 $ response ->disablePayload ();
0 commit comments