{"id":28089338,"url":"https://github.com/alexdodonov/mezon-router","last_synced_at":"2025-05-13T12:56:55.744Z","repository":{"id":39484537,"uuid":"235992385","full_name":"alexdodonov/mezon-router","owner":"alexdodonov","description":"Small and fast router","archived":false,"fork":false,"pushed_at":"2022-09-13T12:20:12.000Z","size":589,"stargazers_count":271,"open_issues_count":3,"forks_count":18,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-06T22:35:07.037Z","etag":null,"topics":["php","rest","rest-api","restful","restful-api","router","routing"],"latest_commit_sha":null,"homepage":"https://twitter.com/mezonphp","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexdodonov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-01-24T11:41:47.000Z","updated_at":"2025-02-20T08:20:50.000Z","dependencies_parsed_at":"2022-09-20T03:11:01.519Z","dependency_job_id":null,"html_url":"https://github.com/alexdodonov/mezon-router","commit_stats":null,"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexdodonov%2Fmezon-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexdodonov%2Fmezon-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexdodonov%2Fmezon-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexdodonov%2Fmezon-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexdodonov","download_url":"https://codeload.github.com/alexdodonov/mezon-router/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253497320,"owners_count":21917683,"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":["php","rest","rest-api","restful","restful-api","router","routing"],"created_at":"2025-05-13T12:56:55.156Z","updated_at":"2025-05-13T12:56:55.730Z","avatar_url":"https://github.com/alexdodonov.png","language":"PHP","funding_links":["https://opencollective.com/mezon-router"],"categories":[],"sub_categories":[],"readme":"# Routing\n\u003ca href=\"https://packagist.org/packages/mezon/router\"\u003e\u003cimg src=\"https://img.shields.io/packagist/v/mezon/router\" alt=\"Latest Stable Version\"\u003e\u003c/a\u003e\n[![Open Collective](https://img.shields.io/badge/Open%20Collective-sponsor-7eadf1?logo=open%20collective\u0026logoColor=7eadf1\u0026labelColor=555555)](https://opencollective.com/mezon-router)  [![Build Status](https://travis-ci.com/alexdodonov/mezon-router.svg?branch=master)](https://travis-ci.com/alexdodonov/mezon-router) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/alexdodonov/mezon-router/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/alexdodonov/mezon-router/?branch=master) [![codecov](https://codecov.io/gh/alexdodonov/mezon-router/branch/master/graph/badge.svg)](https://codecov.io/gh/alexdodonov/mezon-router) [![Twitter](https://img.shields.io/badge/twitter-follow-1DA1F2?logo=twitter\u0026logoColor=1DA1F2\u0026labelColor=555555?style=flat)](https://twitter.com/mezonphp)\n\n## Intro\n[Mezon Framework](https://github.com/alexdodonov/mezon) provides simple routing class for your needs. It is already used in [Web Application](https://github.com/alexdodonov/mezon-common-application), [Service](https://github.com/alexdodonov/mezon-service), [CRUD Service](https://github.com/alexdodonov/mezon-crud-service).\n\n## Contributors\n\nMezon becomes better because of the [contributors](https://github.com/alexdodonov/mezon-router/graphs/contributors). Thank them too. \n\nOnce again thank you people for your contributions.\n\nUse [this link](https://opencollective.com/mezon-router) if you also want to support our project.\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/jaumarar\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/16009441?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJaume\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e \u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/lolix-notepad\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/75387306?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eLolix\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/Elijha\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/1055673?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJamie Smith\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\u003c!-- markdownlint-enable --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\n## FAQ\n\nUse [this service](https://stackoverflow.com/questions/tagged/mezon) for asking questions.\n\n## Installation\n\nJust print in console\n\n```\ncomposer require mezon/router\n```\n\nAnd that's all.\n\n## Reasons to use\n\nThe mezon/router is \n\n- 25 times faster than klein/klein router\n- 7 to 15 times faster than Symfony router\n- 30 to 50 times faster than Laravel router\n- 1.5 times faster than nikic/fast-route\n\nMore benchmarks can be found [here](https://github.com/alexdodonov/mezon-router-benchmark).\n\n# Learn more\n\nMore information can be found here:\n\n[Twitter](https://twitter.com/mezonphp)\n\n[dev.to](https://dev.to/alexdodonov)\n\n## What is \"First case\" and \"Second case\"?\n\n1. **First case** - http server accepts request, launches php script, wich handles this request, and then all script data uploads from memory. All following requests are processed in the same way. In this case very critical to launch script as soon as possible and we do not have time for long pre-compilations and preparations. Because all of it will be lost after the script will finish working.\n\n2. **Second case** - php script is launching, initiating all internal components (and router is one of them) and then starting processing requests. This case can be organized via for example react-php. It differs from the previous case because we can spend reasonable time to pre-compile routes for faster.\n\nIn this table you can see requests per second. The bigger numbers mean better.\n\n![results](https://github.com/alexdodonov/mezon-router/blob/doc/images/table-1.2.8.jpg?raw=true)\n\n[mezon and klein comparison](doc/router.md)\n\n[mezon and symfony comparison](doc/router-symfony.md)\n\n[mezon and laravel comparison](doc/router-laravel.md)\n\n[mezon and fast-route comparison](doc/fast-route.md)\n\n[mezon and yii2 router comparison](doc/yii2.md)\n\n# I'll be very glad if you'll press \"STAR\" button\n\n\n\n## Simple routes\n\nRouter allows you to map URLs on your php code and call when ever it needs to be called.\n\nRouter supports simple routes like in the example above - example.com/contacts/\n\nEach Application object implicitly creates routes for its `action[action-name]` methods, where `action-name` will be stored as a route. Here is small (as usual) example:\n\n```PHP\nclass MySite\n{\n    /**\n     * Main page\n     */\n    public function actionIndex()\n    {\n        return 'This is the main page of our simple site';\n    }\n\n    /**\n     * FAQ page\n     */\n    public function actionFaq()\n    {\n        return 'This is the \"FAQ\" page';\n    }\n\n    /**\n     * Contacts page\n     */\n    public function actionContacts()\n    {\n        return 'This is the \"Contacts\" page';\n    }\n\n    /**\n     * Some custom action handler\n     */\n    public function someOtherPage()\n    {\n        return 'Some other page of our site';\n    }\n    \n    /**\n     * Some static method\n     */\n    public static function someStaticMethod()\n    {\n        return 'Result of static method';\n    }\n}\n```\n\nAnd this code\n\n```PHP\n$router = new \\Mezon\\Router\\Router();\n$router-\u003efetchActions($mySite = new MySite());\n```\n\nwill create router object and loads information about its actions and create routes. Strictly it will create two routes, because the class MySite has only two methods wich start with `action[Suffix]`. Method `someOtherPage` will not be converted into route automatically. By default this method will create routes wich handle both POST and GET request methods.\n\nThen just call to run callback by URL:\n\n```php\n$router-\u003ecallRoute('/index/');\n```\n\nThere is a way to specify request methods for each action:\n\n```php\n$router-\u003efetchActions($mySite = new MySite(), [\n\t'Index' =\u003e 'GET',\n\t'Contacts' =\u003e 'POST',\n\t'Faq' =\u003e ['GET', 'POST'],\n]);\n```\n\nYou can manually specify callbacks for every URL in your application:\n\n```PHP\n$router-\u003eaddRoute('/some-any-other-route/', [$mySite, 'someOtherPage']);\n```\n\nAnd you also can use static methods:\n\n```PHP\n$router-\u003eaddRoute('/static-route/', ['MySite', 'someStaticMethod']);\n// or in this way\n$router-\u003eaddRoute('/static-route/', 'MySite::someStaticMethod');\n```\n\nWe just need to create it explicitly.\n\nWe can also use simple functions for route creation:\n\n```PHP\nfunction sitemap()\n{\n    return 'Some fake sitemap';\n}\n\n$router-\u003eaddRoute('/sitemap/', 'sitemap');\n```\n\nAnd you can find callback without launching it:\n\n```php\n$router-\u003eaddRoute('/static-route/', 'MySite::someStaticMethod');\n$callback = $router-\u003egetCallback('/static-route/');\nvar_dump($callback());\n```\n\n## Supported request methods\n\nMezon Router supports: \n- GET\n- POST\n- PUT\n- DELETE\n- OPTION\n- PATCH\n\nTo get the list of these methods you can use method getListOfSupportedRequestMethods:\n\n```php\n$router = new \\Mezon\\Router\\Router();\nvar_dump($router-\u003egetListOfSupportedRequestMethods());\n```\n\n## One handler for all routes\n\nYou can specify one processor for all routes like this:\n\n```PHP\n$router-\u003eaddRoute('/*/', function(){});\n```\n\nNote that routing search will stops if the `*` handler will be found. For example:\n\n```PHP\n$router-\u003eaddRoute('/*/', function(){});\n$router-\u003eaddRoute('/index/', function(){});\n```\n\nIn this example route /index/ will never be reached. All request will be passed to the `*` handler. But in this example:\n\n```PHP\n$router-\u003eaddRoute('/contacts/', function(){});\n$router-\u003eaddRoute('/*/', function(){});\n$router-\u003eaddRoute('/index/', function(){});\n```\n\nroute /contacts/ will be processed by its own handler, and all other routes (even /index/) will be processed by the `*` handler.\n\n## Route variables\n\nAnd now a little bit more complex routes:\n\n```PHP\n$router-\u003eaddRoute('/catalogue/[i:cat_id]/', function($route, $variables){});\n$router-\u003eaddRoute('/catalogue/[a:cat_name]/', function($route, $variables){});\n```\n\nHere:\n- i - any integer number  \n- a - any [a-z0-9A-Z_\\/\\-\\.\\@]+ string  \n- il - comma separated list of integer ids  \n- s - any string\n\nParameter name must consist of the following chars: [a-zA-Z0-9_\\-] \n\nAll this variables are passed as second function parameter wich is named in the example above - $variales. All variables are passed as an associative array.\n\n## Request types and first steps to the REST API\n\nYou can bind handlers to different request types as shown bellow:\n\n```PHP\n$router-\u003eaddRoute('/contacts/', function(){}, 'POST'); // this handler will be called for POST requests\n$router-\u003eaddRoute('/contacts/', function(){}, 'GET');  // this handler will be called for GET requests\n$router-\u003eaddRoute('/contacts/', function(){}, 'PUT');  // this handler will be called for PUT requests\n$router-\u003eaddRoute('/contacts/', function(){}, 'DELETE');  // this handler will be called for DELETE requests\n$router-\u003eaddRoute('/contacts/', function(){}, 'OPTION');  // this handler will be called for OPTION requests\n$router-\u003eaddRoute('/contacts/', function(){}, 'PATCH');  // this handler will be called for PATCH requests\n```\n\n## Reverse routes\n\nYou can reverse routes and compile URLs by route's name. For example:\n\n```php\n$router = new \\Mezon\\Router\\Router();\n$router-\u003eaddRoute('/some-route/[i:id]', function(){}, 'GET', 'name of the route');\n// will output /some-route/123\nvar_dump($router-\u003ereverse('name of the route', ['id' =\u003e 123]));\n```\n\n## Routes caching\n\nSince version 1.1.0 you can cache routes on disk and read them from this cache.\n\nTo dump cache on disk use:\n\n```php\n$router-\u003edumpOnDisk('./cache/cache.php');\n```\n\nAnd after that you can load routes:\n\n```php\n$router-\u003eloadFromDisk('./cache/cache.php');\n```\n\nBut these methods have limitations - they can not dump and load closures because of obvious reasons.\n\nYou can also warm cache without dumping:\n\n```php\n$router-\u003ewarmCache();\n```\n\n## Middleware and parameters modification\n\nTypes of middlewares that you can add which will be called before the route handler will be executed. This middleware can transform common parameters $route and $parameters into something different.\n- Multiple global middlewares that will be **called in order of attachment**.\n- Multiple route specific middlewares that **will be called in order of attachment**.\n\nOrder of execution of the middlewares\n1. Global middlewares ``$router-\u003eaddRoute('*', ...)``.\n2. Before calling route callback ``$router-\u003eaddRoute('/example', ...)`` all those matching the route will be executed.\n\nLet's look at a simple example:\n\n```php\n$router = new Router();\n$router-\u003eaddRoute('/user/[i:id]', function(string $route, array $parameters){\n    $userModel = new UserModel();\n    $userObject = $userModel-\u003egetUserById($parameters['id']);\n\n    // use $userObject for any purpose you need\n});\n```\n\nNow let's watch an example with all the possibilities. \n\n```php\n$router = new Router();\n\n// First step. We have an API that talks JSON, convert the body\n$router-\u003eregisterMiddleware('*', function (string $route, array $parameters){\n    $request = Request::createFromGlobals();\n    \n    $parameters['_request'] = $request;\n    $parameters['_body'] = json_decode($request-\u003egetContent(), true);\n\n    return $parameters;    \n});\n\n// Second step. Ensure that we are logged in when we are in the private area\n$router-\u003eregisterMiddleware('*', function (string $route, array $parameters){\n    // Is not a private area\n    if (mb_strpos($route, '/user') !== 0 || empty($parameters['user_id'])) {\n        return $parameters;\n    }\n\n    $token = $parameters['_request']-\u003eheaders-\u003eget('oauth_token');\n\n    $auth = new SomeAuth();\n    $auth-\u003evalidateTokenOrFail(\n        $token,\n        $parameters['user_id']\n    );\n\n    // We don't need to return nothing\n});\n\n// Last step. Now we will modify the parameters so the handler can work with them\n$router-\u003eregisterMiddleware('/user/[i:user_id]', function(string $route, array $parameters){\n    $userModel = new UserModel();\n    \n    return $userModel-\u003egetUserById(\n        $parameters['user_id']\n    );\n});\n\n// Final destination. We have ended the middlewares, now we can work with the processed data\n$router-\u003eaddRoute('/user/[i:user_id]', function (UserObject $userObject){\n    // Do everything\n});\n```\n\n## PSR-7 routes processing\n\nOriginally Mezon Router was not designed to be PSR-7 compatible. But one of the latest features have made it possible. You can use middleware for this purpose. For example:\n\n```php\n$router = new Router();\n$router-\u003eaddRoute('/user/[i:id]', function(\\Nyholm\\Psr7\\Request $request){\n    // work here with the request in PSR-7 way\n\n    $psr17Factory = new \\Nyholm\\Psr7\\Factory\\Psr17Factory();\n\n    $responseBody = $psr17Factory-\u003ecreateStream('Hello world');\n    $response = $psr17Factory-\u003ecreateResponse(200)-\u003ewithBody($responseBody);\n    (new \\Zend\\HttpHandlerRunner\\Emitter\\SapiEmitter())-\u003eemit($response);\n});\n\n$router-\u003eregisterMiddleware('/user/[i:id]', function(string $route, array $parameters){\n    $psr17Factory = new \\Nyholm\\Psr7\\Factory\\Psr17Factory();\n\n    $creator = new \\Nyholm\\Psr7Server\\ServerRequestCreator(\n        $psr17Factory, // ServerRequestFactory\n        $psr17Factory, // UriFactory\n        $psr17Factory, // UploadedFileFactory\n        $psr17Factory  // StreamFactory\n    );\n\n    return $creator-\u003efromGlobals();\n});\n```\n\nThe best thing about it - if you don't use PSR-7 in your project, then you don't \"pay\" for it.\n\n## Custom types\n\nYou can define your own types for URL parser. Let's try to create `date` type.\n\nFirst of all we should create simple class:\n\n```php\nclass DateRouterType\n{\n\n    /**\n     * Method returns regexp for searching this entity in the URL\n     *\n     * @return string regexp for searching\n     */\n    public static function searchRegExp(): string\n    {\n        return '(\\[date:'.BaseType::PARAMETER_NAME_REGEXP.'\\])';\n    }\n}\n```\n\nHere BaseType::PARAMETER_NAME_REGEXP is a global setting wich tells router that parameter names must consist of:\n\n- a-z and A-Z letters\n- 0-9\n- and symbols _ and -\n\nNow we need to define one more class method wich will parse date if it will occur:\n\n```php\npublic static function parserRegExp(): string\n{\n    // pretty simple regexp\n    return '([0-9]{4}-[0-9]{2}-[0-9]{2})';\n}\n```\n\nAnd somewhere in your setup files you need to switch this type on:\n\n```php\n$router-\u003eaddType('date', DateRouterType::class);\n```\n\nNow you can handle routes like this:\n\n```bash\n/some-url-part/2020-02-02/ending-part/\n/posts-for-2020-02-02/\n```\n\nBut be careful. For example you will define such routes:\n\n```php\n$router-\u003eaddRoute('/posts-for-[date:posts-date]/', function(UserObject $userObject){\n    // some activities here\n});\n\n$router-\u003eaddRoute('/[s:some-url]/', function(UserObject $userObject){\n    // some activities here\n});\n```\n\nThen the first handler `/posts-for-[date:posts-date]/` will be called for the route `/posts-for-2020-02-02/`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexdodonov%2Fmezon-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexdodonov%2Fmezon-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexdodonov%2Fmezon-router/lists"}