iassasin / easyroute
Very lightweight and simple router for PHP
Installs: 52
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 2
pkg:composer/iassasin/easyroute
Requires
- php: >=7.0
- psr/container: ^1.0
Requires (Dev)
- phpunit/phpunit: ^6
- satooshi/php-coveralls: ^2.0
README
Simple router do not require a lot of setups, install in few minutes and just works.
Installation
To start using router complete 4 simple steps:
- Install
easyroutevia composer:
composer require iassasin/easyroute
- Setup yours routes in root directory of project. For example in file
routes.php:
require_once 'vendor/autoload.php'; use Iassasin\Easyroute\Router; use Iassasin\Easyroute\Route; $router = new Router(); $router->setControllersPath($_SERVER['DOCUMENT_ROOT'].'/controllers/'); $router->addRoutes([ new Route('/(:controller:(/:action:(/:arg)?)?)?', ['controller' => 'home', 'action' => 'index', 'arg' => null]), ]); $router->processRoute();
- Tell web server redirect all requests (except static
assets/) to router via.htaccess:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/assets/
RewriteRule ^(.*)$ routes.php [B,QSA,L]
- Create first controller in
controllers/home.php:
class ControllerHome { public function index($arg){ return '<html><body>Home page! '.($arg !== null ? 'Argument: '.$arg : 'Argument not set').'</body></html>'; // or use "return new Response('...')" which is more flexible } }
That's it!
Note that file name must match :controller name from URL template and controller class name must match :controller name with prefix Controller. In example above route /home/index matches file controllers/home.php and class ControllerHome.
Useful features
Route with parameters
Route is a regex string with simpler syntax for naming route arguments. For example:
/:controller/:action/:arg(all parameters are required)/(foo|bar)-:arg/gotcha(regex and partial parameter matching. Valid are/foo-123/gotcha,/bar-qwerty/gotcha, but not/qwerty/gotcha)/:arg?(parameterargcan be ommited, but not/. Valid are/,/param, but not/sub/param)/(:controller:(/:action:(/:arg)?)?)?(all parameters not required. Valid are/,/home,/home/index/val, but not/home/,/home/index/)
Parameter name must match regex [a-zA-Z_0-9]+.
By default :parameter match regex [^\/]+, but you can use your own filter:
new Route('/:arg(\d+)?', // arg not required and can contains only numerics ['controller' => 'home', 'action' => 'index', 'arg' => null], // default values )
If you need to use ( to match group, but not to filter parameter, use trailing : in parameter name: /:controller:(postfix1|postfix2) will match /homepostfix1 with controller = 'home'.
Built-in simple dependency injection container
You can use built-in dependency injection container to split app code into services (which will be loaded via autoload) and simply use it in controller's actions:
class ControllerHome { public function index($arg, Request $request, DatabaseService $db){ // ... } }
Services constructors can use dependency injection too and require another services via same syntax.
Class SampleContainer impletents Psr\Container\ContainerInterface.
use Iassasin\Easyroute\Router; $router = new Router(); /** @var SimpleContainer $container */ $container = $router->getContainer(); // get default container // Instantiate and add services by hand $container->setService(Database::class, new Database('login', 'password')); // Disable automatic instantiation for services $container->setAutowireEnabled(false);
Also you can use another dependency injection container:
use Iassasin\Easyroute\Router; use Iassasin\Easyroute\Http\Request; $router = new Router(); $request = Request::createFromGlobals(); // some implementation of ContainerInterface $container = new MyContainer(); // Router don't know how to register Request class in your container implementation // You should to do it by self if you want to use Request class in controllers // See next section for details of Request class $container->register(Request::class, $request); $router->setContainer($container);
Request class
Using dependency injection container you have access to Request service that provides access to request parameters.
Request has fields listed below:
query-$_GETarrayrequest-$_POSTarrayattributes- assoc array with any data, which you can set atRequestinstance creationcookies-$_COOKIEarrayfiles-$_FILESarrayserver-$_SERVERarray
Each field is instance of Parameters class and has methods:
get(string $name)- get parameter by it's namehas(string $name)- does parameter with$nameexistsall()- get all parameters in array
Request provide some useful methods:
getContent()- get whole http post content as stringgetClientIP()- returns$this->server->get('REMOTE_ADDR')getScriptName()- returns$this->server->get('SCRIPT_NAME')getScheme()- returns$this->server->get('REQUEST_SCHEME')getHost()- returns$this->server->get('SERVER_NAME')getUri()- returns$this->server->get('REQUEST_URI')getMethod()- returns$this->server->get('REQUEST_METHOD')getProtocol()- returns$this->server->get('SERVER_PROTOCOL')
Response class
Response class used to return data to client. Return Response instances from controller's actions instead of manual data send using echo or etc.
Built-in response classes
There is 3 built-in response classes:
Response- base class for all responses, has methods:__construct(string $content, int $statusCode = 200, array $headers = [])getStatusCode(),setStatusCode(int $code)- get/set http status codegetContent(),setContent(string $content)- get/set http response contentgetHeaders(),setHeaders(array $headers)- get/set http headerssetHeader(string $name, string $value)- set single headersend()- send response to client, called from handlers
Response404- extendsRequestfor default http 404 error with:__construct(string $url, array $headers = [])getUrl()- get URL caused 404 error
Response500- extendsRequestfor default http 500 error. Generated if any exception thrown during route processing.__construct(\Throwable $exception = null, array $headers = [])getException()- get exception thrown during route processing
ResponseJson- extendsRequestfor sending json responses, also set correct httpContent-Typeheader.__construct(object|array $data, int $statusCode = 200, array $headers = [])getData(),setData(object|array $data)- set source data to send to client- Note that
setContentcan't be used and throwsLogicException
Custom handlers for response classes
Set custom handlers for Response classes:
use Iassasin\Easyroute\Http\Responses\Response404; $router->setResponseHandler(Response404::class, function(Response404 $resp){ $resp->setContent('<html><body><h1>Custom 404 Not Found</h1> The requested url "<i>'.htmlspecialchars($resp->getUrl()).'</i>" not found!'); $resp->send(); return true; // do not call other handlers });
And custom handlers for any http status code:
use Iassasin\Easyroute\Http\Response; $router->setStatusHandler(302, function(Response $resp){ $resp->setContent('You have redirected'); $resp->send(); return true; // do not call other handlers });
Note 1: you can use both handlers set, but response handlers will be called first (and can stop calling status handlers).
Note 2: you can set handler for parent response class to match all childs, child handlers will be always called first: from most child to first parent. So, you can match all responses setting handler for
Response::class.
Subdirectories for controllers
Separate zones with subdirectories:
(new Route('/admin/:controller/:action?', ['action' => 'index'])) ->setControllersSubpath('zones/admin') // '/admin/home/index' will match 'controllers/zones/admin/home.php' // and class 'ControllerHome'
Filtering access
Use RouteFilter to prevent access to some routes:
use Iassasin\Easyroute\RouteFilter; class RouteFilterAdmin extends RouteFilter { public function preRoute($path, $controller, $action, $args){ if (!isCurrentUserAdmin()){ (new Response('Access denied!', 403))->send(); return Router::COMPLETED; // Do not call controller's action } return Router::CONTINUE; // Call controller's action } } //... (new Route('/admin/:controller/:action?', ['action' => 'index'])) ->setFilter(new RouteFilterAdmin())
Custom controller class name prefix
Set custom controller class name prefix (default - Controller):
$router->setControllerClassPrefix('TheController');