mirror of
https://github.com/skipperbent/simple-php-router.git
synced 2026-06-17 08:47:52 +00:00
@@ -17,12 +17,22 @@ Add the latest version pf Simple PHP Router to your ```composer.json```
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Basic routing (get, post, put, delete) with support for custom multiple verbs.
|
||||||
|
- Regular Expression Constraints for parameters.
|
||||||
|
- Named routes.
|
||||||
|
- Generating url to routes.
|
||||||
|
- Route groups.
|
||||||
|
- Middleware (classes that intercepts before the route is rendered).
|
||||||
|
- Namespaces.
|
||||||
|
- Route prefixes.
|
||||||
|
- CSRF protection.
|
||||||
|
|
||||||
### Features currently "in-the-works"
|
### Features currently "in-the-works"
|
||||||
|
|
||||||
- Global Constraints
|
- Global Constraints
|
||||||
- Named Routes
|
|
||||||
- Sub-Domain Routing
|
- Sub-Domain Routing
|
||||||
- CSRF Protection
|
|
||||||
- Optional/required parameters
|
- Optional/required parameters
|
||||||
|
|
||||||
## Initialising the router
|
## Initialising the router
|
||||||
@@ -138,8 +148,11 @@ class Router extends SimpleRouter {
|
|||||||
// Init locale settings
|
// Init locale settings
|
||||||
Locale::getInstance();
|
Locale::getInstance();
|
||||||
|
|
||||||
// Set default namespace
|
// Set default namespace for routes
|
||||||
$defaultNamespace = '\\'.Registry::getInstance()->get('AppName') . '\\Controller';
|
$defaultNamespace = '\\'.Registry::getInstance()->get('AppName') . '\\Controller';
|
||||||
|
|
||||||
|
// Add custom csrf verifier (must extend BaseCsrfVerifier)
|
||||||
|
parent::csrfVerifier('MyProject\Middleware\CustomCsrfVerifier');
|
||||||
|
|
||||||
// Handle exceptions
|
// Handle exceptions
|
||||||
try {
|
try {
|
||||||
@@ -171,13 +184,28 @@ function url($controller, $parameters = null, $getParams = null) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This is a basic example for getting the current csrf token
|
||||||
|
|
||||||
|
```php
|
||||||
|
/**
|
||||||
|
* Get current csrf-token
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
function csrf_token() {
|
||||||
|
$token = new \Pecee\CsrfToken();
|
||||||
|
return $token->getToken();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example for getting the url
|
||||||
|
|
||||||
In ```routes.php``` we have added this route:
|
In ```routes.php``` we have added this route:
|
||||||
|
|
||||||
```SimpleRouter::get('/item/{id}', 'myController@show');```
|
```SimpleRouter::get('/item/{id}', 'myController@show', ['as' => 'item']);```
|
||||||
|
|
||||||
In the template we then call:
|
In the template we then call:
|
||||||
|
|
||||||
```url('myController@show', ['id' => 22], ['category' => 'shoes']);```
|
```url('item', ['id' => 22], ['category' => 'shoes']);```
|
||||||
|
|
||||||
Result url is:
|
Result url is:
|
||||||
|
|
||||||
|
|||||||
+24
-38
@@ -3,37 +3,21 @@ namespace Pecee;
|
|||||||
|
|
||||||
class CsrfToken {
|
class CsrfToken {
|
||||||
|
|
||||||
const CSRF_KEY = 'csrf_token';
|
const CSRF_KEY = 'XSRF-TOKEN';
|
||||||
|
|
||||||
protected static $instance;
|
protected $token;
|
||||||
|
|
||||||
protected $lastToken;
|
|
||||||
protected $currentToken;
|
|
||||||
|
|
||||||
public static function getInstance() {
|
|
||||||
if(self::$instance === null) {
|
|
||||||
self::$instance = new static();
|
|
||||||
}
|
|
||||||
return self::$instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->lastToken = isset($_SESSION[self::CSRF_KEY]) ? $_SESSION[self::CSRF_KEY] : null;
|
if($this->getToken() === null) {
|
||||||
$this->currentToken = $this->generate();
|
$this->setToken($this->generateToken());
|
||||||
|
|
||||||
// Initialise session, if it hasn't been initialised.
|
|
||||||
if(!isset($_SESSION)) {
|
|
||||||
session_start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$_SESSION['csrf_token'] = $this->currentToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate random identifier for CSRF token
|
* Generate random identifier for CSRF token
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function generate() {
|
public static function generateToken() {
|
||||||
if (function_exists('mcrypt_create_iv')) {
|
if (function_exists('mcrypt_create_iv')) {
|
||||||
return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
|
return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
|
||||||
}
|
}
|
||||||
@@ -47,28 +31,30 @@ class CsrfToken {
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function validate($token) {
|
public function validate($token) {
|
||||||
return hash_equals($token, $_SESSION[self::CSRF_KEY]);
|
if($token !== null && $this->getToken() !== null) {
|
||||||
|
return hash_equals($token, $this->getToken());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Set csrf token cookie
|
||||||
|
*
|
||||||
|
* @param $token
|
||||||
|
*/
|
||||||
|
public function setToken($token) {
|
||||||
|
setcookie(self::CSRF_KEY, $token, time() + 60 * 120, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get csrf token
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function getLastToken(){
|
public function getToken(){
|
||||||
return $this->lastToken;
|
if(isset($_COOKIE[self::CSRF_KEY])) {
|
||||||
}
|
return $_COOKIE[self::CSRF_KEY];
|
||||||
|
}
|
||||||
/**
|
return null;
|
||||||
* @param string|null $lastToken
|
|
||||||
*/
|
|
||||||
public function setLastToken($lastToken){
|
|
||||||
$this->lastToken = $lastToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getCurrentToken(){
|
|
||||||
return $this->currentToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pecee\Http\Middleware;
|
||||||
|
|
||||||
|
use Pecee\CsrfToken;
|
||||||
|
use Pecee\Http\Request;
|
||||||
|
use Pecee\SimpleRouter\RouterException;
|
||||||
|
|
||||||
|
class BaseCsrfVerifier extends Middleware {
|
||||||
|
|
||||||
|
const POST_KEY = 'csrf-token';
|
||||||
|
const HEADER_KEY = 'X-CSRF-TOKEN';
|
||||||
|
|
||||||
|
public function handle(Request $request) {
|
||||||
|
|
||||||
|
if($request->getMethod() != 'get') {
|
||||||
|
|
||||||
|
$token = (isset($_POST[self::POST_KEY])) ? $_POST[self::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);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenValidator = new CsrfToken();
|
||||||
|
if( !$tokenValidator->validate( $token ) ) {
|
||||||
|
throw new RouterException('Invalid csrf-token.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,5 @@ use Pecee\SimpleRouter\RouterEntry;
|
|||||||
|
|
||||||
abstract class Middleware
|
abstract class Middleware
|
||||||
{
|
{
|
||||||
public function handle(Request $request) {
|
abstract function handle(Request $request);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Pecee\Http\Middleware;
|
|
||||||
|
|
||||||
class VerifyCsrfToken extends Middleware {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,13 @@ class Request {
|
|||||||
protected $uri;
|
protected $uri;
|
||||||
protected $host;
|
protected $host;
|
||||||
protected $method;
|
protected $method;
|
||||||
|
protected $headers;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->host = $_SERVER['HTTP_HOST'];
|
$this->host = $_SERVER['HTTP_HOST'];
|
||||||
$this->uri = rtrim($_SERVER['REQUEST_URI'], '/') . '/';
|
$this->uri = rtrim($_SERVER['REQUEST_URI'], '/') . '/';
|
||||||
$this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : strtolower($_SERVER['REQUEST_METHOD']);
|
$this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : strtolower($_SERVER['REQUEST_METHOD']);
|
||||||
|
$this->headers = getallheaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,4 +52,21 @@ class Request {
|
|||||||
return (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW']: null;
|
return (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW']: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get headers
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getHeaders() {
|
||||||
|
return $this->headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get header value by name
|
||||||
|
* @param string $name
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getHeader($name) {
|
||||||
|
return (isset($this->headers[$name])) ? $this->headers[$name] : null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
namespace Pecee\SimpleRouter;
|
namespace Pecee\SimpleRouter;
|
||||||
|
|
||||||
use Pecee\ArrayUtil;
|
use Pecee\ArrayUtil;
|
||||||
|
use Pecee\Http\Middleware\BaseCsrfVerifier;
|
||||||
use Pecee\Http\Request;
|
use Pecee\Http\Request;
|
||||||
use Pecee\Url;
|
use Pecee\Url;
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ class RouterBase {
|
|||||||
protected $backstack;
|
protected $backstack;
|
||||||
protected $loadedRoute;
|
protected $loadedRoute;
|
||||||
protected $defaultNamespace;
|
protected $defaultNamespace;
|
||||||
|
protected $baseCsrfVerifier;
|
||||||
|
|
||||||
// TODO: make interface for controller routers, so they can be easily detected
|
// TODO: make interface for controller routers, so they can be easily detected
|
||||||
// TODO: clean up - cut some of the methods down to smaller pieces
|
// TODO: clean up - cut some of the methods down to smaller pieces
|
||||||
@@ -26,6 +28,7 @@ class RouterBase {
|
|||||||
$this->backstack = array();
|
$this->backstack = array();
|
||||||
$this->controllerUrlMap = array();
|
$this->controllerUrlMap = array();
|
||||||
$this->request = new Request();
|
$this->request = new Request();
|
||||||
|
$this->baseCsrfVerifier = new BaseCsrfVerifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addRoute(RouterEntry $route) {
|
public function addRoute(RouterEntry $route) {
|
||||||
@@ -85,8 +88,16 @@ class RouterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function routeRequest() {
|
public function routeRequest() {
|
||||||
// Loop through each route-request
|
|
||||||
|
|
||||||
|
// Verify csrf token for request
|
||||||
|
if($this->baseCsrfVerifier !== null) {
|
||||||
|
/* @var $csrfVerifier BaseCsrfVerifier */
|
||||||
|
$csrfVerifier = $this->baseCsrfVerifier;
|
||||||
|
$csrfVerifier = new $csrfVerifier();
|
||||||
|
$csrfVerifier->handle($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through each route-request
|
||||||
$this->processRoutes($this->routes);
|
$this->processRoutes($this->routes);
|
||||||
|
|
||||||
// Make sure the urls is in the right order when comparing
|
// Make sure the urls is in the right order when comparing
|
||||||
@@ -100,7 +111,6 @@ class RouterBase {
|
|||||||
foreach($this->controllerUrlMap as $route) {
|
foreach($this->controllerUrlMap as $route) {
|
||||||
$routeMatch = $route->matchRoute($this->request);
|
$routeMatch = $route->matchRoute($this->request);
|
||||||
|
|
||||||
|
|
||||||
if($routeMatch && !($routeMatch instanceof RouterGroup)) {
|
if($routeMatch && !($routeMatch instanceof RouterGroup)) {
|
||||||
|
|
||||||
if(count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
|
if(count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
|
||||||
@@ -179,6 +189,25 @@ class RouterBase {
|
|||||||
return $this->request;
|
return $this->request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get base csrf verifier class
|
||||||
|
* @return BaseCsrfVerifier
|
||||||
|
*/
|
||||||
|
public function getBaseCsrfVerifier() {
|
||||||
|
return $this->baseCsrfVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set base csrf verifier class
|
||||||
|
*
|
||||||
|
* @param BaseCsrfVerifier $baseCsrfVerifier
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setBaseCsrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) {
|
||||||
|
$this->baseCsrfVerifier = $baseCsrfVerifier;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function processUrl($route, $method = null, $parameters = null, $getParams = null) {
|
protected function processUrl($route, $method = null, $parameters = null, $getParams = null) {
|
||||||
|
|
||||||
$url = $route->getUrl();
|
$url = $route->getUrl();
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ abstract class RouterEntry {
|
|||||||
$className = $this->getNamespace() . '\\' . $controller[0];
|
$className = $this->getNamespace() . '\\' . $controller[0];
|
||||||
|
|
||||||
$class = $this->loadClass($className);
|
$class = $this->loadClass($className);
|
||||||
$method = $request->getMethod() . ucfirst($controller[1]);
|
$method = $controller[1];
|
||||||
|
|
||||||
if (!method_exists($class, $method)) {
|
if (!method_exists($class, $method)) {
|
||||||
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
|
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
|
||||||
|
|||||||
@@ -9,14 +9,29 @@
|
|||||||
|
|
||||||
namespace Pecee\SimpleRouter;
|
namespace Pecee\SimpleRouter;
|
||||||
|
|
||||||
|
use Pecee\Http\Middleware\BaseCsrfVerifier;
|
||||||
|
|
||||||
class SimpleRouter {
|
class SimpleRouter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start/route request
|
||||||
|
* @param null $defaultNamespace
|
||||||
|
* @throws RouterException
|
||||||
|
*/
|
||||||
public static function start($defaultNamespace = null) {
|
public static function start($defaultNamespace = null) {
|
||||||
$router = RouterBase::GetInstance();
|
$router = RouterBase::GetInstance();
|
||||||
$router->setDefaultNamespace($defaultNamespace);
|
$router->setDefaultNamespace($defaultNamespace);
|
||||||
$router->routeRequest();
|
$router->routeRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set base csrf verifier
|
||||||
|
* @param BaseCsrfVerifier $baseCsrfVerifier
|
||||||
|
*/
|
||||||
|
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) {
|
||||||
|
RouterBase::getInstance()->setBaseCsrfVerifier($baseCsrfVerifier);
|
||||||
|
}
|
||||||
|
|
||||||
public static function get($url, $callback, array $settings = null) {
|
public static function get($url, $callback, array $settings = null) {
|
||||||
$route = new RouterRoute($url, $callback);
|
$route = new RouterRoute($url, $callback);
|
||||||
$route->addSettings($settings);
|
$route->addSettings($settings);
|
||||||
|
|||||||
Reference in New Issue
Block a user