c9s / roller
High performance PHP router
Requires
- php: >=5.3.0
- corneltek/serializerkit: ~1
- doctrine/common: ~2.3
Requires (Dev)
- corneltek/phpunit-testmore: dev-master
This package is not auto-updated.
Last update: 2024-10-26 13:16:50 UTC
README
PHP Roller is a simple/fast router for PHP.
Roller compiles your routes into a simple array to store routes, therefore it's fast.
Beyond that, Roller provides an extension to improve more performance for you.
FEATURES
- Highly customizable
- Flexible
- Simple DSL (Sinatra-like syntax)
- APC cache support.
- File cache support.
- Built-in RESTful route generator, resource handler.
- Customizable RESTful route generator, resource handler.
- Simple, Useful route path syntax. (rails-style)
- High performance (through PHP extension, can dispatch 1607% faster than pure php version)
- High unit test coverage, coverage > 88%.
- Ready for Frameworks.
- Annotation reader support.
Benchmark
ROUTE CONSOLE DUMPER
INSTALL
Install from Composer:
{ "require": { "corneltek/roller": "~1.8" } }
Install through PEAR:
pear channel-discover pear.corneltek.com
pear install corneltek/Roller
Or clone this repository:
pear install -f package.xml
Use thie library as a git submodule: clone this repository, pick up a PSR-0 classloader, and add src/
to class
path.
To use console dumper, you need ezc/ConsoleTools, please use PEAR installer to install:
pear channel-discover components.ez.no
pear install -a ezc/ConsoleTools
SYNOPSIS
Roller router provides a simple DSL for you to declare sinatra-like routes:
require 'bootstrap.php'; require 'Roller/DSL.php'; on('/path/to/:date',function() { return 'your content'; }); on('/path/to/:year', [ ':year' => '\d+' ] ,function() { return 'your content'; }); dispatch( $_SERVER['PATH_INFO'] );
More usage:
Initialize a router:
$router = new Roller\Router;
Add a new route with simple callback
$router->add( '/blog/:id/:title' , function($id,$title) { return 'Blog'; });
Add a new route with class callback
$router->add( '/blog/:year/:month/:id/:title' , array('Controller','method') );
Add a new route with class/action callback string:
$router->add( '/path/to/blog' , 'Controller:methodName' );
To add a new route with requirement:
$router->add( '/path/to/:year' , array('Callback','method') , array( ':year' => '\d+', ));
To add a new route with requirement and closure:
$router->add( '/path/to/:year' , function($year) { return $year; },array( ':year' => '\d+', ));
Or by any
:
$router->any( '/path/to/:year' , function($year) { return $year; }, array( ':year' => '\d+', ));
An alias for GET method:
$router->get( '/path/to/:year' , function($year) { ... } );
An alias for POST method:
$router->post( '/path/to/:year' , function($year) { ... } );
META ATTRIBUTE
Roller currently supports: method
, default
, requirement
, post
, get
attributes.
To add a new route with requirement and default value:
$router->add( '/path/to/:year' , array('Callback','method') , array( ':year' => '\d+', 'default' => array( 'year' => 2000, ), ));
To add a new route with request method (like POST method):
$router->add( '/path/to/:year' , array('Callback','method') , array( ':year' => '\d+', 'method' => 'post', 'default' => array( 'year' => 2000, ), ));
RouteSet
RouteSet is a route collection class, you can mount a route set to another route set.
To use RouteSet is very easy:
$subroutes = new Roller\RouteSet; $subroutes->add( '/subitem' , $cb ); $routes = new Roller\RouteSet; $routes->mount( '/item' , $subroutes );
Mount paths
To mount route set:
$routes = new Roller\RouteSet; $routes->add( '/path/to/:year' , array( 'Callback', 'method' ) ); $routes = new Roller\RouteSet; $routes->mount( '/root' , $routes ); $router = new Roller\Router( $routes );
Dispatch
To dispatch path:
$r = $router->dispatch( $_SERVER['PATH_INFO'] );
To evalulate response content:
if( $r !== false ) echo $r(); else die('page not found.');
Customize Route Class
class YourRoute extends Roller\MatchedRoute { // customze here. } $r = new Roller\Router(array( 'route_class' => 'YourRoute' )); $route = $r->dispatch( '/path/to/...' ); // get YourRoute object.
Use Annotation Reader for controllers
class AnnotationTestController { /** * @Route("/hello/:name", name="_hello", requirements={":name" = ".+"}, vars={ "k" = 123, "b" = 234 }) */ function helloAction($name) { return $name; } /** * @Route("/") */ function indexAction() { return 'index'; } } $router = new Roller\Router; $router->importAnnotationMethods( 'AnnotationTestController' , '/Action$/' ); $route = $router->dispatch('/'); $route(); // returns 'index' $route = $router->dispatch('/hello/John'); $route(); // returns "John"
Cache
To enable apc cache:
$router = new Roller\Router( null , array( 'cache_id' => '_router_testing_' ));
To enable file cache:
$router = new Roller\Router( null , array( 'cache_id' => '_router_testing_', 'cache_dir' => 'tests/cache', ));
RESTful interface
Roller Router has a built-in RESTful route generator, it's pretty easy to define a bunch of RESTful routes, Roller Router also provides a simple RESTful route generator, which is pretty easy to customize your own RESTful routes.
First, initalize a RESTful plugin object:
$router = new Roller\Router; $restful = new Roller\Plugin\RESTful(array( 'prefix' => '/restful' ));
Add RESTful plugin to your router manager:
$router->addPlugin($restful);
To support RESTful, you have two solutions:
ResourceHandler
: If you need to define differnt logic for each resource, you can useResourceHandler
, you can separate different resource logic into different handler class.GenericHandler
: If your resources use the same logic and the same permission controll, you can useGenericHandler
for every resources.
To use ResourceHandler, register your resource id to router with your resource handler class name, each resource id is mapping to an resource handler:
$restful->registerResource( 'blog' , 'BlogResourceHandler' );
Define your resource handler, here is a simple blog example that defines how RESTful CRUD works:
use Roller\Plugin\RESTful\ResourceHandler; class BlogResourceHandler extends ResourceHandler { public function create() { $this->codeCreated(); return array( 'id' => 1 ); } public function update($id) { $put = $this->parseInput(); return array( 'id' => 1 ); } // delete a record. public function delete($id) { return array( 'id' => 1 ); } // load one record public function load($id) { return array( 'id' => $id , 'title' => 'title' ); } // find records public function find() { return array( array( 'id' => 0 ), array( 'id' => 1 ), array( 'id' => 2 ), ); } }
For the status code, see the list below:
- codeOk (200)
- codeCreated (201)
- codeAccepted (202)
- codeNoContent (204)
- codeBadRequest (400)
- codeForbidden (403)
- codeNotFound (404)
Before you dispatch URLs, router object calls the expand
method of ResourceHandler
class, which
generates RESTful routes into the routeset of router object. And below is the generated URLs:
GET /restful/blog - get blog list
GET /restful/blog/:id - get one blog record
POST /restful/blog - create one blog record
PUT /restful/blog/:id - update one blog record
DELETE /restful/blog/:id - delete one blog record
You can override the expand
method to define your own style RESTful URLs.
Now you should be able to dispatch RESTful urls:
$_SERVER['REQUEST_METHOD'] = 'get'; $r = $router->dispatch('/restful/blog/1'); // returns {"success":true,"data":{"id":"1","title":"title"},"message":"Record 1 loaded."} $r(); $_SRVER['REQUEST_METHOD'] = 'get'; $r = $router->dispatch('/restful/blog'); // {"success":true,"data":[{"id":0},{"id":1},{"id":2}],"message":"Record find success."} $r();
Customize Resource Handler
Here is how RESTful route generator works:
static function expand($routes, $h, $r) { $routes->add( "/$r(\.:format)" , array($h,'handleFind'), array( 'get' => true , 'default' => array( 'format' => 'json' ) )); $routes->add( '/' . $r . '(\.:format)' , array($h,'handleCreate'), array( 'post' => true, 'default' => array( 'format' => 'json' ) )); $routes->add( '/' . $r . '/:id(\.:format)' , array($h,'handleLoad'), array( 'get' => true, 'default' => array( 'format' => 'json' ) )); $routes->add( '/' . $r . '/:id(\.:format)' , array($h,'handleUpdate'), array( 'put' => true, 'default' => array( 'format' => 'json' ) )); $routes->add( '/' . $r . '/:id(\.:format)' , array($h,'handleDelete'), array( 'delete' => true, 'default' => array( 'format' => 'json' ) )); }
To define your own RESTful Resource Handler (Generator), you can simply inherit class from
Roller\Plugin\RESTful\ResourceHandler
:
use Roller\Plugin\RESTful\ResourceHandler; class YourResourceHandler extends ResourceHandler { // define your own expand method static function expand( $routes , $handlerClass, $resourceId ) { } }
.htaccess File for Apache
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-s
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ your_router.php/$1 [NC,L]
Install Extension
$ cd extension
$ phpize
$ ./configure
$ make && make install
Add config to your php.ini:
extension=roller.so
Hacking
Run Composer Install:
composer install --dev
Install ConsoleTools:
pear channel-discover components.ez.no
pear install -a ezc/ConsoleTools
Get Onion and install it:
$ curl -s http://install.onionphp.org/ | bash
Run onion to install depedencies:
$ onion install
Now you should be able to run phpunit =)
$ phpunit tests
TODO
- Expandable Controller (route table inside)
- Auto Expandable Controller (expand routes automatically, based on method name)