dev-master
9999999-devRouter for web and CLI applications (PHP).
Apache-2.0
The Requires
- php >=7.1
by Tamas Bolner
url php cli web router uri
Router for web and CLI applications (PHP).
Simple and flexible router library for web and command line applications., (*1)
Packagist page: https://packagist.org/packages/tbolner/flex-php-router, (*2)
Recommended IDE: PhpStorm, (*3)
Include the library in the composer.json file of your project:, (*4)
{ "require": { "tbolner/flex-php-router": "dev-master" } }
Then execute:, (*5)
composer update
This library requires PHP 7.1 or newer., (*6)
Create a folder in your project, to contain the controllers. For example:, (*7)
project1/src/Controller/
Then create the controllers. The file name has to match the first part of the URI:, (*8)
Example URL | Controller path |
---|---|
GET http://host/items/12 | project1/src/Controller/items.php |
POST http://host/items/12/list | project1/src/Controller/items.php |
PUT https://host/items/12/detailsasd | project1/src/Controller/items.php |
DELETE http://host/test%25Ctr4/as%25d/66 | project1/src/Controller/testCtr4.php |
GET http://host/test+_Ctr%255/qwerty/66 | project1/src/Controller/test_Ctr5.php |
GET http://host/test-Ctr6/qwerty/66 | project1/src/Controller/test-Ctr6.php |
As you see all special characters are removed from the first part of the URI when looking up the corresponding PHP file. (For security considerations.) To be precise: all characters are removed from the first part, which are not: a-z, A-Z, 0-9, - (dash), _ (underline). (Other parts of the URL are unaffected by this mechanism.), (*9)
The controller will match for patters to decide what to execute. These matching parts are called "actions"., (*10)
project1/src/Controller/items.php, (*11)
<?php declare(strict_types=1); namespace MyProject\Controller; use FlexPhpRouter\Router; Router::get( "items/{id:int}", function (int $id) { /* ... your code ... */ /* you can also throw exceptions to catch them at your router start-up code */ } ); Router::post( "items/{id:int}/list", function (int $id) { /* ... your code ... */ } ); Router::put( "items/{id:int}/details{other:string}", function (int $id, string $other) { /* ... your code ... */ } );
Notes: - In the path parameters, patterns can have 4 types: - string, int, float, bool - To have additional types, just use "string" and do the type conversion in the action. - The slashes (/) at the endings are ignored. The patterns will match regardless of the ending slashes. - To match for the site root, just match for "/" in the default controller. (See next point on how to specify the default controller.) - Notice that in the 3rd action, the parameter follows the "/details" text without a slash separating them, which is an accepted solution. - The supported methods are: - get, post, put, delete, any, (*12)
Finally add the router start-up code to your application:, (*13)
project1/web/index.php, (*14)
<?php declare(strict_types=1); namespace Web; use FlexPhpRouter\Router; use MyProject\Exceptions\MySpecialException; require_once (dirname(__FILE__)."/../vendor/autoload.php"); try { Router::route(dirname(__FILE__)."/../src/Controller", 'default'); } catch (MySpecialException $ex) { /* ... */ } catch (\Exception $ex) { /* ... */ }
Notes: - The first parameter of Router::route() defines the path to the controller directory. Use a way similar to the above, to define the path in a relative way. - The second parameter of Router::route() command expects the name of the default controller, which is called when no controller was found. You can handle there the site root for example. The name "default" will lead to the controller file "default.php". - Handle all exceptions which you would throw in the controllers or in code invoked from the controllers., (*15)
You can pass an optional callback as the fourth parameter of Router::route()
:, (*16)
class MyAuthClass { public static function authenticate(bool $isWeb, string $path) { ... } } ... Router::route(dirname(__FILE__)."/../src/Controller", 'default', '', // The third parameter is the API prefix [MyAuthClass::class, 'authenticate'] ); ...
It will be executed before any controller code, with the knowledge of the selected controller path. The callback receives 2 parameters. The first '$isWeb' is true when a web controller gets executed and false when a CLI. The second tells which path was selected., (*17)
This feature allows you to implement authentication for a selection of
API entries. The recommendation is that in case of an auth failure,
throw an exception created by you, for example: MyAuthException
, and
handle it at the main entry point, where normally all exceptions are
caught., (*18)
The 3rd parameter of Router::route(...) specifies an API prefix, which will always get ignored during the routing process, like if it was cut off from the beginning of the URI., (*19)
Example:, (*20)
Router::route(dirname(__FILE__)."/../src/Controller", 'default', 'api');
Example URL | Controller path |
---|---|
http://host/api/items/12 | project1/src/Controller/items.php |
http://host/api/other/list | project1/src/Controller/other.php |
Notes: - The previous example works also with the following prefixes: '/api', 'api/', '/api/'., (*21)
The default controller (described earlier) is called in two cases: - If no controller was found, based on the first part of the URI - If none of the actions had matching pattern inside the loaded controller, (*22)
As a result we can always put an action at the end of the default, which is called when nothing got executed., (*23)
Controller/default.php, (*24)
<?php declare(strict_types=1); namespace Controller; use FlexPhpIO\Response; use FlexPhpRouter\Router; Router::any( "/", function () { Response::printJson([ "message" => "This is the site root." ]); } ); Router::any( "*", function () { Response::printHtmlFile(dirname(__FILE__).'/../web/notFound.html', 404); } );
Notes: - Notice the "*" character in the last action. It means 'any'. - The first action - which handles a site root request - works also with an empty pattern: ""., (*25)
Create a folder in your project, to contain the CLI controllers. For example:, (*26)
Then create the controllers. The file name has to match the first part of the path passed as first parameter:, (*27)
Example console command | Controller path |
---|---|
console.php test/cleanup | project1/src/CLI/test.php |
console.php test/run param1=55 param2="asd" | project1/src/CLI/test.php |
console.php other/do/something | project1/src/CLI/other.php |
Example controller:, (*28)
project1/src/CLI/test.php, (*29)
<?php declare(strict_types=1); namespace MyProject/CLI; use FlexPhpRouter\Router; use FlexPhpRouter\CliParameter; Router::cli("test/cleanup") ->matchRoute(function () { /* ... your code ... */ /* you can also throw exceptions to catch them at your router start-up code */ }); Router::cli("test/run") ->addParameter("param1", CliParameter::TYPE_INT, true, "Description of first parameter.") ->addParameter("param2", CliParameter::TYPE_STRING, false, "Description of second parameter.") ->matchRoute(function (int $param1, string $param2 = null) { /* ... your code ... */ });
In this case the router start-up code has to go into a not web related PHP file (so it should be outside of the web folder). For example:, (*30)
project1/console.php, (*31)
#!/usr/bin/php <?php declare(strict_types=1); namespace CLI; use FlexPhpRouter\Router; use MyProject\Exceptions\MySpecialException; require_once (dirname(__FILE__)."/vendor/autoload.php"); try { Router::route(dirname(__FILE__)."/src/CLI", 'default'); } catch (MySpecialException $ex) { /* ... */ } catch (\Exception $ex) { /* ... */ }
Notes:, (*32)
Router for web and CLI applications (PHP).
Apache-2.0
url php cli web router uri