├── .gitignore ├── README.md ├── composer.json └── src └── Router.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | phpunit.phar 3 | /vendor 4 | composer.phar 5 | composer.lock 6 | *.sublime-project 7 | *.sublime-workspace 8 | *.project 9 | /nbproject 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP Router 2 | ========== 3 | 4 | The Laravel router, for use outside of the Laravel framework. 5 | 6 | Installation 7 | ------------ 8 | 9 | Add the package to your `composer.json` and run `composer update`. 10 | 11 | { 12 | "require": { 13 | "seytar/php-router": "1.0.x-stable" 14 | } 15 | } 16 | 17 | Usage 18 | ----- 19 | 20 | To start using the router you will need to bootstrap it like this: 21 | 22 | require 'vendor/autoload.php'; 23 | 24 | use Seytar\Routing\Router; 25 | 26 | Router::bootstrap(function($ex) { 27 | header('Content-Type: text/html; charset=utf-8'); 28 | echo '404 - Page Not Found'; 29 | }); 30 | 31 | Once this has been done, you can define any route like you would in Laravel: 32 | 33 | Route::get('/', function() 34 | { 35 | echo 'Hello world.'; 36 | }); 37 | 38 | The bootstrap process will check if there is a `routes.php` file in your application, and will automatically load it for you. It will also register a shutdown function that dispatches the current request. If you want to dispatch the current request manually, you can call `Router::dispatch()`. 39 | 40 | The `Request`, `Response`, `Input` and `URL` facades are also available. 41 | 42 | #### Basic GET Route 43 | 44 | Route::get('/', function() 45 | { 46 | return 'Hello World'; 47 | }); 48 | 49 | #### Basic POST Route 50 | 51 | Route::post('foo/bar', function() 52 | { 53 | return 'Hello World'; 54 | }); 55 | 56 | #### Registering A Route For Multiple Verbs 57 | 58 | Route::match(array('GET', 'POST'), '/', function() 59 | { 60 | return 'Hello World'; 61 | }); 62 | 63 | #### Registering A Route Responding To Any HTTP Verb 64 | 65 | Route::any('foo', function() 66 | { 67 | return 'Hello World'; 68 | }); 69 | 70 | #### Forcing A Route To Be Served Over HTTPS 71 | 72 | Route::get('foo', array('https', function() 73 | { 74 | return 'Must be over HTTPS'; 75 | })); 76 | 77 | Often, you will need to generate URLs to your routes, you may do so using the `URL::to` method: 78 | 79 | $url = URL::to('foo'); 80 | 81 | 82 | ## Route Parameters 83 | 84 | Route::get('user/{id}', function($id) 85 | { 86 | return 'User '.$id; 87 | }); 88 | 89 | #### Optional Route Parameters 90 | 91 | Route::get('user/{name?}', function($name = null) 92 | { 93 | return $name; 94 | }); 95 | 96 | #### Optional Route Parameters With Defaults 97 | 98 | Route::get('user/{name?}', function($name = 'John') 99 | { 100 | return $name; 101 | }); 102 | 103 | #### Regular Expression Route Constraints 104 | 105 | Route::get('user/{name}', function($name) 106 | { 107 | // 108 | }) 109 | ->where('name', '[A-Za-z]+'); 110 | 111 | Route::get('user/{id}', function($id) 112 | { 113 | // 114 | }) 115 | ->where('id', '[0-9]+'); 116 | 117 | #### Passing An Array Of Wheres 118 | 119 | Of course, you may pass an array of constraints when necessary: 120 | 121 | Route::get('user/{id}/{name}', function($id, $name) 122 | { 123 | // 124 | }) 125 | ->where(array('id' => '[0-9]+', 'name' => '[a-z]+')) 126 | 127 | #### Defining Global Patterns 128 | 129 | If you would like a route parameter to always be constrained by a given regular expression, you may use the `pattern` method: 130 | 131 | Route::pattern('id', '[0-9]+'); 132 | 133 | Route::get('user/{id}', function($id) 134 | { 135 | // Only called if {id} is numeric. 136 | }); 137 | 138 | #### Accessing A Route Parameter Value 139 | 140 | If you need to access a route parameter value outside of a route, you may use the `Route::input` method: 141 | 142 | Route::filter('foo', function() 143 | { 144 | if (Route::input('id') == 1) 145 | { 146 | // 147 | } 148 | }); 149 | 150 | 151 | ## Route Filters 152 | 153 | Route filters provide a convenient way of limiting access to a given route, which is useful for creating areas of your site which require authentication. There are several filters included in the Laravel framework, including an `auth` filter, an `auth.basic` filter, a `guest` filter, and a `csrf` filter. These are located in the `app/filters.php` file. 154 | 155 | #### Defining A Route Filter 156 | 157 | Route::filter('old', function() 158 | { 159 | if (Input::get('age') < 200) 160 | { 161 | return Redirect::to('home'); 162 | } 163 | }); 164 | 165 | If the filter returns a response, that response is considered the response to the request and the route will not execute. Any `after` filters on the route are also cancelled. 166 | 167 | #### Attaching A Filter To A Route 168 | 169 | Route::get('user', array('before' => 'old', function() 170 | { 171 | return 'You are over 200 years old!'; 172 | })); 173 | 174 | #### Attaching A Filter To A Controller Action 175 | 176 | Route::get('user', array('before' => 'old', 'uses' => 'UserController@showProfile')); 177 | 178 | #### Attaching Multiple Filters To A Route 179 | 180 | Route::get('user', array('before' => 'auth|old', function() 181 | { 182 | return 'You are authenticated and over 200 years old!'; 183 | })); 184 | 185 | #### Attaching Multiple Filters Via Array 186 | 187 | Route::get('user', array('before' => array('auth', 'old'), function() 188 | { 189 | return 'You are authenticated and over 200 years old!'; 190 | })); 191 | 192 | #### Specifying Filter Parameters 193 | 194 | Route::filter('age', function($route, $request, $value) 195 | { 196 | // 197 | }); 198 | 199 | Route::get('user', array('before' => 'age:200', function() 200 | { 201 | return 'Hello World'; 202 | })); 203 | 204 | After filters receive a `$response` as the third argument passed to the filter: 205 | 206 | Route::filter('log', function($route, $request, $response) 207 | { 208 | // 209 | }); 210 | 211 | #### Pattern Based Filters 212 | 213 | You may also specify that a filter applies to an entire set of routes based on their URI. 214 | 215 | Route::filter('admin', function() 216 | { 217 | // 218 | }); 219 | 220 | Route::when('admin/*', 'admin'); 221 | 222 | In the example above, the `admin` filter would be applied to all routes beginning with `admin/`. The asterisk is used as a wildcard, and will match any combination of characters. 223 | 224 | You may also constrain pattern filters by HTTP verbs: 225 | 226 | Route::when('admin/*', 'admin', array('post')); 227 | 228 | #### Filter Classes 229 | 230 | For advanced filtering, you may wish to use a class instead of a Closure. Since filter classes are resolved out of the application [IoC Container](/docs/ioc), you will be able to utilize dependency injection in these filters for greater testability. 231 | 232 | #### Registering A Class Based Filter 233 | 234 | Route::filter('foo', 'FooFilter'); 235 | 236 | By default, the `filter` method on the `FooFilter` class will be called: 237 | 238 | class FooFilter { 239 | 240 | public function filter() 241 | { 242 | // Filter logic... 243 | } 244 | 245 | } 246 | 247 | If you do not wish to use the `filter` method, just specify another method: 248 | 249 | Route::filter('foo', 'FooFilter@foo'); 250 | 251 | 252 | ## Named Routes 253 | 254 | Named routes make referring to routes when generating redirects or URLs more convenient. You may specify a name for a route like so: 255 | 256 | Route::get('user/profile', array('as' => 'profile', function() 257 | { 258 | // 259 | })); 260 | 261 | You may also specify route names for controller actions: 262 | 263 | Route::get('user/profile', array('as' => 'profile', 'uses' => 'UserController@showProfile')); 264 | 265 | Now, you may use the route's name when generating URLs or redirects: 266 | 267 | $url = URL::route('profile'); 268 | 269 | $redirect = Redirect::route('profile'); 270 | 271 | You may access the name of a route that is running via the `currentRouteName` method: 272 | 273 | $name = Route::currentRouteName(); 274 | 275 | 276 | ## Route Groups 277 | 278 | Sometimes you may need to apply filters to a group of routes. Instead of specifying the filter on each route, you may use a route group: 279 | 280 | Route::group(array('before' => 'auth'), function() 281 | { 282 | Route::get('/', function() 283 | { 284 | // Has Auth Filter 285 | }); 286 | 287 | Route::get('user/profile', function() 288 | { 289 | // Has Auth Filter 290 | }); 291 | }); 292 | 293 | You may also use the `namespace` parameter within your `group` array to specify all controllers within that group as being in a given namespace: 294 | 295 | Route::group(array('namespace' => 'Admin'), function() 296 | { 297 | // 298 | }); 299 | 300 | 301 | ## Sub-Domain Routing 302 | 303 | Laravel routes are also able to handle wildcard sub-domains, and pass you wildcard parameters from the domain: 304 | 305 | #### Registering Sub-Domain Routes 306 | 307 | Route::group(array('domain' => '{account}.myapp.com'), function() 308 | { 309 | 310 | Route::get('user/{id}', function($account, $id) 311 | { 312 | // 313 | }); 314 | 315 | }); 316 | 317 | 318 | ## Route Prefixing 319 | 320 | A group of routes may be prefixed by using the `prefix` option in the attributes array of a group: 321 | 322 | Route::group(array('prefix' => 'admin'), function() 323 | { 324 | 325 | Route::get('user', function() 326 | { 327 | // 328 | }); 329 | 330 | }); 331 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seytar/php-router", 3 | "description": "The Laravel router, for use outside of the Laravel framework", 4 | "keywords": ["laravel", "router"], 5 | "license": "MIT", 6 | "homepage": "https://github.com/seytar/php-router", 7 | "authors": [ 8 | { 9 | "name": "Saadettin Sivaz", 10 | "homepage": "https://github.com/seytar" 11 | } 12 | ], 13 | "require": { 14 | "illuminate/routing": "4.*", 15 | "illuminate/events": "4.*" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "Seytar\\Routing\\": "src/" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Router.php: -------------------------------------------------------------------------------- 1 | 'Seytar\Routing\Router', 41 | 'App' => 'Illuminate\Support\Facades\App', 42 | 'Input' => 'Illuminate\Support\Facades\Input', 43 | 'Redirect' => 'Illuminate\Support\Facades\Redirect', 44 | 'Request' => 'Illuminate\Support\Facades\Request', 45 | 'Response' => 'Illuminate\Support\Facades\Response', 46 | 'Route' => 'Illuminate\Support\Facades\Route', 47 | 'URL' => 'Illuminate\Support\Facades\URL' 48 | ); 49 | 50 | /** 51 | * Create a new router instance. 52 | * 53 | * @return void 54 | */ 55 | public function __construct() 56 | { 57 | $this->bootstrap(); 58 | } 59 | 60 | public static function bootstrap($errorCallbacks) 61 | { 62 | // Only bootstrap once. 63 | if (static::$bootstrapped) 64 | return; 65 | 66 | // Load helper functions. 67 | require_once __DIR__ . '/../../../illuminate/support/Illuminate/Support/helpers.php'; 68 | 69 | // Directories. 70 | $basePath = str_finish(realpath(__DIR__ . '/..'), '/'); 71 | $controllersDirectory = $basePath . 'Controllers'; 72 | $modelsDirectory = $basePath . 'Models'; 73 | 74 | // Register the autoloader and add directories. 75 | ClassLoader::register(); 76 | ClassLoader::addDirectories(array($controllersDirectory, $modelsDirectory)); 77 | 78 | // Instantiate the container. 79 | $app = new Container; 80 | static::$container = $app; 81 | 82 | // Tell facade about the application instance. 83 | Facade::setFacadeApplication($app); 84 | 85 | // Register application instance with container 86 | $app['app'] = $app; 87 | 88 | // Set environment. 89 | $app['env'] = 'production'; 90 | 91 | // Enable HTTP Method Override. 92 | Request::enableHttpMethodParameterOverride(); 93 | 94 | // Create the request. 95 | $app['request'] = Request::createFromGlobals(); 96 | 97 | // Register services. 98 | with(new EventServiceProvider($app))->register(); 99 | with(new RoutingServiceProvider($app))->register(); 100 | 101 | // Register aliases. 102 | foreach (static::$aliases as $alias => $class) 103 | { 104 | class_alias($class, $alias); 105 | } 106 | 107 | // Load the routes file if it exists. 108 | if (file_exists($basePath . 'routes.php')) 109 | { 110 | require_once $basePath . 'routes.php'; 111 | } 112 | 113 | // Dispatch on shutdown. 114 | register_shutdown_function('Seytar\Routing\Router::dispatch', $errorCallbacks); 115 | 116 | // Mark bootstrapped. 117 | static::$bootstrapped = true; 118 | } 119 | 120 | /** 121 | * Dispatch the current request to the application. 122 | * 123 | * @return \Illuminate\Http\Response 124 | */ 125 | public static function dispatch($callbacks) 126 | { 127 | // Only dispatch once. 128 | if (static::$dispatched) return; 129 | 130 | // Get the request. 131 | $request = static::$container['request']; 132 | 133 | try { 134 | // Pass the request to the router. 135 | $response = static::$container['router']->dispatch($request); 136 | 137 | // Send the response. 138 | $response->send(); 139 | } catch (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $ex) { 140 | $callback = is_array($callbacks) ? $callbacks['not_found'] : $callbacks; 141 | call_user_func($callback, $ex); 142 | } catch (\Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException $ex) { 143 | $callback = is_array($callbacks) ? $callbacks['not_allowed'] : $callbacks; 144 | call_user_func($callback, $ex); 145 | } 146 | 147 | // Mark as dispatched. 148 | static::$dispatched = true; 149 | } 150 | 151 | /** 152 | * Dynamically pass calls to the router instance. 153 | * 154 | * @param string $method 155 | * @param array $parameters 156 | * @return mixed 157 | */ 158 | public function __call($method, $parameters) 159 | { 160 | return call_user_func_array(array(static::$container['router'], $method), $parameters); 161 | } 162 | 163 | } 164 | --------------------------------------------------------------------------------