From 147af0579d4f33447927f2fee1fe19a703bbf602 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:21:54 +0100 Subject: [PATCH] Migrate off Http::getRoute/setRoute/match deprecation shims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows utopia-php/http#249 migration guide to drop usage of the shims kept for one-minor-release compat. Affected call sites switch to: - Handler / hook context: inject 'route' (Route|null from the per-request DI container) instead of $utopia->getRoute(). - Resource factories (init/resources/request.php): Router::matchRoute() returning ?RouteMatch instead of $utopia->match($request). - Error path in app/http.php: $app->getResource('routeMatch')?->route, guarded with try/catch for the pre-dispatch error case. - GraphQL resolver inner-request pattern (Resolvers.php): capture getResource('routeMatch') as the original, Router::matchRoute() for the inner URL, rebind 'route' / 'routeMatch' on the resolver container in finally instead of setRoute($original). Http::execute() shim is retained — the guide explicitly keeps it for this hand-built-route case. Redundant re-match in shared/api.php storage-cache branch removed; the injected $route is already the current one. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/controllers/general.php | 10 ++++++---- app/controllers/mock.php | 6 +++--- app/controllers/shared/api.php | 19 ++++++++++++------- app/controllers/shared/api/auth.php | 9 ++++----- app/http.php | 12 ++++++++++-- app/init/resources/request.php | 15 ++++++++++----- src/Appwrite/GraphQL/Resolvers.php | 21 ++++++++++++++++----- 7 files changed, 61 insertions(+), 31 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index dff66b5326..17b7958e73 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -52,6 +52,8 @@ use Utopia\Database\Validator\Authorization; use Utopia\Domains\Domain; use Utopia\DSN\DSN; use Utopia\Http\Http; +use Utopia\Http\Route; +use Utopia\Http\Router; use Utopia\Locale\Locale; use Utopia\Logger\Adapter\Sentry; use Utopia\Logger\Log; @@ -848,7 +850,8 @@ Http::init() ->inject('authorization') ->inject('queueForDeletes') ->inject('executionsRetentionCount') - ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, Event $queueForEvents, Bus $bus, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) { + ->inject('route') + ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, Event $queueForEvents, Bus $bus, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount, ?Route $route) { /* * Appwrite Router */ @@ -862,7 +865,6 @@ Http::init() /* * Request format */ - $route = $utopia->getRoute(); $request->setRoute($route); if ($route === null) { @@ -1172,9 +1174,9 @@ Http::error() ->inject('bus') ->inject('devKey') ->inject('authorization') - ->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Bus $bus, Document $devKey, Authorization $authorization) { + ->inject('route') + ->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Bus $bus, Document $devKey, Authorization $authorization, ?Route $route) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - $route = $utopia->getRoute(); $class = \get_class($error); $code = $error->getCode(); $message = $error->getMessage(); diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 99713af430..7cced736e5 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -13,6 +13,7 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\UID; use Utopia\Http\Http; +use Utopia\Http\Route; use Utopia\Locale\Locale; use Utopia\System\System; use Utopia\Validator\Text; @@ -283,13 +284,12 @@ Http::get('/v1/mock/github/callback') Http::shutdown() ->groups(['mock']) - ->inject('utopia') ->inject('response') ->inject('request') - ->action(function (Http $utopia, Response $response, Request $request) { + ->inject('route') + ->action(function (Response $response, Request $request, ?Route $route) { $result = []; - $route = $utopia->getRoute(); $path = APP_STORAGE_CACHE . '/tests.json'; $tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : []; diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index ca2c69aa72..684d7e022d 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -39,6 +39,8 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Roles; use Utopia\Http\Http; +use Utopia\Http\Route; +use Utopia\Http\Router; use Utopia\Span\Span; use Utopia\System\System; use Utopia\Telemetry\Adapter as Telemetry; @@ -99,8 +101,8 @@ Http::init() ->inject('team') ->inject('apiKey') ->inject('authorization') - ->action(function (Http $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, AuditContext $auditContext, Document $project, User $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) { - $route = $utopia->getRoute(); + ->inject('route') + ->action(function (Http $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, AuditContext $auditContext, Document $project, User $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization, ?Route $route) { if ($route === null) { throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); } @@ -505,12 +507,12 @@ Http::init() ->inject('telemetry') ->inject('platform') ->inject('authorization') - ->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Messaging $queueForMessaging, AuditContext $auditContext, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Context $usage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) { + ->inject('route') + ->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Messaging $queueForMessaging, AuditContext $auditContext, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Context $usage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization, ?Route $route) { $response->setUser($user); $request->setUser($user); - $route = $utopia->getRoute(); if ($route === null) { throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); } @@ -631,7 +633,6 @@ Http::init() $useCache = $route->getLabel('cache', false); $storageCacheOperationsCounter = $telemetry->createCounter('storage.cache.operations.load'); if ($useCache) { - $route = $utopia->match($request); $isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview'; $isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && ! $user->isPrivileged($authorization->getRoles()); @@ -809,7 +810,8 @@ Http::shutdown() ->inject('bus') ->inject('apiKey') ->inject('mode') - ->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, AuditContext $auditContext, Audit $publisherForAudits, Context $usage, UsagePublisher $publisherForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor, Bus $bus, ?Key $apiKey, string $mode) use ($parseLabel) { + ->inject('route') + ->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, AuditContext $auditContext, Audit $publisherForAudits, Context $usage, UsagePublisher $publisherForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor, Bus $bus, ?Key $apiKey, string $mode, ?Route $route) use ($parseLabel) { $responsePayload = $response->getPayload(); @@ -859,7 +861,10 @@ Http::shutdown() } } - $route = $utopia->getRoute(); + if ($route === null) { + return; + } + $requestParams = $route->getParamsValues(); /** diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index db98d97bf5..9d15bef302 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -9,6 +9,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Http\Http; +use Utopia\Http\Route; use Utopia\System\System; Http::init() @@ -32,13 +33,13 @@ Http::init() Http::init() ->groups(['auth']) - ->inject('utopia') ->inject('request') ->inject('project') ->inject('geodb') ->inject('user') ->inject('authorization') - ->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, User $user, Authorization $authorization) { + ->inject('route') + ->action(function (Request $request, Document $project, Reader $geodb, User $user, Authorization $authorization, ?Route $route) { $denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', ''); if (!empty($denylist && $project->getId() === 'console')) { $countries = explode(',', $denylist); @@ -49,8 +50,6 @@ Http::init() } } - $route = $utopia->match($request); - $isPrivilegedUser = $user->isPrivileged($authorization->getRoles()); $isAppUser = $user->isApp($authorization->getRoles()); @@ -59,7 +58,7 @@ Http::init() } $auths = $project->getAttribute('auths', []); - switch ($route->getLabel('auth.type', '')) { + switch ($route?->getLabel('auth.type', '')) { case 'email-password': if (($auths[Config::getParam('auth')['email-password']['key']] ?? true) === false) { throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email / Password authentication is disabled for this project'); diff --git a/app/http.php b/app/http.php index b72f3b7f34..9827bd0112 100644 --- a/app/http.php +++ b/app/http.php @@ -545,7 +545,11 @@ $swooleAdapter->onRequest(function ($utopiaRequest, $utopiaResponse) use ($files $app->run($request, $response); - $route = $app->getRoute(); + try { + $route = $app->getResource('routeMatch')?->route; + } catch (\Throwable) { + $route = null; + } Span::add('http.path', $route?->getPath() ?? 'unknown'); } catch (\Throwable $th) { Span::error($th); @@ -561,7 +565,11 @@ $swooleAdapter->onRequest(function ($utopiaRequest, $utopiaResponse) use ($files // All good, user is optional information for logger } - $route = $app->getRoute(); + try { + $route = $app->getResource('routeMatch')?->route; + } catch (\Throwable) { + $route = null; + } $log = $app->getResource("log"); diff --git a/app/init/resources/request.php b/app/init/resources/request.php index 3f6196c460..2c4e06d9fa 100644 --- a/app/init/resources/request.php +++ b/app/init/resources/request.php @@ -47,6 +47,7 @@ use Utopia\DI\Container; use Utopia\Domains\Domain; use Utopia\DSN\DSN; use Utopia\Http\Http; +use Utopia\Http\Router; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Pools\Group; @@ -620,10 +621,12 @@ return function (Container $container): void { // These endpoints moved from /v1/projects/:projectId/ to /v1/ // When accessed via the old alias path, extract projectId from the URI $deprecatedProjectPathPrefix = '/v1/projects/'; - $route = $utopia->match($request); - if (!empty($route)) { + $urlPath = \parse_url($request->getURI(), PHP_URL_PATH); + $urlPath = \is_string($urlPath) ? ($urlPath === '' ? '/' : $urlPath) : '/'; + $match = Router::matchRoute($request->getMethod(), $urlPath); + if ($match !== null) { $isDeprecatedAlias = \str_starts_with($request->getURI(), $deprecatedProjectPathPrefix) && - !\str_starts_with($route->getPath(), $deprecatedProjectPathPrefix); + !\str_starts_with($match->route->getPath(), $deprecatedProjectPathPrefix); if ($isDeprecatedAlias) { $projectId = \explode('/', $request->getURI(), 5)[3] ?? ''; @@ -1109,8 +1112,10 @@ return function (Container $container): void { if ($project->getId() !== 'console') { $teamInternalId = $project->getAttribute('teamInternalId', ''); } else { - $route = $utopia->match($request); - $path = ! empty($route) ? $route->getPath() : $request->getURI(); + $urlPath = \parse_url($request->getURI(), PHP_URL_PATH); + $urlPath = \is_string($urlPath) ? ($urlPath === '' ? '/' : $urlPath) : '/'; + $match = Router::matchRoute($request->getMethod(), $urlPath); + $path = $match !== null ? $match->route->getPath() : $request->getURI(); $orgHeader = $request->getHeader('x-appwrite-organization', ''); if (str_starts_with($path, '/v1/projects/:projectId')) { $uri = $request->getURI(); diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index cabb357607..369be393eb 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -10,6 +10,7 @@ use Utopia\DI\Container; use Utopia\Http\Exception; use Utopia\Http\Http; use Utopia\Http\Route; +use Utopia\Http\Router; use Utopia\System\System; class Resolvers @@ -359,7 +360,11 @@ class Resolvers $lock->acquire(); - $original = $utopia->getRoute(); + try { + $originalMatch = $utopia->getResource('routeMatch'); + } catch (\Throwable) { + $originalMatch = null; + } try { $request = clone $request; @@ -380,7 +385,13 @@ class Resolvers $resolverResponse->setContentType(Response::CONTENT_TYPE_NULL); $resolverResponse->setSent(false); - $route = $utopia->match($request, fresh: true); + $urlPath = \parse_url($request->getURI(), PHP_URL_PATH); + $urlPath = \is_string($urlPath) ? ($urlPath === '' ? '/' : $urlPath) : '/'; + $match = Router::matchRoute($request->getMethod(), $urlPath); + if ($match === null) { + throw new Exception('Route not found: ' . $request->getMethod() . ' ' . $urlPath, 404); + } + $route = $match->route; $request->setRoute($route); $utopia->execute($route, $request, $resolverResponse); @@ -402,9 +413,9 @@ class Resolvers $reject($e); return; } finally { - if ($original !== null) { - $utopia->setRoute($original); - } + $container = self::getResolverContainer($utopia); + $container->set('route', static fn () => $originalMatch?->route); + $container->set('routeMatch', static fn () => $originalMatch); $lock->release(); unset(self::$locks[\spl_object_hash($utopia)]);