{"id":22264354,"url":"https://github.com/kuria/router","last_synced_at":"2025-06-19T09:39:08.141Z","repository":{"id":57009964,"uuid":"136511462","full_name":"kuria/router","owner":"kuria","description":"HTTP request router","archived":false,"fork":false,"pushed_at":"2023-04-22T14:38:42.000Z","size":45,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-04T17:20:01.612Z","etag":null,"topics":["generate-urls","http","match","php","request","route","router"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kuria.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.rst","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-07T17:41:53.000Z","updated_at":"2023-09-19T17:45:35.000Z","dependencies_parsed_at":"2025-01-30T12:44:53.243Z","dependency_job_id":"74ba1f5a-2da8-4ec1-a04c-7006b2465f76","html_url":"https://github.com/kuria/router","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/kuria/router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kuria%2Frouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kuria%2Frouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kuria%2Frouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kuria%2Frouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kuria","download_url":"https://codeload.github.com/kuria/router/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kuria%2Frouter/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260725997,"owners_count":23053054,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["generate-urls","http","match","php","request","route","router"],"created_at":"2024-12-03T10:08:41.158Z","updated_at":"2025-06-19T09:39:03.119Z","avatar_url":"https://github.com/kuria.png","language":"PHP","readme":"Router\n######\n\nHTTP request router.\n\n.. image:: https://travis-ci.com/kuria/router.svg?branch=master\n   :target: https://travis-ci.com/kuria/router\n\n.. contents::\n   :depth: 3\n\n\nFeatures\n********\n\n- defining routes using OO builders\n- matching request attributes (method, scheme, host, port, path)\n- regex-driven host and path patterns\n- generating URLs\n\n\nRequirements\n************\n\n- PHP 7.1+\n\n\nUsage\n*****\n\nRouting incoming requests\n=========================\n\nSimple ``PATH_INFO`` routing\n----------------------------\n\nSimple routing using ``$_SERVER['PATH_INFO']`` and hardcoded context information.\n\nExample URL: ``http://localhost/index.php/page/index``\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Router\\Context;\n   use Kuria\\Router\\Result\\Match;\n   use Kuria\\Router\\Result\\MethodNotAllowed;\n   use Kuria\\Router\\Route\\RouteCollector;\n   use Kuria\\Router\\Router;\n\n   // create router\n   $router = new Router();\n\n   // define default context\n   $router-\u003esetDefaultContext(new Context(\n       'http',         // scheme\n       'localhost',    // host\n       80,             // port\n       '/index.php'    // base path\n   ));\n\n   // define routes\n   $router-\u003edefineRoutes(function (RouteCollector $c) {\n       $c-\u003eget('index')-\u003epath('/');\n       $c-\u003eget('page')-\u003epath('/page/{name}');\n\n       $c-\u003eaddGroup('user_', '/user', function (RouteCollector $c) {\n           $c-\u003eadd('register')-\u003emethods(['GET', 'POST'])-\u003epath('/register');\n           $c-\u003eadd('login')-\u003emethods(['GET', 'POST'])-\u003epath('/login');\n           $c-\u003eget('logout')-\u003epath('/logout');\n           $c-\u003eget('profile')-\u003epath('/profile/{username}');\n       });\n   });\n\n   // match current request\n   $path = rawurldecode($_SERVER['PATH_INFO'] ?? '/');\n   $result = $router-\u003ematchPath($_SERVER['REQUEST_METHOD'], $path);\n\n   // handle the result\n   if ($result instanceof Match) {\n       // success\n       // do something with the matched route and parameters\n       echo 'Matched path: ', $result-\u003egetSubject()-\u003epath, \"\\n\";\n       echo 'Matched route: ', $result-\u003egetRoute()-\u003egetName(), \"\\n\";\n       echo 'Parameters: ', print_r($result-\u003egetParameters(), true), \"\\n\";\n   } elseif ($result instanceof MethodNotAllowed) {\n       // method not allowed\n       http_response_code(405);\n       header('Allow: ' . implode(', ', $result-\u003egetAllowedMethods()));\n       echo \"Method not allowed :(\\n\";\n   } else {\n       // not found\n       http_response_code(404);\n       echo \"Not found :(\\n\";\n   }\n\n\nDynamic routing using kuria/request-info\n----------------------------------------\n\nContext and path info can be auto-detected using the `kuria/request-info \u003chttps://github.com/kuria/request-info\u003e`_ library.\n\nIt supports both simple path info and rewritten URLs and can extract information from trusted proxy headers.\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\RequestInfo\\RequestInfo;\n   use Kuria\\Router\\Context;\n   use Kuria\\Router\\Result\\Match;\n   use Kuria\\Router\\Result\\MethodNotAllowed;\n   use Kuria\\Router\\Route\\RouteCollector;\n   use Kuria\\Router\\Router;\n\n   // create router\n   $router = new Router();\n\n   // define default context\n   $router-\u003esetDefaultContext(new Context(\n       RequestInfo::getScheme(),\n       RequestInfo::getHost(),\n       RequestInfo::getPort(),\n       RequestInfo::getBasePath()\n   ));\n\n   // define routes\n   $router-\u003edefineRoutes(function (RouteCollector $c) {\n       $c-\u003eget('index')-\u003epath('/');\n       $c-\u003eget('page')-\u003epath('/page/{name}');\n\n       $c-\u003eaddGroup('user_', '/user', function (RouteCollector $c) {\n           $c-\u003eadd('register')-\u003emethods(['GET', 'POST'])-\u003epath('/register');\n           $c-\u003eadd('login')-\u003emethods(['GET', 'POST'])-\u003epath('/login');\n           $c-\u003eget('logout')-\u003epath('/logout');\n           $c-\u003eget('profile')-\u003epath('/profile/{username}');\n       });\n   });\n\n   // match current request\n   $path = rawurldecode(RequestInfo::getPathInfo());\n   $result = $router-\u003ematchPath(RequestInfo::getMethod(), $path !== '' ? $path : '/');\n\n   // handle the result\n   if ($result instanceof Match) {\n       // success\n       // do something with the matched route and parameters\n       echo 'Matched path: ', $result-\u003egetSubject()-\u003epath, \"\\n\";\n       echo 'Matched route: ', $result-\u003egetRoute()-\u003egetName(), \"\\n\";\n       echo 'Parameters: ', print_r($result-\u003egetParameters(), true), \"\\n\";\n   } elseif ($result instanceof MethodNotAllowed) {\n       // method not allowed\n       http_response_code(405);\n       header('Allow: ' . implode(', ', $result-\u003egetAllowedMethods()));\n       echo \"Method not allowed :(\\n\";\n   } else {\n       // not found\n       http_response_code(404);\n       echo \"Not found :(\\n\";\n   }\n\n\nDefining routes\n===============\n\n``RouteCollector`` provides a convenient interface to define routes.\n\nThe easier way to use it is to call ``Router-\u003edefineRoutes()`` with a callback\naccepting an instance of ``RouteCollector``. The router then takes care of adding\nthe defined routes.\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Router\\Route\\RouteCollector;\n   use Kuria\\Router\\Router;\n\n   $router = new Router();\n\n   $router-\u003edefineRoutes(function (RouteCollector $c) {\n       $c-\u003eget('index')-\u003epath('/');\n       $c-\u003epost('login')-\u003epath('/login');\n       // ...\n   });\n\n\nRoute collector API\n-------------------\n\n``RouteCollector`` provides methods to create and organize route builders.\n\nThe returned ``RouteBuilder`` instances can be used to configure the routes.\nSee `Route builder API`_.\n\n- ``add($routeName): RouteBuilder`` - add a route\n- ``get($routeName): RouteBuilder`` - add a route that matches GET requests\n- ``head($routeName): RouteBuilder`` - add a route that matches HEAD requests\n- ``post($routeName): RouteBuilder`` - add a route that matches POST requests\n- ``put($routeName): RouteBuilder`` - add a route that matches PUT requests\n- ``delete($routeName): RouteBuilder`` - add a route that matches DELETE requests\n- ``options($routeName): RouteBuilder`` - add a route that matches OPTIONS requests\n- ``patch($routeName): RouteBuilder`` - add a route that matches PATCH requests\n- ``addVariant($existingRouteName, $newRouteName): RouteBuilder`` - add a variant of an existing route, see `Route variants`_\n- ``addGroup($namePrefix, $pathPrefix, $callback): void`` - add a group of routes with common prefixes, see `Route groups`_\n- ``hasBuilder($routeName): bool`` - see if a route is defined\n- ``getBuilder($routeName): RouteBuilder`` - get builder for the given route\n- ``removeBuilder($routeName): void`` - remove route definition\n- ``getBuilders(): RouteBuilder[]`` - get all configured builders\n- ``getRoutes(): Route[]`` - build routes\n- ``clear(): void`` - remove all defined routes\n\n\nRoute variants\n^^^^^^^^^^^^^^\n\nTo add multiple similar routes, you can define a single route and then use that\ndefinition as a base of new routes by calling ``addVariant()``:\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Router\\Route\\RouteCollector;\n   use Kuria\\Router\\Router;\n\n   $router = new Router();\n\n   $router-\u003edefineRoutes(function (RouteCollector $c) {\n       // define a base route\n       $c-\u003eget('get_row')\n           -\u003epath('/{database}/{row}')\n           -\u003edefaults(['format' =\u003e 'json']);\n\n       // define a variant of the base route\n       $c-\u003eaddVariant('get_row', 'get_row_with_format')\n           -\u003eappendPath('.{format}')\n           -\u003erequirements(['format' =\u003e 'json|xml']);\n   });\n\n   // print defined routes\n   foreach ($router-\u003egetRoutes() as $route) {\n       echo $route-\u003egetName(), ' :: ', $route-\u003edump(), \"\\n\";\n   }\n\nOutput:\n\n::\n\n  get_row :: GET /{database}/{row}\n  get_row_with_format :: GET /{database}/{row}.{format}\n\n\nRoute groups\n^^^^^^^^^^^^\n\nTo define several routes that share a common path and name prefix, use ``addGroup()``:\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Router\\Route\\RouteCollector;\n   use Kuria\\Router\\Router;\n\n   $router = new Router();\n\n   $router-\u003edefineRoutes(function (RouteCollector $c) {\n       $c-\u003eaddGroup('user_', '/user', function (RouteCollector $c) {\n           $c-\u003eadd('register')-\u003emethods(['GET', 'POST'])-\u003epath('/register');\n           $c-\u003eadd('login')-\u003emethods(['GET', 'POST'])-\u003epath('/login');\n           $c-\u003eget('logout')-\u003epath('/logout');\n           $c-\u003eget('profile')-\u003epath('/profile/{username}');\n       });\n   });\n\n   // print defined routes\n   foreach ($router-\u003egetRoutes() as $route) {\n       echo $route-\u003egetName(), ' :: ', $route-\u003edump(), \"\\n\";\n   }\n\nOutput:\n\n::\n\n  user_register :: GET|POST /user/register\n  user_login :: GET|POST /user/login\n  user_logout :: GET /user/logout\n  user_profile :: GET /user/profile/{username}\n\n\nRoute builder API\n-----------------\n\n``RouteBuilder`` provides a fluent interface to configure a single route.\n\n- ``methods($allowedMethods): self`` - match request methods (must be uppercase, e.g. ``GET``, ``POST``, etc.)\n- ``scheme($scheme): self`` - match a scheme (e.g. ``http`` or ``https``)\n- ``host($hostPattern): self`` - match host name pattern, see `Route patterns`_\n- ``prependHost($hostPatternPrefix): self`` - add a prefix to the host name pattern\n- ``appendHost($hostPatternPrefix): self`` - add a suffix to the host name pattern\n- ``port($port): self`` - match port\n- ``path($pathPattern): self`` - match path pattern, see `Route patterns`_\n- ``prependPath($pathPatternPrefix): self`` - add a prefix to the path pattern\n- ``appendPath($pathPatternPrefix): self`` - add a suffix to the path pattern\n- ``defaults($defaults): self`` - specify default parameters, see `Route defaults`_\n- ``attributes($attributes): self`` - specify arbitrary route attributes, see `Route attributes`_\n- ``requirements($requirements): self`` - specify parameter requirements, see `Route requirements`_\n\nExample call:\n\n.. code:: php\n\n   \u003c?php\n\n   $router-\u003edefineRoutes(function (RouteCollector $c) {\n       // $c-\u003eadd() returns a RouteBuilder\n       $c-\u003eadd('user_profile_page')\n           -\u003emethods(['GET', 'POST'])\n           -\u003escheme('https')\n           -\u003ehost('{username}.example.com')\n           -\u003eport(8080)\n           -\u003epath('/{page}')\n           -\u003edefaults(['page' =\u003e 'home'])\n           -\u003erequirements(['username' =\u003e '\\w+', 'page' =\u003e '[\\w.\\-]+']);\n   });\n\n\nRoute patterns\n--------------\n\nThe host and path of a route can contain any number of parameter placeholders.\n\nPlaceholder syntax is the following:\n\n::\n\n  {parameterName}\n\nParameter name can consist of any characters with the exception of ``}``.\n\nThese parameters will be available in the matching result. See `Matching routes`_.\n\n.. NOTE::\n\n   Optional pattern parameters are not supported. If you need differently structured\n   URLs to match the same resource, define multiple routes accordingly.\n\n   See `Route variants`_.\n\n\nRoute defaults\n--------------\n\nA route can contain default parameter values.\n\nThese defaults are used when generating URLs (in case one or more parameters haven't been specified).\nSee `Generating URLs`_.\n\nDefault parameters can also be useful when defining multiple routes that point to the\nsame resource (so the routes are interchangeable).\n\n\nRoute attributes\n----------------\n\nA route can contain arbitrary attributes.\n\nThe use depends entirely on the application, but it is a good place to store\nvarious metadata, e.g. controller names or handler callables.\n\n\nRoute requirements\n------------------\n\nRoute requirements are a set of plain regular expressions for each host or path pattern\nparameter. See `Route patterns`_.\n\nThe regular expressions should not be delimited. They are also anchored automatically, so\nthey should not contain ``^`` or ``$``.\n\n\nDefault requirements\n^^^^^^^^^^^^^^^^^^^^\n\nIf no requirement is specified, a default one will be assumed instead, depending on the\ntype of the pattern:\n\n- host pattern: ``.+``\n\n  - one or more characters of any type\n\n- path pattern: ``[^/]+``\n\n  - one or more characters that are not a forward slash\n\n\nCaching routes\n==============\n\nBuilding and compiling routes will introduce some overhead into your application.\nLuckily, the defined routes can be serialized and stored for later use.\n\nBelow is an example of route caching using the `kuria/cache \u003chttps://github.com/kuria/cache\u003e`_\nlibrary, but you can any other library or code.\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Cache\\Cache;\n   use Kuria\\Cache\\Driver\\Filesystem\\FilesystemDriver;\n   use Kuria\\Router\\Route\\RouteCollector;\n   use Kuria\\Router\\Router;\n\n   // example cache\n   $cache = new Cache(new FilesystemDriver(__DIR__ . '/cache'));\n\n   // create router\n   $router = new Router();\n\n   // attempt to load routes from the cache\n   $routes = $cache-\u003eget('routes');\n\n   if ($routes === null) {\n       // no routes found in cache, define them\n       $router-\u003edefineRoutes(function (RouteCollector $c) {\n           $c-\u003eget('index')-\u003epath('/');\n           $c-\u003eget('page')-\u003epath('/page/{name}');\n       });\n\n       // store defined routes in the cache\n       $cache-\u003eset('routes', $router-\u003egetRoutes());\n   } else {\n       // use routes from cache\n       $router-\u003esetRoutes($routes);\n   }\n\n.. NOTE::\n\n   Routes that contain unserializable values (such as closures in the attributes)\n   cannot be cached.\n\n\nMatching routes\n===============\n\nAfter routes have been defined, the router can be used to route a request.\n\nSee example code in `Routing incoming requests`_.\n\n\nUsing ``Router-\u003ematch()/matchPath()``\n-------------------------------------\n\nBoth ``Router-\u003ematch()`` and ``Router-\u003ematchPath()`` return an instance of ``Kuria\\Router\\Result\\Result``,\nwhich may be one of the following:\n\n\n``Kuria\\Router\\Result\\Match``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nA route has been matched successfully. The ``Match`` object provides access to the\nmatched route and parameters.\n\nIt us up to the application to do something with this information.\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Router\\Result\\Match;\n   use Kuria\\Router\\Router;\n\n   $result = $router-\u003ematchPath('GET', '/user/profile/bob');\n\n   if ($result instanceof Match) {\n       echo 'Matched route is ', $result-\u003egetRoute()-\u003egetName(), \"\\n\";\n       echo 'Matched parameters are: ', json_encode($result-\u003egetParameters()), \"\\n\";\n   }\n\nOutput:\n\n::\n\n  Matched route is user_profile\n  Matched parameters are: {\"username\":\"bob\"}\n\n.. TIP::\n\n   You can access route attributes at ``$result-\u003egetRoute()-\u003egetAttributes()``.\n\n   See `Route attributes`_.\n\n\n``Kuria\\Router\\Result\\MethodNotAllowed``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nNo routes have been matched, but there are routes that would match if the method\nwas different.\n\nA proper response in this case is HTTP 405 Method Not Allowed, with an ``Allow``\nheader specifying the allowed methods.\n\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Router\\Result\\MethodNotAllowed;\n\n   $result = $router-\u003ematchPath('POST', '/user/logout');\n\n   if ($result instanceof MethodNotAllowed) {\n       http_response_code(405);\n       header('Allow: ' . implode(', ', $result-\u003egetAllowedMethods()));\n   }\n\n\n``Kuria\\Router\\Result\\NotFound``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nNo routes have matched.\n\nA proper response in this case is HTTP 404 Not Found.\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Router\\Result\\NotFound;\n\n   $result = $router-\u003ematchPath('GET', '/nonexistent');\n\n   if ($result instanceof NotFound) {\n       http_response_code(404);\n   }\n\n\nHEAD to GET fallback\n--------------------\n\nTo ease compliance with the HTTP specification, if a ``HEAD`` request does not match\nany route, a second matching attempt will be made assuming a ``GET`` method instead.\n\nPHP itself supports ``HEAD`` requests and will only respond with headers, so you don't\nhave to craft additional routes to handle these requests in most cases.\n\n\nGenerating URLs\n===============\n\nAfter routes have been defined, the router can be used to generate URLs.\n\nSee `Routing incoming requests`_ for an example of a configured router.\n\n\nUsing ``Router-\u003egenerate()``\n----------------------------\n\nThe ``Router-\u003egenerate()`` method will generate an URL for the given route\nand parameters and return an instance of ``Kuria\\Url\\Url``.\n\n- if no such route exists or the parameters are invalid, an exception will\n  be thrown (see `Route requirements`_)\n- if some parameters are missing, the configured default values will be used\n  instead (see `Route defaults`_)\n- any extra parameters (that are not present in the host or path pattern)\n  will be added as query parameters instead\n- if the scheme, host or port is different from the context, the URL's preferred\n  format will be ``Url::ABSOLUTE``; if they are all the same or undefined, it will\n  be ``Url::RELATIVE`` (See `Router context`_)\n\n.. code:: php\n\n   \u003c?php\n\n   var_dump(\n       $router-\u003egenerate('user_register')-\u003ebuild(),\n       $router-\u003egenerate('user_profile', ['username' =\u003e 'bob', 'extra' =\u003e 'example'])-\u003ebuild()\n   );\n\nOutput:\n\n::\n\n  string(14) \"/user/register\"\n  string(31) \"/user/profile/bob?extra=example\"\n\nIf you wish to get absolute URLs regardless of the context, use ``buildAbsolute()``:\n\n.. code:: php\n\n   \u003c?php\n\n   var_dump(\n       $router-\u003egenerate('index')-\u003ebuildAbsolute(),\n       $router-\u003egenerate('page', ['name' =\u003e 'contact'])-\u003ebuildAbsolute()\n   );\n\nOutput:\n\n::\n\n  string(17) \"http://localhost/\"\n  string(29) \"http://localhost/page/contact\"\n\n\nRouter context\n==============\n\nRouter context is used to fill in missing information (scheme, host, port, etc.) when generating\nURLs or matching paths.\n\nIt can be specified in two ways:\n\n\nUsing ``Router-\u003esetDefaultContext()``\n-------------------------------------\n\nThis method defines a default context to be used the none is given.\n\n.. code:: php\n\n   \u003c?php\n\n   use Kuria\\Router\\Context;\n\n   $router-\u003esetDefaultContext(new Context(\n       'https',       // scheme\n       'example.com', // host\n       443,           // port\n       ''             // basePath\n   ));\n\n\nUsing the ``$context`` parameter\n--------------------------------\n\n``Router-\u003ematchPath()`` and ``Router-\u003egenerate()`` accept an optional ``$context`` argument.\n\nIf no context is given, the default context will be used instead. If no default context\nis specified, an exception will be thrown.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkuria%2Frouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkuria%2Frouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkuria%2Frouter/lists"}