mirror of
https://github.com/skipperbent/simple-php-router.git
synced 2026-06-18 17:26:28 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b3d71a328 | |||
| 491e920cfc | |||
| 1fae638aaf | |||
| 6cef099110 | |||
| 5f95290e4b | |||
| 7234415e24 | |||
| 257875c6f9 | |||
| 9b743e6e57 | |||
| 457dbc5710 | |||
| fc4fd0edf1 | |||
| 2f2c3ca3ca | |||
| b34738a51a | |||
| 37c8bc9f32 | |||
| 975c27659c | |||
| 75029b330a | |||
| 27371dfa11 | |||
| 6e102711a8 | |||
| 4d58fbf7b5 | |||
| fd5d893040 | |||
| 35bb58818e | |||
| c1512740af | |||
| 212ae133de | |||
| 438d3c258b | |||
| ae58231fa1 | |||
| a58ec1544d | |||
| 1bafbab56b | |||
| 6395801a06 | |||
| 060479f1fe | |||
| b29f2ce37d | |||
| 358b25d4f1 | |||
| 5483ffe458 | |||
| 6d8e95fcaa | |||
| 3d45851d9b | |||
| db78135d19 | |||
| c4fcf750d4 | |||
| ee5c2207f8 | |||
| a92b6008fa | |||
| b1ca3fc9ef | |||
| 253c0c70d4 | |||
| 115c8e510a |
+2
-1
@@ -1,2 +1,3 @@
|
||||
.idea
|
||||
composer.lock
|
||||
composer.lock
|
||||
vendor/
|
||||
@@ -1,19 +1,16 @@
|
||||
# Simple PHP router
|
||||
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing.
|
||||
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expandability in mind.
|
||||
|
||||
## Installation
|
||||
Add the latest version of Simple PHP Router to your ```composer.json```
|
||||
Add the latest version of Simple PHP Router running this command.
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"pecee/simple-php-router": "1.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"pecee/simple-php-router": "1.*"
|
||||
}
|
||||
}
|
||||
```
|
||||
composer require pecee/simple-router
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP 5.4 or greater
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -32,10 +29,7 @@ The goal of this project is to create a router that is 100% compatible with the
|
||||
- CSRF protection.
|
||||
- Optional parameters
|
||||
- Sub-domain routing
|
||||
|
||||
### Features currently "in-the-works"
|
||||
|
||||
- Global Constraints
|
||||
- Custom boot managers to redirect urls to other routes
|
||||
|
||||
## Initialising the router
|
||||
|
||||
@@ -44,7 +38,7 @@ In your ```index.php``` require your ```routes.php``` and call the ```routeReque
|
||||
This is an example of a basic ```index.php``` file:
|
||||
|
||||
```php
|
||||
use \Pecee\SimpleRouter;
|
||||
use \Pecee\SimpleRouter\SimpleRouter;
|
||||
|
||||
require_once 'routes.php'; // change this to whatever makes sense in your project
|
||||
|
||||
@@ -52,7 +46,7 @@ require_once 'routes.php'; // change this to whatever makes sense in your projec
|
||||
$defaultControllerNamespace = 'MyWebsite\\Controller';
|
||||
|
||||
// Do the routing
|
||||
SimpleRouter::init($defaultControllerNamespace);
|
||||
SimpleRouter::start($defaultControllerNamespace);
|
||||
```
|
||||
|
||||
## Adding routes
|
||||
@@ -61,6 +55,9 @@ This router is heavily inspired by the Laravel 5.* router, so anything you find
|
||||
|
||||
### Basic example
|
||||
|
||||
- ExceptionsHandlers must implement the `IExceptionHandler` interface.
|
||||
- Middlewares must implement the `IMiddleware` interface.
|
||||
|
||||
```php
|
||||
use Pecee\SimpleRouter\SimpleRouter;
|
||||
|
||||
@@ -72,12 +69,15 @@ use Pecee\SimpleRouter\SimpleRouter;
|
||||
* the request, for instance if a user is not authenticated.
|
||||
*/
|
||||
|
||||
// Add CSRF support (if needed)
|
||||
SimpleRouter::csrfVerifier(new \Pecee\Http\Middleware\BaseCsrfVerifier());
|
||||
|
||||
SimpleRouter::group(['prefix' => 'v1', 'middleware' => '\MyWebsite\Middleware\SomeMiddlewareClass'], function() {
|
||||
|
||||
SimpleRouter::group(['prefix' => '/services', 'exceptionHandler' => '\MyProject\Handler\CustomExceptionHandler'], function() {
|
||||
|
||||
SimpleRouter::get('/answers/{id}', 'ControllerAnswers@show')->where(['id' => '[0-9]+');
|
||||
|
||||
|
||||
// Optional parameter
|
||||
SimpleRouter::get('/answers/{id?}', 'ControllerAnswers@show');
|
||||
|
||||
@@ -170,23 +170,6 @@ class Router extends SimpleRouter {
|
||||
parent::start($defaultNamespace);
|
||||
} catch(\Exception $e) {
|
||||
|
||||
$route = RouterBase::getInstance()->getLoadedRoute();
|
||||
|
||||
$exceptionHandler = null;
|
||||
|
||||
// Load and use exception-handler defined on group
|
||||
|
||||
if($route && $route->getGroup()) {
|
||||
$exceptionHandler = $route->getGroup()->getExceptionHandler();
|
||||
|
||||
if($exceptionHandler !== null) {
|
||||
$class = new $exceptionHandler();
|
||||
$class->handleError(RouterBase::getInstance()->getRequest(), $route, $e);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise use the fallback default exceptions handler
|
||||
|
||||
if(self::$defaultExceptionHandler !== null) {
|
||||
$class = new self::$defaultExceptionHandler();
|
||||
$class->handleError(RouterBase::getInstance()->getRequest(), $route, $e);
|
||||
@@ -270,9 +253,63 @@ Register the new class in your ```routes.php```, custom ```Router``` class or wh
|
||||
SimpleRouter::csrfVerifier(new \Demo\Middleware\CsrfVerifier());
|
||||
```
|
||||
|
||||
## Using router bootmanager to make custom rewrite rules
|
||||
|
||||
Sometimes it can be necessary to keep urls stored in the database, file or similar. In this example, we want the url ```/my-cat-is-beatiful``` to load the route ```/article/view/1``` which the router knows, because it's defined in the ```routes.php``` file.
|
||||
|
||||
To interfere with the router, we create a class that inherits from ```RouterBootManager```. This class will be loaded before any other rules in ```routes.php``` and allow us to "change" the current route, if any of our criteria are fulfilled (like coming from the url ```/my-cat-is-beatiful```).
|
||||
|
||||
```php
|
||||
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\RouterBootManager;
|
||||
|
||||
class CustomRouterRules extends RouterBootManager{
|
||||
|
||||
public function boot(Request $request) {
|
||||
|
||||
$rewriteRules = [
|
||||
'/my-cat-is-beatiful' => '/article/view/1',
|
||||
'/horses-are-great' => '/article/view/2'
|
||||
];
|
||||
|
||||
foreach($rewriteRules as $url => $rule) {
|
||||
|
||||
// If the current uri matches the url, we use our custom route
|
||||
|
||||
if($request->getUri() === $url) {
|
||||
$request->setUri($rule);
|
||||
}
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The above should be pretty self-explanatory and can easily be changed to loop through urls store in the database, file or cache.
|
||||
|
||||
What happens is that if the current route matches the route defined in the index of our ```$rewriteRules``` array, we set the route to the array value instead.
|
||||
|
||||
By doing this the route will now load the url ```/article/view/1``` instead of ```/my-cat-is-beatiful```.
|
||||
|
||||
The last thing we need to do, is to add our custom boot-manager to the ```routes.php``` file. You can create as many bootmanagers as you like and easily add them in your ```routes.php``` file.
|
||||
|
||||
**routes.php example:**
|
||||
|
||||
```php
|
||||
// Add new bootmanager
|
||||
SimpleRouter::addBootManager(new CustomRouterRules());
|
||||
|
||||
// This rule is what our custom bootmanager will use.
|
||||
SimpleRouter::get('/article/view/{id}', 'ControllerArticle@view');
|
||||
```
|
||||
|
||||
## Easily overwrite route about to be loaded
|
||||
Sometimes it can be useful to manipulate the route that's about to be loaded, for instance if a user is not authenticated or if an error occurred within your Middleware that requires
|
||||
some other route to be initialised. Simple PHP Router allows you to easily change the route about to be executed. All information about the current route is stored in
|
||||
Sometimes it can be useful to manipulate the route that's about to be loaded, for instance if a user is not authenticated or if an error occurred within your Middleware that requires
|
||||
some other route to be initialised. Simple PHP Router allows you to easily change the route about to be executed. All information about the current route is stored in
|
||||
the ```\Pecee\SimpleRouter\Http\Request``` object.
|
||||
|
||||
**Note:** Please note that it's only possible to change the route BEFORE any route has initially been loaded, so doing this in your custom ExceptionHandler or Middleware is highly recommended.
|
||||
@@ -288,6 +325,12 @@ $route->setClass('Example\MyCustomClass');
|
||||
$route->setMethod('hello');
|
||||
```
|
||||
|
||||
## Sites
|
||||
This is some sites that uses the simple-router project in production.
|
||||
|
||||
- [holla.dk](http://www.holla.dk)
|
||||
- [ninjaimg.com](http://ninjaimg.com)
|
||||
|
||||
## Documentation
|
||||
While I work on a better documentation, please refer to the Laravel 5 routing documentation here:
|
||||
|
||||
@@ -297,11 +340,11 @@ http://laravel.com/docs/5.1/routing
|
||||
The router can be easily extended to customize your needs.
|
||||
|
||||
## Ideas and issues
|
||||
If you want a great new feature or experience any issues what-so-ever, please feel free to leave an issue and i'll look into it whenever possible.
|
||||
If you want a great new feature or experience any issues what-so-ever, please feel free to leave an issue and i'll look into it whenever possible.
|
||||
|
||||
## The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Simon Sessingø / simple-php-router
|
||||
Copyright (c) 2016 Simon Sessingø / simple-php-router
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.7.7"
|
||||
|
||||
@@ -37,7 +37,7 @@ class CsrfToken {
|
||||
* @param $token
|
||||
*/
|
||||
public function setToken($token) {
|
||||
setcookie(self::CSRF_KEY, $token, time() + 60 * 120, '/');
|
||||
setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ class CsrfToken {
|
||||
*/
|
||||
public function getToken(){
|
||||
if($this->hasToken()) {
|
||||
return $_COOKIE[self::CSRF_KEY];
|
||||
return $_COOKIE[static::CSRF_KEY];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ class CsrfToken {
|
||||
* @return bool
|
||||
*/
|
||||
public function hasToken() {
|
||||
return isset($_COOKIE[self::CSRF_KEY]);
|
||||
return isset($_COOKIE[static::CSRF_KEY]);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
namespace Pecee\SimpleRouter;
|
||||
namespace Pecee\Exception;
|
||||
class RouterException extends \Exception { }
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace Pecee\Handler;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\RouterEntry;
|
||||
|
||||
interface IExceptionHandler {
|
||||
|
||||
public function handleError(Request $request, RouterEntry $router = null, \Exception $error);
|
||||
|
||||
}
|
||||
@@ -12,10 +12,13 @@ class BaseCsrfVerifier implements IMiddleware {
|
||||
|
||||
protected $except;
|
||||
protected $csrfToken;
|
||||
|
||||
protected $token;
|
||||
|
||||
public function __construct() {
|
||||
$this->csrfToken = new CsrfToken();
|
||||
|
||||
// Generate or get the CSRF-Token from Cookie.
|
||||
$this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,14 +53,14 @@ class BaseCsrfVerifier implements IMiddleware {
|
||||
|
||||
if($request->getMethod() != 'get' && !$this->skip($request)) {
|
||||
|
||||
$token = (isset($_POST[self::POST_KEY])) ? $_POST[self::POST_KEY] : null;
|
||||
$token = (isset($_POST[static::POST_KEY])) ? $_POST[static::POST_KEY] : null;
|
||||
|
||||
// If the token is not posted, check headers for valid x-csrf-token
|
||||
if($token === null) {
|
||||
$token = $request->getHeader(self::HEADER_KEY);
|
||||
$token = $request->getHeader(static::HEADER_KEY);
|
||||
}
|
||||
|
||||
if( !$this->csrfToken->validate( $token ) ) {
|
||||
if( !$this->csrfToken->validate($token) ) {
|
||||
throw new TokenMismatchException('Invalid csrf-token.');
|
||||
}
|
||||
|
||||
@@ -65,4 +68,22 @@ class BaseCsrfVerifier implements IMiddleware {
|
||||
|
||||
}
|
||||
|
||||
public function generateToken() {
|
||||
$token = $this->csrfToken->generateToken();
|
||||
$this->csrfToken->setToken($token);
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function hasToken() {
|
||||
if($this->token != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->csrfToken->hasToken();
|
||||
}
|
||||
|
||||
public function getToken() {
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +1,11 @@
|
||||
<?php
|
||||
namespace Pecee\Http;
|
||||
|
||||
use Pecee\SimpleRouter\RouterBase;
|
||||
|
||||
class Request {
|
||||
|
||||
protected static $instance;
|
||||
|
||||
protected $data;
|
||||
protected $uri;
|
||||
protected $host;
|
||||
protected $method;
|
||||
protected $headers;
|
||||
protected $loadedRoute;
|
||||
|
||||
/**
|
||||
* Return new instance
|
||||
@@ -44,6 +37,9 @@ class Request {
|
||||
}
|
||||
|
||||
public function getIsSecure() {
|
||||
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
|
||||
return true;
|
||||
}
|
||||
return isset($_SERVER['HTTPS']) ? true : (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] === 443);
|
||||
}
|
||||
|
||||
@@ -135,6 +131,17 @@ class Request {
|
||||
return (isset($_REQUEST[$name]) ? $_REQUEST[$name] : $defaultValue);
|
||||
}
|
||||
|
||||
public function isFormatAccepted($format) {
|
||||
return (isset($_SERVER['HTTP_ACCEPT']) && stripos($_SERVER['HTTP_ACCEPT'], $format) > -1);
|
||||
}
|
||||
|
||||
public function getAcceptFormats() {
|
||||
if(isset($_SERVER['HTTP_ACCEPT'])) {
|
||||
return explode(',', $_SERVER['HTTP_ACCEPT']);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function __set($name, $value = null) {
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
@@ -151,4 +158,25 @@ class Request {
|
||||
return $this->loadedRoute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $uri
|
||||
*/
|
||||
public function setUri($uri) {
|
||||
$this->uri = $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $host
|
||||
*/
|
||||
public function setHost($host) {
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $method
|
||||
*/
|
||||
public function setMethod($method) {
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,9 +19,14 @@ class Response {
|
||||
* Redirect the response
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $httpCode
|
||||
*/
|
||||
public function redirect($url) {
|
||||
$this->header('Location: ' . $url);
|
||||
public function redirect($url, $httpCode = null) {
|
||||
if($httpCode !== null) {
|
||||
$this->httpCode($httpCode);
|
||||
}
|
||||
|
||||
$this->header('location: ' . $url);
|
||||
die();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
interface IRouteEntry {
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\CsrfToken;
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Handler\IExceptionHandler;
|
||||
use Pecee\Http\Middleware\BaseCsrfVerifier;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
@@ -16,21 +17,17 @@ class RouterBase {
|
||||
protected $controllerUrlMap;
|
||||
protected $backStack;
|
||||
protected $defaultNamespace;
|
||||
protected $bootManagers;
|
||||
protected $baseCsrfVerifier;
|
||||
|
||||
// TODO: make interface for controller routers, so they can be easily detected
|
||||
// TODO: clean up - cut some of the methods down to smaller pieces
|
||||
|
||||
public function __construct() {
|
||||
$this->routes = array();
|
||||
$this->backStack = array();
|
||||
$this->controllerUrlMap = array();
|
||||
$this->baseCsrfVerifier = new BaseCsrfVerifier();
|
||||
$this->request = Request::getInstance();
|
||||
|
||||
$csrf = new CsrfToken();
|
||||
$token = ($csrf->hasToken()) ? $csrf->getToken() : $csrf->generateToken();
|
||||
$csrf->setToken($token);
|
||||
$this->bootManagers = array();
|
||||
}
|
||||
|
||||
public function addRoute(RouterEntry $route) {
|
||||
@@ -71,13 +68,13 @@ class RouterBase {
|
||||
|
||||
$newPrefixes = $prefixes;
|
||||
|
||||
if($route->getPrefix()) {
|
||||
array_push($newPrefixes, rtrim($route->getPrefix(), '/'));
|
||||
if($route->getPrefix() && trim($route->getPrefix(), '/') !== '') {
|
||||
array_push($newPrefixes, trim($route->getPrefix(), '/'));
|
||||
}
|
||||
|
||||
if(!($route instanceof RouterGroup)) {
|
||||
if(is_array($newPrefixes) && count($newPrefixes) && $backStack) {
|
||||
$route->setUrl( join('/', $newPrefixes) . $route->getUrl() );
|
||||
$route->setUrl( '/' . join('/', $newPrefixes) . $route->getUrl() );
|
||||
}
|
||||
|
||||
$group = null;
|
||||
@@ -88,8 +85,14 @@ class RouterBase {
|
||||
|
||||
if($route instanceof RouterGroup && is_callable($route->getCallback())) {
|
||||
$group = $route;
|
||||
|
||||
// Load middleware on group if route matches
|
||||
if($route->getPrefix() !== null && $route->matchRoute($this->request)) {
|
||||
$route->loadMiddleware($this->request);
|
||||
}
|
||||
|
||||
$route->renderRoute($this->request);
|
||||
$mergedSettings = array_merge($route->getMergeableSettings(), $settings);
|
||||
$mergedSettings = array_merge($settings, $route->getMergeableSettings());
|
||||
}
|
||||
|
||||
$this->currentRoute = null;
|
||||
@@ -106,12 +109,23 @@ class RouterBase {
|
||||
|
||||
public function routeRequest() {
|
||||
|
||||
$originalUri = $this->request->getUri();
|
||||
|
||||
// Initialize boot-managers
|
||||
if(count($this->bootManagers)) {
|
||||
/* @var $manager RouterBootManager */
|
||||
foreach($this->bootManagers as $manager) {
|
||||
$this->request = $manager->boot($this->request);
|
||||
|
||||
if(!($this->request instanceof Request)) {
|
||||
throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify csrf token for request
|
||||
if($this->baseCsrfVerifier !== null) {
|
||||
/* @var $csrfVerifier BaseCsrfVerifier */
|
||||
$csrfVerifier = $this->baseCsrfVerifier;
|
||||
$csrfVerifier = new $csrfVerifier();
|
||||
$csrfVerifier->handle($this->request);
|
||||
$this->baseCsrfVerifier->handle($this->request);
|
||||
}
|
||||
|
||||
// Loop through each route-request
|
||||
@@ -119,6 +133,14 @@ class RouterBase {
|
||||
|
||||
$routeNotAllowed = false;
|
||||
|
||||
// Make sure routes with longer urls are rendered first
|
||||
usort($this->controllerUrlMap, function($a, $b) {
|
||||
if(strlen($a->getUrl()) < strlen($b->getUrl())) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
|
||||
$max = count($this->controllerUrlMap);
|
||||
|
||||
/* @var $route RouterEntry */
|
||||
@@ -126,12 +148,6 @@ class RouterBase {
|
||||
|
||||
$route = $this->controllerUrlMap[$i];
|
||||
|
||||
if($route->getGroup() !== null) {
|
||||
if($route->getGroup()->matchDomain($this->request) === false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$routeMatch = $route->matchRoute($this->request);
|
||||
|
||||
if($routeMatch) {
|
||||
@@ -143,16 +159,24 @@ class RouterBase {
|
||||
|
||||
$routeNotAllowed = false;
|
||||
|
||||
$this->request->rewrite_uri = $this->request->uri;
|
||||
$this->request->setUri($originalUri);
|
||||
|
||||
$this->request->loadedRoute = $route;
|
||||
$route->loadMiddleware($this->request);
|
||||
|
||||
$this->request->loadedRoute->renderRoute($this->request);
|
||||
try {
|
||||
$this->request->loadedRoute->renderRoute($this->request);
|
||||
} catch(\Exception $e) {
|
||||
$this->handleException($e);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($routeNotAllowed) {
|
||||
throw new RouterException('Route or method not allowed', 403);
|
||||
$this->handleException(new RouterException('Route or method not allowed', 403));
|
||||
}
|
||||
|
||||
if(!$this->request->loadedRoute) {
|
||||
@@ -160,6 +184,19 @@ class RouterBase {
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleException(\Exception $e) {
|
||||
if($this->request->loadedRoute !== null && $this->request->loadedRoute->exceptionHandler !== null) {
|
||||
$handler = new $this->request->loadedRoute->exceptionHandler();
|
||||
if(!($handler instanceof IExceptionHandler)) {
|
||||
throw new RouterException('Exception handler must implement the IExceptionHandler interface.');
|
||||
}
|
||||
|
||||
$handler->handleError($this->request, $this->request->loadedRoute, $e);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -174,6 +211,24 @@ class RouterBase {
|
||||
$this->defaultNamespace = $defaultNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getBootManagers() {
|
||||
return $this->bootManagers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $bootManagers
|
||||
*/
|
||||
public function setBootManagers(array $bootManagers) {
|
||||
$this->bootManagers = $bootManagers;
|
||||
}
|
||||
|
||||
public function addBootManager(RouterBootManager $bootManager) {
|
||||
$this->bootManagers[] = $bootManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RouterEntry
|
||||
*/
|
||||
@@ -342,7 +397,7 @@ class RouterBase {
|
||||
$route = $this->controllerUrlMap[$i];
|
||||
|
||||
// Check an alias exist, if the matches - use it
|
||||
if($route instanceof RouterRoute && strtolower($route->getAlias()) === strtolower($controller)) {
|
||||
if($route instanceof RouterRoute && $route->hasAlias($controller)) {
|
||||
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
|
||||
}
|
||||
|
||||
@@ -392,7 +447,6 @@ class RouterBase {
|
||||
|
||||
$url = '/' . trim(join('/', $url), '/') . '/';
|
||||
|
||||
|
||||
if($getParams !== null && count($getParams)) {
|
||||
$url .= '?' . $this->arrayToParams($getParams);
|
||||
}
|
||||
@@ -407,4 +461,8 @@ class RouterBase {
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function reset() {
|
||||
self::$instance = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
|
||||
abstract class RouterBootManager {
|
||||
|
||||
abstract public function boot(Request $request);
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class RouterController extends RouterEntry {
|
||||
@@ -43,7 +44,7 @@ class RouterController extends RouterEntry {
|
||||
}
|
||||
|
||||
public function matchRoute(Request $request) {
|
||||
$url = parse_url($request->getUri());
|
||||
$url = parse_url(urldecode($request->getUri()));
|
||||
$url = rtrim($url['path'], '/') . '/';
|
||||
|
||||
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
|
||||
@@ -78,10 +79,12 @@ class RouterController extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return static
|
||||
*/
|
||||
public function setUrl($url) {
|
||||
$url = rtrim($url, '/') . '/';
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,9 +96,11 @@ class RouterController extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @return static
|
||||
*/
|
||||
public function setController($controller) {
|
||||
$this->controller = $controller;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,9 +112,11 @@ class RouterController extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @return static
|
||||
*/
|
||||
public function setMethod($method) {
|
||||
$this->method = $method;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
@@ -92,7 +93,7 @@ abstract class RouterEntry {
|
||||
* @return self
|
||||
*/
|
||||
public function setPrefix($prefix) {
|
||||
$this->prefix = '/' . trim($prefix, '/') . '/';
|
||||
$this->prefix = '/' . ltrim($prefix, '/');
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -122,7 +123,7 @@ abstract class RouterEntry {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|array
|
||||
*/
|
||||
public function getMiddleware() {
|
||||
return $this->middleware;
|
||||
@@ -359,9 +360,7 @@ abstract class RouterEntry {
|
||||
}
|
||||
|
||||
public function renderRoute(Request $request) {
|
||||
|
||||
if(is_object($this->getCallback()) && is_callable($this->getCallback())) {
|
||||
|
||||
// When the callback is a function
|
||||
call_user_func_array($this->getCallback(), $this->getParameters());
|
||||
} else {
|
||||
|
||||
@@ -2,14 +2,11 @@
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class RouterGroup extends RouterEntry {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function matchDomain(Request $request) {
|
||||
if($this->domain !== null) {
|
||||
|
||||
@@ -66,7 +63,12 @@ class RouterGroup extends RouterEntry {
|
||||
}
|
||||
|
||||
public function matchRoute(Request $request) {
|
||||
return null;
|
||||
// Skip if prefix doesn't match
|
||||
if($this->getPrefix() !== null && stripos($request->getUri(), $this->getPrefix()) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->matchDomain($request);
|
||||
}
|
||||
|
||||
public function setExceptionHandler($class) {
|
||||
@@ -82,4 +84,18 @@ class RouterGroup extends RouterEntry {
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $settings
|
||||
* @return self
|
||||
*/
|
||||
public function addSettings(array $settings = null) {
|
||||
if($this->getNamespace() !== null && isset($settings['namespace'])) {
|
||||
unset($settings['namespace']);
|
||||
}
|
||||
if(is_array($settings)) {
|
||||
$this->settings = array_merge($this->settings, $settings);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class RouterResource extends RouterEntry {
|
||||
@@ -46,7 +47,7 @@ class RouterResource extends RouterEntry {
|
||||
}
|
||||
|
||||
public function matchRoute(Request $request) {
|
||||
$url = parse_url($request->getUri());
|
||||
$url = parse_url(urldecode($request->getUri()));
|
||||
$url = rtrim($url['path'], '/') . '/';
|
||||
|
||||
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
|
||||
@@ -108,10 +109,12 @@ class RouterResource extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return static
|
||||
*/
|
||||
public function setUrl($url) {
|
||||
$url = rtrim($url, '/') . '/';
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,9 +126,11 @@ class RouterResource extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @return static
|
||||
*/
|
||||
public function setController($controller) {
|
||||
$this->controller = $controller;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\ArrayUtil;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class RouterRoute extends RouterEntry {
|
||||
@@ -15,13 +14,11 @@ class RouterRoute extends RouterEntry {
|
||||
parent::__construct();
|
||||
$this->setUrl($url);
|
||||
$this->setCallback($callback);
|
||||
|
||||
$this->settings['aliases'] = array();
|
||||
}
|
||||
|
||||
public function matchRoute(Request $request) {
|
||||
|
||||
$url = parse_url($request->getUri());
|
||||
$url = parse_url(urldecode($request->getUri()));
|
||||
$url = rtrim($url['path'], '/') . '/';
|
||||
|
||||
// Match on custom defined regular expression
|
||||
@@ -50,8 +47,6 @@ class RouterRoute extends RouterEntry {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -88,15 +83,37 @@ class RouterRoute extends RouterEntry {
|
||||
|
||||
/**
|
||||
* Get alias for the url which can be used when getting the url route.
|
||||
* @return string
|
||||
* @return string|array
|
||||
*/
|
||||
public function getAlias(){
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if route has given alias.
|
||||
*
|
||||
* @param $name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAlias($name) {
|
||||
if(is_array($this->alias)) {
|
||||
foreach($this->alias as $alias) {
|
||||
if(strtolower($alias) === strtolower($name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(strtolower($this->getAlias()) === strtolower($name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the url alias for easier getting the url route.
|
||||
* @param string $alias
|
||||
* @param string|array $alias
|
||||
* @return self
|
||||
*/
|
||||
public function setAlias($alias){
|
||||
|
||||
@@ -16,7 +16,7 @@ class SimpleRouter {
|
||||
/**
|
||||
* Start/route request
|
||||
* @param null $defaultNamespace
|
||||
* @throws RouterException
|
||||
* @throws \Pecee\Exception\RouterException
|
||||
*/
|
||||
public static function start($defaultNamespace = null) {
|
||||
$router = RouterBase::getInstance();
|
||||
@@ -32,6 +32,10 @@ class SimpleRouter {
|
||||
RouterBase::getInstance()->setBaseCsrfVerifier($baseCsrfVerifier);
|
||||
}
|
||||
|
||||
public static function addBootManager(RouterBootManager $bootManager) {
|
||||
RouterBase::getInstance()->addBootManager($bootManager);
|
||||
}
|
||||
|
||||
public static function get($url, $callback, array $settings = null) {
|
||||
$route = new RouterRoute($url, $callback);
|
||||
$route->addSettings($settings);
|
||||
@@ -94,7 +98,7 @@ class SimpleRouter {
|
||||
* Adds get + post route
|
||||
*
|
||||
* @param string $url
|
||||
* @param function $callback
|
||||
* @param callable $callback
|
||||
* @param array|null $settings
|
||||
* @return RouterRoute
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
class DummyController {
|
||||
|
||||
public function start() {
|
||||
echo static::class . '@' .'start() OK';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
require_once 'Exceptions/MiddlewareLoadedException.php';
|
||||
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class DummyMiddleware implements IMiddleware {
|
||||
|
||||
public function handle(Request $request) {
|
||||
throw new MiddlewareLoadedException('Middleware loaded!');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
class MiddlewareLoadedException extends \Exception {}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
|
||||
class GroupTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
protected $result;
|
||||
|
||||
public function __construct() {
|
||||
// Initial setup
|
||||
$_SERVER['HTTP_HOST'] = 'example.com';
|
||||
$_SERVER['REQUEST_URI'] = '/api/v1/test';
|
||||
$_SERVER['REQUEST_METHOD'] = 'get';
|
||||
}
|
||||
|
||||
protected function group() {
|
||||
$this->result = true;
|
||||
}
|
||||
|
||||
public function testGroup() {
|
||||
\Pecee\SimpleRouter\RouterBase::reset();
|
||||
|
||||
$this->result = false;
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/group'], $this->group());
|
||||
|
||||
try {
|
||||
\Pecee\SimpleRouter\SimpleRouter::start();
|
||||
} catch(Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
$this->assertTrue($this->result);
|
||||
}
|
||||
|
||||
public function testNestedGroup() {
|
||||
\Pecee\SimpleRouter\RouterBase::reset();
|
||||
|
||||
\Pecee\Http\Request::getInstance()->setUri('/api/v1/test');
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() {
|
||||
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() {
|
||||
\Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start');
|
||||
});
|
||||
});
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
|
||||
class MiddlewareTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
public function __construct() {
|
||||
// Initial setup
|
||||
$_SERVER['HTTP_HOST'] = 'example.com';
|
||||
$_SERVER['REQUEST_URI'] = '/my/test/url';
|
||||
$_SERVER['REQUEST_METHOD'] = 'get';
|
||||
}
|
||||
|
||||
public function testMiddlewareFound() {
|
||||
|
||||
\Pecee\Http\Request::getInstance()->setMethod('get');
|
||||
|
||||
\Pecee\SimpleRouter\RouterBase::reset();
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
|
||||
|
||||
try {
|
||||
\Pecee\SimpleRouter\SimpleRouter::start();
|
||||
}catch(Exception $e) {
|
||||
$this->assertTrue(($e instanceof MiddlewareLoadedException));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception('Middleware not loaded');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
|
||||
class RouterRouteTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
public function __construct() {
|
||||
// Initial setup
|
||||
$_SERVER['HTTP_HOST'] = 'example.com';
|
||||
$_SERVER['REQUEST_URI'] = '/my/test/url';
|
||||
$_SERVER['REQUEST_METHOD'] = 'get';
|
||||
}
|
||||
|
||||
public function testGet() {
|
||||
|
||||
\Pecee\SimpleRouter\RouterBase::reset();
|
||||
|
||||
\Pecee\Http\Request::getInstance()->setMethod('get');
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start');
|
||||
\Pecee\SimpleRouter\SimpleRouter::start();
|
||||
}
|
||||
|
||||
public function testPost() {
|
||||
\Pecee\Http\Request::getInstance()->setMethod('post');
|
||||
|
||||
\Pecee\SimpleRouter\RouterBase::reset();
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::post('/my/test/url', 'DummyController@start');
|
||||
\Pecee\SimpleRouter\SimpleRouter::start();
|
||||
}
|
||||
|
||||
public function testPut() {
|
||||
\Pecee\Http\Request::getInstance()->setMethod('put');
|
||||
|
||||
\Pecee\SimpleRouter\RouterBase::reset();
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::put('/my/test/url', 'DummyController@start');
|
||||
\Pecee\SimpleRouter\SimpleRouter::start();
|
||||
}
|
||||
|
||||
public function testDelete() {
|
||||
\Pecee\Http\Request::getInstance()->setMethod('delete');
|
||||
|
||||
\Pecee\SimpleRouter\RouterBase::reset();
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::delete('/my/test/url', 'DummyController@start');
|
||||
\Pecee\SimpleRouter\SimpleRouter::start();
|
||||
|
||||
}
|
||||
|
||||
public function testMethodNotAllowed() {
|
||||
|
||||
\Pecee\SimpleRouter\RouterBase::reset();
|
||||
|
||||
\Pecee\Http\Request::getInstance()->setMethod('post');
|
||||
|
||||
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start');
|
||||
|
||||
try {
|
||||
\Pecee\SimpleRouter\SimpleRouter::start();
|
||||
} catch(\Exception $e) {
|
||||
$this->assertEquals(403, $e->getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user