{"id":25011575,"url":"https://github.com/sergiosgc/fs-rest-router","last_synced_at":"2026-07-21T17:55:52.141Z","repository":{"id":62542512,"uuid":"76476604","full_name":"sergiosgc/fs-rest-router","owner":"sergiosgc","description":"Filesystem based request router for HTTP verb-aware applications (RESTful)","archived":false,"fork":false,"pushed_at":"2023-04-11T15:35:07.000Z","size":21,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-05T05:36:05.085Z","etag":null,"topics":["php","request-router","rest-router"],"latest_commit_sha":null,"homepage":null,"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/sergiosgc.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2016-12-14T16:22:04.000Z","updated_at":"2021-11-29T19:12:33.000Z","dependencies_parsed_at":"2025-02-06T10:31:19.958Z","dependency_job_id":null,"html_url":"https://github.com/sergiosgc/fs-rest-router","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiosgc%2Ffs-rest-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiosgc%2Ffs-rest-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiosgc%2Ffs-rest-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergiosgc%2Ffs-rest-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sergiosgc","download_url":"https://codeload.github.com/sergiosgc/fs-rest-router/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246269927,"owners_count":20750321,"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","request-router","rest-router"],"created_at":"2025-02-05T05:34:40.350Z","updated_at":"2025-10-11T03:12:52.312Z","avatar_url":"https://github.com/sergiosgc.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rest-router\nFilesystem based request router for HTTP verb-aware applications (RESTful)\n\n## Objective\n\nThe usual PHP request routing pattern is that the script filename is obtained from the request URI, possibly after appending `index.php`. This router follows the same logic, with a step up the RESTful path. It uses the request HTTP verb (`GET`, `POST`, `PUT`, ...) to find out the correct script in the filesystem path that will handle the request.\n\nLet's assume an application stored under the document root `/srv/www/myapp` receiving three requests:\n\n1. A GET to `/customer/`\n2. A POST to `/customer/`\n3. A PUT to `/customer/10`\n\nUnder typical PHP routing, the first two requests will be handled by `/srv/www/myapp/customer/index.php` and the third one would require request rewriting at the webserver layer\n\nUsing this router, the requests will be handled by:\n\n1. `/srv/www/myapp/customer/get.php`\n2. `/srv/www/myapp/customer/post.php`\n3. `/srv/www/myapp/customer/put.php` (extraction of the customer id is possible and covered in this readme)\n\nThis eases development of RESTful apps, while leveraging the application directory structure to map out the URL space. \n\n## Installation\n\nThe easiest way to install and use is via Composer. On your composer.json:\n\n```\n# composer.json\n{\n    \"repositories\": [\n        {\n            \"type\": \"vcs\",\n            \"url\": \"https://github.com/sergiosgc/composer-fs-rest-router.git\"\n        }\n    ],\n    \"require\": {\n        \"sergiosgc/rest-router\": \"dev-master\"\n    }\n}\n```\n\nThen issue either `composer update` or `composer install`.\n\nNow, on your document root, create a catch-all script named index.php:\n```\n\u003c?php\nrequire_once('vendor/autoload.php');\n(new \\sergiosgc\\router\\Rest())-\u003eroute();\n```\n\nAnd configure your webserver to route all requests for the vhost onto this index.php. For example, on nginx, the vhost file would be similar to:\n```\nserver {\n  listen 80;\n\n  root /srv/www/myapp;\n  index /index.php;\n\n  location / {\n    try_files $uri /index.php?$args;\n  }\n\n  location ~ \\.php$ {\n    fastcgi_split_path_info ^(.+\\.php)(/.+)$;\n    fastcgi_pass unix:/run/php/php7.0-fpm.sock;\n    include fastcgi_params;\n  }\n}\n```\n\n## Basic Usage\n\nJust create directories for your RESTful objects. For example, to handle requests for `/customer/`, create a directory `/srv/www/myapp/customer/`. Then, create one script file for each method you wish to handle:\n\n* `post.php` for the POST method (create)\n* `get.php` for GET/HEAD methods (read)\n* `head.php` for the HEAD method\n* `put.php` for the PUT method (update)\n* `delete.php` for the DELETE method (delete)\n\nIf `head.php` is not present, the router will try to fallback to `get.php`. Any method is accepted; the filename should be the lowercased method name with `.php` appended.\n\n## Argument extraction from the URL\n\nThe router will set these two variables:\n* `$_SERVER['ROUTER_PATHBOUND_REQUEST_URI']`: The part of the URL that matches filesystem directories\n* `$_SERVER['ROUTER_PATHBOUND_SCRIPT_FILENAME']`: The script filename obtained from `$_SERVER['ROUTER_PATHBOUND_REQUEST_URI']`\n\n$_SERVER['ROUTER_PATHBOUND_REQUEST_URI'] is the result of consuming, from $_SERVER['REQUEST_URI'], the successive matches from regex files found in the matching path. It matches existing directories in the document root.\n\nWhen using beautified URLs, you may extract extra data from the unbound part of the request URL. On the example above, a PUT to `/customer/10` handled by `/srv/www/myapp/customer/put.php` will have `10` as the value of `$_SERVER['ROUTER_UNBOUND_REQUEST_URI']`.\n\nFor more complex URLs, you may use regular expressions to extract the parameters. On the same dir as the verb handling php script, drop a regular expression with named groups. Let's imagine the URL `/customer/10/20` is interpreted as customers from minid 10 to maxid 20. Alongside `/srv/www/myapp/customer/get.php` create `/srv/www/myapp/customer/get.regex` with this content:\n```\n_^(?\u003cminid\u003e[0-9]+)/(?\u003cmaxid\u003e[0-9]+)_\n```\n\nThe request router will use this regex to:\n\n1. Match the unbound request uri and extract arguments minid and maxid. These will populate `$_REQUEST`\n2. Validate that the unbound URI is valid (throw a 404 otherwise)\n\n## URL subtrees\n\nIf you wish a script to automatically handle all requests from that URL tree node down, you may do so with a regular expression that consumes the remaining URI. \n\nFor example, a script `/srv/www/myapp/customer/order/get.php` may handle both the `/customer/order/` and the `/customer/order/return/` URLs. To do so, place a regex in `/srv/www/myapp/customer/order/get.regex` with:\n```\n_^(?\u003cremaining\u003e.*)$_\n```\n\nThe most specific script in the path will be used. For example, if these two scripts are present: `/srv/www/myapp/customer/order/get.php` and `/srv/www/myapp/customer/get.php`, the URL `/customer/order/return/` will be handled by `/srv/www/myapp/customer/order/get.php`.\n\n## Workaround for HTML verb limitation\n\nWhile HTTP defines a rich set of methods, HTML actually allows only GET and POST. To workaround this limitation, in HTML you may use the `x-verb` parameter in any request to override the HTTP verb. For example, the link `/customer/12?x-verb=DELETE`, when clicked in a browser will issue a GET HTTP request, but will be processed as a DELETE request by the router.\n\n## Debugging\n\nYou may get a debug of URL matching by the router, instead of a regular response, by enabling the $debug static variable. This may easily be done in the catch-all script referred above, like so:\n```\n\u003c?php\nrequire_once('vendor/autoload.php');\n\\sergiosgc\\router\\Rest::$debug = true;\n(new \\sergiosgc\\router\\Rest())-\u003eroute();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergiosgc%2Ffs-rest-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsergiosgc%2Ffs-rest-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergiosgc%2Ffs-rest-router/lists"}