Compare commits

..

16 Commits

Author SHA1 Message Date
Simon Sessingø 4f47463497 Merge pull request #131 from skipperbent/v2-development
V2 development
2016-11-15 08:13:09 +02:00
Simon Sessingø 9c413a3c53 Bugfixes 2016-11-15 06:38:39 +01:00
Simon Sessingø 669d318a12 Documentation updates 2016-11-15 05:57:58 +01:00
Simon Sessingø 9513e38009 Fixed to unit tests 2016-11-15 05:57:04 +01:00
Simon Sessingø a13bcd4768 Updated demo project + documentation 2016-11-15 05:48:08 +01:00
Simon Sessingø 83c73a4240 Callback, faking route and documentation updates 2016-11-15 05:44:24 +01:00
Simon Sessingø bc14790a67 Updated documentation 2016-11-15 03:55:28 +01:00
Simon Sessingø d5e7a13d89 Development + bugfixes 2016-11-15 03:43:26 +01:00
Simon Sessingø e5c86c1822 Merge pull request #129 from skipperbent/v2-development
Bugfixes
2016-11-15 01:12:41 +02:00
Simon Sessingø 6de0700e17 Bugfixes
- Removed debugging constructor.
- Updated documentation.
2016-11-15 00:11:57 +01:00
Simon Sessingø 32c305bd2c Merge pull request #124 from skipperbent/v2-development
V2 development
2016-11-08 18:22:49 +02:00
Simon Sessingø 28ffa30d3e Development
- all() in Input class now returns correct array.
- all() now supports json data.
- Minor bugfixes.
2016-11-08 18:21:21 +02:00
Simon Sessingø 540ebb31ac Updated documentation 2016-11-07 05:48:02 +01:00
Simon Sessingø 48317ded7a Updated documentation 2016-11-07 05:40:32 +01:00
Simon Sessingø 28c3370b67 Fixed group not adding multiple middlewares 2016-11-07 04:55:43 +01:00
Simon Sessingø 7ee42c98a7 Updated documentaiton 2016-11-07 04:45:18 +01:00
27 changed files with 694 additions and 512 deletions
+116 -54
View File
@@ -18,7 +18,7 @@ The goal of this project is to create a router that is 100% compatible with the
### Features
- Basic routing (get, post, put, delete) with support for custom multiple verbs.
- Basic routing (`GET`, `POST`, `PUT`, `DELETE`) with support for custom multiple verbs.
- Regular Expression Constraints for parameters.
- Named routes.
- Generating url to routes.
@@ -49,13 +49,18 @@ This is an example of a basic ```index.php``` file:
```php
use \Pecee\SimpleRouter\SimpleRouter;
require_once 'routes.php'; // change this to whatever makes sense in your project
// Load external routes file
require_once 'routes.php';
// The apps default namespace (so we don't have to specify it each time we use MyController@home)
$defaultControllerNamespace = 'MyWebsite\\Controller';
/*
* The default namespace for route-callbacks, so we don't have to specify it each time.
* Can be overwritten by using the namespace config option.
*/
SimpleRouter::setDefaultNamespace('MyWebsite\Controller');
// Do the routing
SimpleRouter::start($defaultControllerNamespace);
// Start the routing
SimpleRouter::start();
```
## Adding routes
@@ -81,7 +86,9 @@ use Pecee\SimpleRouter\SimpleRouter;
// Add CSRF support (if needed)
SimpleRouter::csrfVerifier(new \Pecee\Http\Middleware\BaseCsrfVerifier());
SimpleRouter::group(['prefix' => 'v1', 'middleware' => '\MyWebsite\Middleware\SomeMiddlewareClass'], function() {
SimpleRouter::get('/page/404', 'ControllerPage@notFound', ['as' => 'page.notfound']);
SimpleRouter::group(['prefix' => '/v1', 'middleware' => '\MyWebsite\Middleware\SomeMiddlewareClass'], function() {
SimpleRouter::group(['prefix' => '/services', 'exceptionHandler' => '\MyProject\Handler\CustomExceptionHandler'], function() {
@@ -127,7 +134,12 @@ class CustomExceptionHandler implements IExceptionHandler {
// If the error-code is 404; show another route which contains the page-not-found
if($error->getCode() === 404) {
// Load your custom 404-page view
// Throw your custom 404-page view
// - or -
// load another route with our 404 page
return $request->setUri(url('page.notfound'));
}
// Output error as json if on api path.
@@ -190,22 +202,25 @@ use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter {
public static function start($defaultNamespace = null) {
public static function start() {
// change this to whatever makes sense in your project
require_once 'routes.php';
// change default namespace for all routes
parent::setDefaultNamespace('\Demo\Controllers');
// Do initial stuff
parent::start('\\Demo\\Controllers');
parent::start();
}
}
```
#### Helper functions examples
**This is a basic example of a helper function for generating urls.**
## Helper functions
To simplify to use of simple-router functionality, we recommend you add these helper functions to your project.
```php
use Pecee\SimpleRouter\SimpleRouter;
@@ -238,6 +253,14 @@ function request() {
function response() {
return SimpleRouter::response();
}
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input() {
return SimpleRouter::request()->getInput();
}
```
## Getting urls
@@ -273,7 +296,10 @@ use Pecee\Http\Middleware\BaseCsrfVerifier;
class CsrfVerifier extends BaseCsrfVerifier {
protected $except = ['/companies/*', '/user/save'];
protected $except = [
'/companies/*',
'/api'
];
}
```
@@ -327,64 +353,113 @@ By doing this the route will now load the url ```/article/view/1``` instead of `
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
the ```\Pecee\SimpleRouter\Http\Request``` object. All information about the current route is as a ```\Pecee\SimpleRouter\Http\Request``` object which can always be obtained on
the `RouterBase` instance. For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::request()`.
Sometimes it can be useful to manipulate the route about to be loaded.
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\RouterBase``` instance's `loadedRoute` property.
For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::router()`.
**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.
```php
use Pecee\SimpleRouter;
$route = SimpleRouter::request()->getLoadedRoute();
$route = SimpleRouter::router()->getLoadedRoute();
$route->setCallback('Example\MyCustomClass@hello');
// -- or --
// -- or you can rewrite by doing --
$route->setClass('Example\MyCustomClass');
$route->setMethod('hello');
```
### Examples
It's only possible to change the route BEFORE the route has initially been loaded. If you want to redirect to another route, we highly recommend that you
modify the `RouterEntry` object from a `Middleware` or `ExceptionHandler`, like the examples below.
#### Faking new route
The example below will cause the router to re-route the request with another url. We are using the `url()` helper function to get the uri to another route added in the `routes.php` file.
This does require the `$request` object to be returned, otherwise the `request` object will be ignored by the router.
Using the example below will NOT inherit the rules from the other route. This means that IF you are faking a route that is enabled in `post`.
**NOTE: Use this method if you want to fully load a route (middlewares, request-method etc. will be kept).**
```php
namespace demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class CustomMiddleware implements Middleware {
public function handle(Request $request, RouterEntry &$route = null) {
return $request->setUri(url('home'));
}
}
```
#### Changing callback
You can also change the callback by modifying the `$route` parameter. This is perfect if you just want to display a view quickly - or change the callback depending
on some criteria's for the request.
The callback below will fire immediately after the `Middleware` or `ExceptionHandler` has been loaded, as they are loaded before the route is rendered.
If you wish to change the callback from outside, please have this in mind.
**NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.**
```php
namespace demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class CustomMiddleware implements Middleware {
public function handle(Request $request, RouterEntry &$route = null) {
$route->callback('DefaultController@home');
}
}
```
## Using the Input class to manage parameters
We've added the `Input` class to easy access parameters from your Controller-classes.
**Return single parameter value (matches both GET, POST, FILE):**
```php
$value = SimpleRouter::request()->getInput()->get('name');
$value = input()->get('name');
```
**Return parameter object (matches both GET, POST, FILE):**
```php
$object = SimpleRouter::request()->getInput()->getObject('name');
$object = input()->getObject('name');
```
**Return specific GET parameter (where name is the name of your parameter):**
```php
$object = SimpleRouter::request()->getInput()->get->name;
$object = SimpleRouter::request()->getInput()->post->name;
$object = SimpleRouter::request()->getInput()->file->name;
$object = input()->get->name;
$object = input()->post->name;
$object = input()->file->name;
```
**Return all parameters:**
```php
// Get all
$objects = SimpleRouter::request()->getInput()->all();
$values = input()->all();
// Only match certain keys
$objects = SimpleRouter::request()->getInput()->all([
$values = input()->all([
'company_name',
'user_id'
]);
@@ -404,26 +479,13 @@ All object inherits from `InputItem` class and will always contain these methods
- `getError()` - get file upload error.
### Easy access your input
Create a helper function to easily get access to the input elements.
### Easy access to methods
Example:
```php
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input() {
return SimpleRouter::request()->getInput();
}
```
Then you can easily do something like this in your controller:
Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation.
```php
// Get parameter site_id or default-value 2
$value = input()->get('site_id', '2');
$siteId = input()->get('site_id', 2);
```
## Sites
@@ -9,12 +9,10 @@ class ApiController {
// The variable authenticated is set to true in the ApiVerification middleware class.
$request = SimpleRouter::request();
header('content-type: application/json');
echo json_encode([
'authenticated' => $request->authenticated
'authenticated' => request()->authenticated
]);
}
@@ -6,7 +6,7 @@ class DefaultController {
public function index() {
// implement
echo 'DefaultController -> index';
echo sprintf('DefaultController -> index (?fun=%s)', input()->get('fun'));
}
@@ -7,7 +7,7 @@ use Pecee\SimpleRouter\RouterEntry;
class CustomExceptionHandler implements IExceptionHandler {
public function handleError( Request $request, RouterEntry $router = null, \Exception $error) {
public function handleError( Request $request, RouterEntry &$route = null, \Exception $error) {
// Return json errors if we encounter an error on /api.
if(stripos($request->getUri(), '/api') !== false) {
@@ -3,10 +3,11 @@ namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class ApiVerification implements IMiddleware {
public function handle(Request $request) {
public function handle(Request $request, RouterEntry &$route = null) {
// Do authentication
$request->authenticated = true;
+3 -1
View File
@@ -19,8 +19,10 @@ class Router extends SimpleRouter {
// Load our custom routes
require_once 'routes.php';
parent::setDefaultNamespace('\Demo\Controllers');
// Do initial stuff
parent::start('\\Demo\\Controllers');
parent::start();
}
+9 -1
View File
@@ -1,5 +1,5 @@
<?php
use \Pecee\SimpleRouter\SimpleRouter;
use Pecee\SimpleRouter\SimpleRouter;
function url($controller, $parameters = null, $getParams = null) {
SimpleRouter::getRoute($controller, $parameters, $getParams);
@@ -28,4 +28,12 @@ function request() {
*/
function response() {
return SimpleRouter::response();
}
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input() {
return SimpleRouter::request()->getInput();
}
+1
View File
@@ -1,3 +1,4 @@
<?php
namespace Pecee\Exception;
class RouterException extends \Exception { }
+7 -1
View File
@@ -6,6 +6,12 @@ use Pecee\SimpleRouter\RouterEntry;
interface IExceptionHandler {
public function handleError(Request $request, RouterEntry $router = null, \Exception $error);
/**
* @param Request $request
* @param RouterEntry|null $route
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, RouterEntry &$route = null, \Exception $error);
}
+33 -21
View File
@@ -25,7 +25,7 @@ class Input {
*/
protected $request;
public function __construct(Request &$request) {
public function __construct(Request $request) {
$this->request = $request;
$this->setGet();
$this->setPost();
@@ -38,17 +38,31 @@ class Input {
* @return array
*/
public function all(array $filter = null) {
$output = $this->get->getData();
$output = array_merge($output, $this->post->getData());
if($filter !== null) {
$tmp = array();
foreach($output as $key => $val) {
if(in_array($key, $filter)) {
$tmp[$key] = $val;
$output = $_POST;
if($this->request->getMethod() === 'post') {
$contents = file_get_contents('php://input');
if (stripos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if($output === false) {
$output = array();
}
}
return $tmp;
}
$output = array_merge($_GET, $output);
if($filter !== null) {
$output = array_filter($output, function ($key) use ($filter) {
if (in_array($key, $filter)) {
return true;
}
return false;
}, ARRAY_FILTER_USE_KEY);
}
return $output;
@@ -93,7 +107,7 @@ class Input {
if($item !== null) {
if(is_array($item) || $item instanceof InputFile) {
if($item instanceof InputCollection || $item instanceof InputFile) {
return $item;
}
@@ -117,10 +131,10 @@ class Input {
continue;
}
$output = array();
$output = new InputCollection();
foreach($get as $k => $g) {
$output[$k] = new InputItem($k, $g);
$output->{$k} = new InputItem($k, $g);
}
$this->get->{$key} = $output;
@@ -131,12 +145,10 @@ class Input {
public function setPost() {
$this->post = new InputCollection();
$postVars = array();
$postVars = $_POST;
if(isset($_SERVER['REQUEST_METHOD']) && in_array($_SERVER['REQUEST_METHOD'], ['PUT', 'PATCH', 'DELETE'])) {
if(in_array($this->request->getMethod(), ['put', 'patch', 'delete'])) {
parse_str(file_get_contents('php://input'), $postVars);
} else {
$postVars = $_POST;
}
if(count($postVars)) {
@@ -147,10 +159,10 @@ class Input {
continue;
}
$output = array();
$output = new InputCollection();
foreach($post as $k=>$p) {
$output[$k] = new InputItem($k, $p);
foreach($post as $k => $p) {
$output->{$k} = new InputItem($k, $p);
}
$this->post->{strtolower($key)} = $output;
@@ -178,7 +190,7 @@ class Input {
continue;
}
$output = array();
$output = new InputCollection();
foreach($value['name'] as $k=>$val) {
// Strip empty values
@@ -189,7 +201,7 @@ class Input {
$file->setType($value['type'][$k]);
$file->setTmpName($value['tmp_name'][$k]);
$file->setError($value['error'][$k]);
$output[$k] = $file;
$output->{$k} = $file;
}
}
+3 -2
View File
@@ -10,9 +10,10 @@ class InputCollection implements \IteratorAggregate {
* Useful for searching for finding items where $index doesn't contain form name.
*
* @param string $index
* @param string|null $defaultValue
* @return mixed
*/
public function findFirst($index) {
public function findFirst($index, $defaultValue = null) {
if(count($this->data)) {
if(isset($this->data[$index])) {
@@ -26,7 +27,7 @@ class InputCollection implements \IteratorAggregate {
}
}
return null;
return $defaultValue;
}
/**
@@ -4,6 +4,7 @@ namespace Pecee\Http\Middleware;
use Pecee\CsrfToken;
use Pecee\Exception\TokenMismatchException;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class BaseCsrfVerifier implements IMiddleware {
@@ -49,11 +50,11 @@ class BaseCsrfVerifier implements IMiddleware {
return false;
}
public function handle(Request $request) {
public function handle(Request $request, RouterEntry &$route = null) {
if($request->getMethod() != 'get' && !$this->skip($request)) {
if($request->getMethod() !== 'get' && !$this->skip($request)) {
$token = (isset($_POST[static::POST_KEY])) ? $_POST[static::POST_KEY] : null;
$token = $request->getInput()->post->findFirst(static::POST_KEY);
// If the token is not posted, check headers for valid x-csrf-token
if($token === null) {
+9 -1
View File
@@ -2,7 +2,15 @@
namespace Pecee\Http\Middleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
interface IMiddleware {
public function handle(Request $request);
/**
* @param Request $request
* @param RouterEntry|null $route
* @return Request|null
*/
public function handle(Request $request, RouterEntry &$route = null);
}
+72 -59
View File
@@ -5,32 +5,41 @@ use Pecee\Http\Input\Input;
class Request {
protected $data;
protected $data = array();
protected $headers;
protected $host;
protected $uri;
protected $method;
protected $input;
public function __construct() {
$this->data = array();
$this->host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : array();
$this->uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : array();
$this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : (isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : array());
$this->headers = $this->getAllHeaders();
$this->parseHeaders();
$this->input = new Input($this);
$this->host = $this->getHeader('http_host');;
$this->uri = $this->getHeader('request_uri');
$this->method = strtolower($this->input->post->findFirst('_method', $this->getHeader('request_method')));
}
protected function getAllHeaders() {
$headers = array();
protected function parseHeaders() {
$this->headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) === 'HTTP_') {
$headers[strtolower(str_replace('_', '-', substr($name, 5)))] = $value;
}
$this->headers[strtolower($name)] = $value;
}
return $headers;
}
public function getIsSecure() {
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
public function isSecure() {
if($this->getHeader('http_x_forwarded_proto') === 'https') {
return true;
}
return isset($_SERVER['HTTPS']) ? true : (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] === 443);
if($this->getHeader('https') !== null) {
return true;
}
return ($this->getHeader('server_port') === 443);
}
/**
@@ -59,7 +68,7 @@ class Request {
* @return string|null
*/
public function getUser() {
return (isset($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER']: null;
return $this->getHeader('php_auth_user');
}
/**
@@ -67,11 +76,11 @@ class Request {
* @return string|null
*/
public function getPassword() {
return (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW']: null;
return $this->getHeader('php_auth_pw');
}
/**
* Get headers
* Get all headers
* @return array
*/
public function getHeaders() {
@@ -83,10 +92,15 @@ class Request {
* @return string
*/
public function getIp() {
if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
if($this->getHeader('http_cf_connecting_ip') !== null) {
return $this->getHeader('http_cf_connecting_ip');
}
return ((isset($_SERVER['HTTP_X_FORWARDED_FOR']) && strlen($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null);
if($this->getHeader('http_x_forwarded_for') !== null && strlen($this->getHeader('http_x_forwarded_for'))) {
return $this->getHeader('http_x_forwarded_for');
}
return $this->getHeader('remote_addr');
}
/**
@@ -94,7 +108,7 @@ class Request {
* @return string
*/
public function getReferer() {
return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
return $this->getHeader('http_referer');
}
/**
@@ -102,16 +116,17 @@ class Request {
* @return string
*/
public function getUserAgent() {
return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
return $this->getHeader('http_user_agent');
}
/**
* Get header value by name
* @param string $name
* @param object|null $defaultValue
* @return string|null
*/
public function getHeader($name) {
return (isset($this->headers[strtolower($name)])) ? $this->headers[strtolower($name)] : null;
public function getHeader($name, $defaultValue = null) {
return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue;
}
/**
@@ -122,15 +137,42 @@ class Request {
return $this->input;
}
/**
* Is format accepted
* @param string $format
* @return bool
*/
public function isFormatAccepted($format) {
return (isset($_SERVER['HTTP_ACCEPT']) && stripos($_SERVER['HTTP_ACCEPT'], $format) > -1);
return ($this->getHeader('http_accept') !== null && stripos($this->getHeader('http_accept'), $format) > -1);
}
/**
* Get accept formats
* @return array
*/
public function getAcceptFormats() {
if(isset($_SERVER['HTTP_ACCEPT'])) {
return explode(',', $_SERVER['HTTP_ACCEPT']);
}
return array();
return explode(',', $this->getHeader('http_accept'));
}
/**
* @param string $uri
*/
public function setUri($uri) {
$this->uri = $uri;
}
/**
* @param string $host
*/
public function setHost($host) {
$this->host = $host;
}
/**
* @param string $method
*/
public function setMethod($method) {
$this->method = $method;
}
public function __set($name, $value = null) {
@@ -141,33 +183,4 @@ class Request {
return isset($this->data[$name]) ? $this->data[$name] : null;
}
/**
* Get the currently loaded route.
* @return \Pecee\SimpleRouter\RouterEntry
*/
public function getLoadedRoute() {
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;
}
}
+5 -5
View File
@@ -6,7 +6,7 @@ class Response {
protected $request;
public function __construct(Request &$request) {
public function __construct(Request $request) {
$this->request = $request;
}
@@ -14,7 +14,7 @@ class Response {
* Set the http status code
*
* @param int $code
* @return self $this
* @return static
*/
public function httpCode($code) {
http_response_code($code);
@@ -43,7 +43,7 @@ class Response {
/**
* Add http authorisation
* @param string $name
* @return self $this
* @return static
*/
public function auth($name = '') {
$this->headers([
@@ -87,7 +87,7 @@ class Response {
/**
* Add header to response
* @param string $value
* @return self $this
* @return static
*/
public function header($value) {
header($value);
@@ -97,7 +97,7 @@ class Response {
/**
* Add multiple headers to response
* @param array $headers
* @return self $this
* @return static
*/
public function headers(array $headers) {
foreach($headers as $header) {
@@ -0,0 +1,11 @@
<?php
namespace Pecee\SimpleRouter;
interface IControllerRoute {
public function getController();
public function setController($controller);
public function getMethod();
public function setMethod($method);
}
@@ -0,0 +1,9 @@
<?php
namespace Pecee\SimpleRouter;
interface ILoadableRoute {
public function getUrl();
public function setUrl($url);
}
+174 -116
View File
@@ -11,18 +11,78 @@ class RouterBase {
protected static $instance;
/**
* Current request
* @var Request
*/
protected $request;
/**
* Response
* @var Response
*/
protected $response;
/**
* Used to keep track of whether to add routes to stack or not.
* @var RouterEntry
*/
protected $currentRoute;
/**
* All added routes
* @var array
*/
protected $routes;
protected $processedRoutes;
/**
* List of
* @var array
*/
protected $controllerUrlMap;
/**
* Backstack array used to keep track of sub-routes
* @var array
*/
protected $backStack;
/**
* The default namespace that all routes will inherit
* @var string
*/
protected $defaultNamespace;
/**
* List of added bootmanagers
* @var array
*/
protected $bootManagers;
protected $baseCsrfVerifier;
/**
* Csrf verifier class
* @var BaseCsrfVerifier
*/
protected $csrfVerifier;
/**
* Get exception handlers
* @var array
*/
protected $exceptionHandlers;
/**
* The current loaded route
* @var RouterRoute|null
*/
protected $loadedRoute;
/**
* List over route changes (to avoid looping)
* @var array
*/
protected $routeChanges;
public function __construct() {
$this->reset();
}
@@ -35,34 +95,43 @@ class RouterBase {
$this->controllerUrlMap = array();
$this->bootManagers = array();
$this->exceptionHandlers = array();
$this->routeChanges = array();
}
/**
* Add route
* @param RouterEntry $route
* @return RouterEntry
*/
public function addRoute(RouterEntry $route) {
if($this->currentRoute !== null) {
$this->backStack[] = $route;
} else {
$this->routes[] = $route;
}
return $route;
}
protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), $backStack = false, $group = null) {
protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), $backStack = false, RouterGroup $group = null) {
// Loop through each route-request
$routesCount = count($routes);
$mergedSettings = array();
/* @var $route RouterEntry */
for($i = 0; $i < $routesCount; $i++) {
for($i = 0; $i < count($routes); $i++) {
$route = $routes[$i];
$route->addSettings($settings);
if(count($settings)) {
$route->addSettings($settings);
}
if($backStack) {
if($backStack && $group !== null) {
$route->setGroup($group);
}
if($this->defaultNamespace && !$route->getNamespace()) {
if($route->getNamespace() === null && $this->defaultNamespace !== null) {
$namespace = $this->defaultNamespace;
if ($route->getNamespace()) {
$namespace .= '\\' . $route->getNamespace();
@@ -71,42 +140,38 @@ class RouterBase {
$route->setNamespace($namespace);
}
$newPrefixes = $prefixes;
if($route->getPrefix() && trim($route->getPrefix(), '/') !== '') {
array_push($newPrefixes, trim($route->getPrefix(), '/'));
if($group !== null && $group->getPrefix() !== null && trim($group->getPrefix(), '/') !== '') {
$prefixes[] = trim($group->getPrefix(), '/');
}
/* @var $group RouterGroup */
$group = null;
$this->currentRoute = $route;
if(!($route instanceof RouterGroup)) {
if(is_array($newPrefixes) && count($newPrefixes) && $backStack) {
$route->setUrl( '/' . join('/', $newPrefixes) . $route->getUrl() );
if($route instanceof ILoadableRoute) {
if(is_array($prefixes) && count($prefixes) && $backStack) {
$route->setUrl( '/' . join('/', $prefixes) . $route->getUrl() );
}
$this->controllerUrlMap[] = $route;
}
} else {
if(is_callable($route->getCallback())) {
$this->currentRoute = $route;
$route->renderRoute($this->request);
if($route instanceof RouterGroup && is_callable($route->getCallback())) {
if ($route->matchRoute($this->request)) {
$route->renderRoute($this->request);
/* @var $group RouterGroup */
$group = $route;
if($route->matchRoute($this->request)) {
$mergedSettings = array_merge($settings, $group->getMergeableSettings());
$group = $route;
// Add ExceptionHandler
if ($group->getExceptionHandler() !== null) {
$this->exceptionHandlers[] = $route;
}
$mergedSettings = array_merge($settings, $group->getMergeableSettings());
// Add ExceptionHandler
if ($group->getExceptionHandler() !== null) {
$this->exceptionHandlers[] = $route;
}
}
}
$this->currentRoute = null;
@@ -116,24 +181,26 @@ class RouterBase {
$this->backStack = array();
// Route any routes added to the backstack
$this->processRoutes($backStack, $mergedSettings, $newPrefixes, true, $group);
$this->processRoutes($backStack, $mergedSettings, $prefixes, true, $group);
}
}
}
public function routeRequest($original = true) {
$originalUri = $this->request->getUri();
public function routeRequest(Request $newRequest = null) {
$this->loadedRoute = null;
$routeNotAllowed = false;
// Create a fictive request - so it can be changed in the middleware or exceptionhandler later on...
$request = clone $this->request;
try {
// Initialize boot-managers
if(count($this->bootManagers)) {
/* @var $manager RouterBootManager */
foreach($this->bootManagers as $manager) {
$this->request = $manager->boot($this->request);
$request = $manager->boot($request);
if(!($this->request instanceof Request)) {
throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.');
@@ -141,41 +208,46 @@ class RouterBase {
}
}
// Loop through each route-request
$this->processRoutes($this->routes);
if($newRequest === null) {
if($original === true) {
// Verify csrf token for request
if ($this->baseCsrfVerifier !== null) {
$this->baseCsrfVerifier->handle($this->request);
// Loop through each route-request
$this->processRoutes($this->routes);
if($this->csrfVerifier !== null) {
// Verify csrf token for request
$this->csrfVerifier->handle($this->request);
}
}
$max = count($this->controllerUrlMap);
$request = ($newRequest !== null) ? $newRequest : $request;
/* @var $route RouterEntry */
for ($i = 0; $i < $max; $i++) {
for ($i = 0; $i < count($this->controllerUrlMap); $i++) {
$route = $this->controllerUrlMap[$i];
$routeMatch = $route->matchRoute($this->request);
if ($route->matchRoute($request)) {
if ($routeMatch) {
if (count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
if (count($route->getRequestMethods()) && !in_array($request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
$routeNotAllowed = false;
$this->request->rewrite_uri = $this->request->uri;
$this->request->setUri($originalUri);
$this->loadedRoute = $route;
$this->request->loadedRoute = $route;
$route->loadMiddleware($this->request);
$request = $this->loadedRoute->loadMiddleware($request, $this->loadedRoute);
$request = ($request === null) ? $this->request : $request;
$this->request->loadedRoute->renderRoute($this->request);
if($request !== null && $request->getUri() !== $this->request->getUri() && !in_array($request->getUri(), $this->routeChanges)) {
$this->routeChanges[] = $request->getUri();
$this->routeRequest($request);
return;
}
$this->loadedRoute->renderRoute($request);
break;
}
@@ -189,18 +261,17 @@ class RouterBase {
$this->handleException(new RouterException('Route or method not allowed', 403));
}
if(!$this->request->loadedRoute) {
$this->handleException(new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404));
if($this->loadedRoute === null) {
$this->handleException(new RouterException(sprintf('Route not found: %s', $request->getUri()), 404));
}
}
protected function handleException(\Exception $e) {
$request = null;
$request = clone $this->request;
/* @var $route RouterGroup */
foreach ($this->exceptionHandlers as $route) {
$route->loadMiddleware($this->request);
$handler = $route->getExceptionHandler();
$handler = new $handler();
@@ -208,19 +279,29 @@ class RouterBase {
throw new RouterException('Exception handler must implement the IExceptionHandler interface.');
}
$request = $handler->handleError($this->request, $this->request->loadedRoute, $e);
}
$request = $handler->handleError($request, $this->loadedRoute, $e);
$request = ($request === null) ? $this->request : $request;
if(!in_array($request->getUri(), $this->routeChanges)) {
$this->routeChanges[] = $request->getUri();
if($request->getUri() !== $this->request->getUri()) {
$this->routeRequest($request);
} else {
$this->loadedRoute->renderRoute($request);
}
return;
}
if($request !== null) {
$this->request = $request;
$this->routeRequest(false);
return;
}
throw $e;
}
/**
* Get default namespace
* @return string
*/
public function getDefaultNamespace(){
@@ -228,6 +309,7 @@ class RouterBase {
}
/**
* Set the main default namespace that all routes will inherit
* @param string $defaultNamespace
* @return static
*/
@@ -237,6 +319,7 @@ class RouterBase {
}
/**
* Get bootmanagers
* @return array
*/
public function getBootManagers() {
@@ -244,40 +327,21 @@ class RouterBase {
}
/**
* Set bootmanagers
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers) {
$this->bootManagers = $bootManagers;
}
/**
* Add bootmanager
* @param RouterBootManager $bootManager
*/
public function addBootManager(RouterBootManager $bootManager) {
$this->bootManagers[] = $bootManager;
}
/**
* @return RouterEntry
*/
public function getLoadedRoute() {
if(!($this->request->loadedRoute instanceof RouterGroup)) {
return $this->request->loadedRoute;
}
return null;
}
/**
* @return array
*/
public function getBackstack() {
return $this->backStack;
}
/**
* @return RouterEntry
*/
public function getCurrentRoute(){
return $this->currentRoute;
}
/**
* @return array
*/
@@ -303,21 +367,21 @@ class RouterBase {
}
/**
* Get base csrf verifier class
* Get csrf verifier class
* @return BaseCsrfVerifier
*/
public function getBaseCsrfVerifier() {
return $this->baseCsrfVerifier;
public function getCsrfVerifier() {
return $this->csrfVerifier;
}
/**
* Set base csrf verifier class
* Set csrf verifier class
*
* @param BaseCsrfVerifier $baseCsrfVerifier
* @return self
* @param BaseCsrfVerifier $csrfVerifier
* @return static
*/
public function setBaseCsrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) {
$this->baseCsrfVerifier = $baseCsrfVerifier;
public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier) {
$this->csrfVerifier = $csrfVerifier;
return $this;
}
@@ -353,25 +417,22 @@ class RouterBase {
$url = $domain . '/' . trim($route->getUrl(), '/');
if(($route instanceof RouterController || $route instanceof RouterResource) && $method !== null) {
if($method !== null) {
$url .= $method;
}
if($route instanceof IControllerRoute && $method !== null) {
$url .= $method;
if(count($parameters)) {
$url .= join('/', $parameters);
}
} else {
/* @var $route RouterEntry */
if(is_array($parameters)) {
if($parameters !== null && is_array($parameters)) {
$params = array_merge($route->getParameters(), $parameters);
} else {
$params = $route->getParameters();
}
$otherParams = [];
$otherParams = array();
$i = 0;
foreach($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
if(stripos($url, '{' . $param. '}') !== false || stripos($url, '{' . $param . '?}') !== false) {
@@ -406,7 +467,7 @@ class RouterBase {
// Return current route if no options has been specified
if($controller === null && $parameters === null) {
$getParams = (is_array($getParams)) ? array_merge($_GET, $getParams) : $_GET;
$getParams = ($getParams !== null && is_array($getParams)) ? array_merge($_GET, $getParams) : $_GET;
$url = parse_url($this->request->getUri(), PHP_URL_PATH);
@@ -417,13 +478,12 @@ class RouterBase {
return $url;
}
if($controller === null && $this->request->loadedRoute !== null) {
return $this->processUrl($this->request->loadedRoute, $this->request->loadedRoute->getMethod(), $parameters, $getParams);
if($controller === null && $this->loadedRoute !== null) {
return $this->processUrl($this->loadedRoute, $this->loadedRoute->getMethod(), $parameters, $getParams);
}
$c = '';
$method = null;
$max = count($this->controllerUrlMap);
/* @var $route RouterRoute */
@@ -432,8 +492,9 @@ class RouterBase {
$route = $this->controllerUrlMap[$i];
// Check an alias exist, if the matches - use it
if($route instanceof RouterRoute) {
if($route instanceof IControllerRoute) {
$c = $route->getController();
} else {
if($route->hasAlias($controller)) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
@@ -441,8 +502,6 @@ class RouterBase {
if(!is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) {
$c = $route->getCallback();
}
} else if($route instanceof RouterController || $route instanceof RouterResource) {
$c = $route->getController();
}
if($c === $controller || strpos($c, $controller) === 0) {
@@ -457,10 +516,10 @@ class RouterBase {
$route = $this->controllerUrlMap[$i];
if($route instanceof RouterRoute && !is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) {
$c = $route->getClass();
} else if($route instanceof RouterController || $route instanceof RouterResource) {
if($route instanceof IControllerRoute) {
$c = $route->getController();
} else if(!is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) {
$c = $route->getClass();
}
if(stripos($controller, '@') !== false) {
@@ -477,10 +536,8 @@ class RouterBase {
$controller = ($controller === null) ? '/' : $controller;
$url = array($controller);
if(is_array($parameters)) {
foreach($parameters as $key => $value) {
array_push($url,$value);
}
if($parameters !== null && is_array($parameters) && count($parameters)) {
$url = array_merge($url, $parameters);
}
$url = '/' . trim(join('/', $url), '/') . '/';
@@ -500,6 +557,7 @@ class RouterBase {
if(static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
+5 -6
View File
@@ -4,7 +4,7 @@ namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterController extends RouterEntry {
class RouterController extends RouterEntry implements ILoadableRoute, IControllerRoute {
const DEFAULT_METHOD = 'index';
@@ -13,7 +13,6 @@ class RouterController extends RouterEntry {
protected $method;
public function __construct($url, $controller) {
parent::__construct();
$this->url = $url;
$this->controller = $controller;
}
@@ -44,8 +43,8 @@ class RouterController extends RouterEntry {
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
@@ -55,11 +54,11 @@ class RouterController extends RouterEntry {
if(count($path)) {
$method = (!isset($path[0]) || trim($path[0]) === '') ? self::DEFAULT_METHOD : $path[0];
$method = (!isset($path[0]) || trim($path[0]) === '') ? static::DEFAULT_METHOD : $path[0];
$this->method = $method;
array_shift($path);
$this->parameters = $path;
$this->settings['parameters'] = $path;
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
+74 -135
View File
@@ -14,41 +14,26 @@ abstract class RouterEntry {
const REQUEST_TYPE_PATCH = 'patch';
const REQUEST_TYPE_DELETE = 'delete';
public static $allowedRequestTypes = array(
public static $allowedRequestTypes = [
self::REQUEST_TYPE_DELETE,
self::REQUEST_TYPE_GET,
self::REQUEST_TYPE_POST,
self::REQUEST_TYPE_PUT,
self::REQUEST_TYPE_PATCH
);
self::REQUEST_TYPE_PATCH,
];
protected $settings = [
'requestMethods' => array(),
'where' => array(),
'parameters' => array(),
'middleware' => array(),
];
protected $settings;
protected $callback;
public function __construct() {
$this->settings = array();
$this->settings['requestMethods'] = array();
$this->settings['where'] = array();
$this->settings['parameters'] = array();
}
/**
* Returns callback name/identifier for the current route based on the callback.
* Useful if you need to get a unique identifier for the loaded route, for instance
* when using translations etc.
*
* @return string
*/
public function getIdentifier() {
if(strpos($this->callback, '@') !== false) {
return $this->callback;
}
return 'function_' . md5($this->callback);
}
/**
* @param string $callback
* @return self;
* @return static
*/
public function setCallback($callback) {
$this->callback = $callback;
@@ -88,52 +73,36 @@ abstract class RouterEntry {
return $this;
}
/**
* @param string $prefix
* @return self
*/
public function setPrefix($prefix) {
$this->prefix = '/' . ltrim($prefix, '/');
return $this;
}
/**
* @param string $middleware
* @return self
* @return static
*/
public function setMiddleware($middleware) {
$this->middleware = $middleware;
$this->settings['middleware'][] = $middleware;
return $this;
}
/**
* @param string $namespace
* @return self
* @return static
*/
public function setNamespace($namespace) {
$this->namespace = $namespace;
$this->settings['namespace'] = $namespace;
return $this;
}
/**
* @return string
*/
public function getPrefix() {
return $this->prefix;
}
/**
* @return string|array
*/
public function getMiddleware() {
return $this->middleware;
return $this->settingArray('middleware');
}
/**
* @return string
*/
public function getNamespace() {
return $this->namespace;
return $this->setting('namespace');
}
/**
@@ -144,18 +113,18 @@ abstract class RouterEntry {
}
/**
* @return mixed
* @return array
*/
public function getParameters(){
return ($this->parameters === null) ? array() : $this->parameters;
return $this->setting('parameters', array());
}
/**
* @param mixed $parameters
* @return self
* @return static
*/
public function setParameters($parameters) {
$this->parameters = $parameters;
$this->settings['parameters'] = $parameters;
return $this;
}
@@ -163,10 +132,10 @@ abstract class RouterEntry {
* Add regular expression parameter match
*
* @param array $options
* @return self
* @return static
*/
public function where(array $options) {
$this->where = array_merge($this->where, $options);
$this->settings['where'] = array_merge($this->settings['where'], $options);
return $this;
}
@@ -174,10 +143,10 @@ abstract class RouterEntry {
* Add regular expression match for url
*
* @param string $regex
* @return self
* @return static
*/
public function match($regex) {
$this->regexMatch = $regex;
$this->settings['regexMatch'] = $regex;
return $this;
}
@@ -187,60 +156,27 @@ abstract class RouterEntry {
* @return array
*/
public function getMergeableSettings() {
$settings = $this->settings;
if(isset($settings['prefix'])) {
unset($settings['prefix']);
}
return $settings;
return $this->settings;
}
/**
* @param array $settings
* @return self
* @return static
*/
public function addSettings(array $settings = null) {
if(is_array($settings)) {
$this->settings = array_merge($this->settings, $settings);
}
public function addSettings(array $settings) {
$this->settings = array_merge($this->settings, $settings);
return $this;
}
/**
* @param array $settings
* @return self
* @return static
*/
public function setSettings($settings) {
$this->settings = $settings;
if(isset($settings['prefix'])) {
$this->setPrefix($settings['prefix']);
}
return $this;
}
/**
* Dynamically access settings value
*
* @param $name
* @return mixed|null
*/
public function __get($name) {
return (isset($this->settings[$name]) ? $this->settings[$name] : null);
}
/**
* Dynamically set settings value
*
* @param string $name
* @param mixed|null $value
*/
public function __set($name, $value = null) {
$this->settings[$name] = $value;
}
protected function loadClass($name) {
if(!class_exists($name)) {
throw new RouterException(sprintf('Class %s does not exist', $name));
@@ -273,8 +209,8 @@ abstract class RouterEntry {
// Check for optional parameter
// Use custom parameter regex if it exists
if(is_array($this->where) && isset($this->where[$parameter])) {
$parameterRegex = $this->where[$parameter];
if(is_array($this->setting('where')) && isset($this->settings['where'][$parameter])) {
$parameterRegex = $this->settings['where'][$parameter];
}
if($lastCharacter === '?') {
@@ -284,7 +220,12 @@ abstract class RouterEntry {
} else {
$regex .= '\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?';
}
$parameterNames[] = array('name' => $parameter, 'required' => $required);
$parameterNames[] = [
'name' => $parameter,
'required' => $required
];
$parameter = '';
$isParameter = false;
@@ -307,21 +248,19 @@ abstract class RouterEntry {
$max = count($parameterNames);
if($max) {
for($i = 0; $i < $max; $i++) {
$name = $parameterNames[$i];
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
for($i = 0; $i < $max; $i++) {
$name = $parameterNames[$i];
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if($name['required'] && $parameterValue === null) {
throw new RouterException('Missing required parameter ' . $name['name'], 404);
}
if(!$name['required'] && $parameterValue === null) {
continue;
}
$parameters[$name['name']] = $parameterValue;
if($name['required'] && $parameterValue === null) {
throw new RouterException('Missing required parameter ' . $name['name'], 404);
}
if(!$name['required'] && $parameterValue === null) {
continue;
}
$parameters[$name['name']] = $parameterValue;
}
return $parameters;
@@ -330,26 +269,16 @@ abstract class RouterEntry {
return null;
}
public function loadMiddleware(Request $request) {
if($this->getMiddleware()) {
if(is_array($this->getMiddleware())) {
foreach($this->getMiddleware() as $middleware) {
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new RouterException($middleware . ' must be instance of Middleware');
}
/* @var $class IMiddleware */
$middleware->handle($request);
}
} else {
$middleware = $this->loadClass($this->getMiddleware());
public function loadMiddleware(Request $request, RouterRoute &$route) {
if(count($this->getMiddleware())) {
foreach($this->getMiddleware() as $middleware) {
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new RouterException($this->getMiddleware() . ' must be instance of Middleware');
throw new RouterException($middleware . ' must be instance of Middleware');
}
/* @var $class IMiddleware */
$middleware->handle($request);
$middleware->handle($request, $route);
}
}
}
@@ -371,7 +300,7 @@ abstract class RouterEntry {
}
$parameters = array_filter($this->getParameters(), function($var){
return !is_null($var);
return ($var !== null);
});
call_user_func_array(array($class, $method), $parameters);
@@ -386,7 +315,7 @@ abstract class RouterEntry {
* Set allowed request methods
*
* @param array $methods
* @return self $this
* @return static $this
*/
public function setRequestMethods(array $methods) {
$this->settings['requestMethods'] = $methods;
@@ -399,22 +328,32 @@ abstract class RouterEntry {
* @return array
*/
public function getRequestMethods() {
if(!isset($this->settings['requestMethods']) || isset($this->settings['requestMethods']) && !is_array($this->settings['requestMethods'])) {
$value = isset($this->settings['requestMethods']) ? $this->settings['requestMethods'] : null;
return array($value);
}
return $this->settings['requestMethods'];
return $this->settingArray('requestMethods');
}
public function getGroup() {
return $this->group;
return $this->setting('group');
}
public function setGroup($group) {
$this->group = $group;
$this->settings['group'] = $group;
return $this;
}
protected function setting($name, $defaultValue = null) {
return isset($this->settings[$name]) ? $this->settings[$name] : $defaultValue;
}
protected function settingArray($name) {
$value = $this->setting($name);
if($value === null) {
return [];
}
return (!is_array($value)) ? array($value) : $value;
}
abstract function matchRoute(Request $request);
}
+49 -27
View File
@@ -8,19 +8,17 @@ use Pecee\Http\Request;
class RouterGroup extends RouterEntry {
public function matchDomain(Request $request) {
if($this->domain !== null) {
if($this->setting('domain') !== null) {
if(is_array($this->domain)) {
if(is_array($this->setting('domain'))) {
$max = count($this->domain);
for($i = 0; $i < $max; $i++) {
$domain = $this->domain[$i];
for($i = 0; $i < count($this->setting('domain')); $i++) {
$domain = $this->settings['domain'][$i];
$parameters = $this->parseParameters($domain, $request->getHost(), '[^.]*');
if($parameters !== null) {
$this->parameters = $parameters;
$this->settings['parameters'] = $parameters;
return true;
}
}
@@ -28,10 +26,10 @@ class RouterGroup extends RouterEntry {
return false;
}
$parameters = $this->parseParameters($this->domain, $request->getHost(), '[^.]*');
$parameters = $this->parseParameters($this->setting('domain'), $request->getHost(), '[^.]*');
if ($parameters !== null) {
$this->parameters = $parameters;
$this->settings['parameters'] = $parameters;
return true;
}
@@ -43,10 +41,10 @@ class RouterGroup extends RouterEntry {
public function renderRoute(Request $request) {
// Check if request method is allowed
$hasAccess = (!$this->method);
$hasAccess = true;
if($this->method) {
if(is_array($this->method)) {
if($this->setting('method') !== null) {
if(is_array($this->setting('method'))) {
$hasAccess = (in_array($request->getMethod(), $this->getRequestMethods()));
} else {
$hasAccess = strtolower($this->getRequestMethods()) == strtolower($request->getMethod());
@@ -72,45 +70,69 @@ class RouterGroup extends RouterEntry {
}
public function setExceptionHandler($class) {
$this->exceptionHandler = $class;
$this->settings['exceptionHandler'] = $class;
return $this;
}
public function getExceptionHandler() {
return $this->exceptionHandler;
return $this->setting('exceptionHandler');
}
public function getDomain() {
return $this->domain;
return $this->setting('domain');
}
/**
* @param string $prefix
* @return static
*/
public function setPrefix($prefix) {
$this->settings['prefix'] = '/' . ltrim($prefix, '/');
return $this;
}
/**
* @return string
*/
public function getPrefix() {
return $this->setting('prefix');
}
/**
* @param array $settings
* @return self
* @return static
*/
public function addSettings(array $settings = null) {
if($this->getNamespace() !== null && isset($settings['namespace'])) {
public function addSettings(array $settings) {
if ($this->getNamespace() !== null && isset($settings['namespace'])) {
unset($settings['namespace']);
}
// Push middleware if multiple
if($this->getMiddleware() !== null && isset($settings['middleware'])) {
if ($this->getMiddleware() !== null && isset($settings['middleware'])) {
if(!is_array($this->getMiddleware())) {
$middlewares = [$this->getMiddleware(), $settings['middleware']];
if (!is_array($settings['middleware'])) {
$settings['middleware'] = array_merge($this->getMiddleware(), array($settings['middleware']));
} else {
$middlewares = array_push($settings['middleware']);
$settings['middleware'][] = $this->getMiddleware();
}
$settings['middleware'] = array_unique(array_reverse($middlewares));
$settings['middleware'] = array_unique(array_reverse($settings['middleware']));
}
if(is_array($settings)) {
$this->settings = array_merge($this->settings, $settings);
}
$this->settings = array_merge($this->settings, $settings);
return $this;
}
public function getMergeableSettings() {
$settings = $this->settings;
if(isset($settings['prefix'])) {
unset($settings['prefix']);
}
return $settings;
}
}
+12 -16
View File
@@ -4,17 +4,14 @@ namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterResource extends RouterEntry {
class RouterResource extends RouterEntry implements ILoadableRoute, IControllerRoute {
protected $url;
protected $controller;
protected $postMethod;
public function __construct($url, $controller) {
parent::__construct();
$this->url = $url;
$this->controller = $controller;
$this->postMethod = strtolower(($_SERVER['REQUEST_METHOD'] != 'GET') ? 'post' : 'get');
}
public function renderRoute(Request $request) {
@@ -42,13 +39,13 @@ class RouterResource extends RouterEntry {
protected function call($method, $parameters) {
$this->setCallback($this->controller . '@' . $method);
$this->parameters = $parameters;
$this->settings['parameters'] = $parameters;
return true;
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
@@ -57,39 +54,39 @@ class RouterResource extends RouterEntry {
if($parameters !== null) {
if(is_array($parameters)) {
$parameters = array_merge($this->parameters, $parameters);
$parameters = array_merge($this->settings['parameters'], $parameters);
}
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
// Delete
if($request->getMethod() === self::REQUEST_TYPE_DELETE && $this->postMethod === self::REQUEST_TYPE_POST) {
if($request->getMethod() === static::REQUEST_TYPE_DELETE && $request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('destroy', $parameters);
}
// Update
if(in_array($request->getMethod(), array(self::REQUEST_TYPE_PATCH, self::REQUEST_TYPE_PUT)) && $this->postMethod === self::REQUEST_TYPE_POST) {
if(in_array($request->getMethod(), array(static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT)) && $request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('update', $parameters);
}
// Edit
if(isset($action) && strtolower($action) === 'edit' && $this->postMethod === self::REQUEST_TYPE_GET) {
if(isset($action) && strtolower($action) === 'edit' && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('edit', $parameters);
}
// Create
if(strtolower($action) === 'create' && $request->getMethod() === self::REQUEST_TYPE_GET) {
if(strtolower($action) === 'create' && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('create', $parameters);
}
// Save
if($this->postMethod === self::REQUEST_TYPE_POST) {
if($request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('store', $parameters);
}
// Show
if(isset($parameters['id']) && $this->postMethod === self::REQUEST_TYPE_GET) {
if(isset($parameters['id']) && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('show', $parameters);
}
@@ -112,8 +109,7 @@ class RouterResource extends RouterEntry {
* @return static
*/
public function setUrl($url) {
$url = rtrim($url, '/') . '/';
$this->url = $url;
$this->url = rtrim($url, '/') . '/';
return $this;
}
+27 -32
View File
@@ -4,28 +4,27 @@ namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
class RouterRoute extends RouterEntry {
class RouterRoute extends RouterEntry implements ILoadableRoute {
const PARAMETERS_REGEX_MATCH = '{([A-Za-z\-\_]*?)\?{0,1}}';
protected $url;
public function __construct($url, $callback) {
parent::__construct();
$this->setUrl($url);
$this->setCallback($callback);
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
// Match on custom defined regular expression
if($this->regexMatch) {
if($this->setting('regexMatch') !== null) {
$parameters = array();
if(preg_match('/('.$this->regexMatch.')/is', $request->getHost() . $url, $parameters)) {
$this->parameters = (!is_array($parameters[0]) ? array($parameters[0]) : $parameters[0]);
if(preg_match('/(' . $this->setting('regexMatch') . ')/is', $request->getHost() . $url, $parameters)) {
$this->settings['parameters'] = (!is_array($parameters[0]) ? array($parameters[0]) : $parameters[0]);
return true;
}
return null;
@@ -37,13 +36,7 @@ class RouterRoute extends RouterEntry {
$parameters = $this->parseParameters($route, $url);
if($parameters !== null) {
if(is_array($this->parameters)) {
$this->parameters = array_merge($this->parameters, $parameters);
} else {
$this->parameters = $parameters;
}
$this->settings['parameters'] = array_merge($this->settingArray('parameters'), $parameters);
return true;
}
@@ -59,22 +52,23 @@ class RouterRoute extends RouterEntry {
/**
* @param string $url
* @return self
* @return static
*/
public function setUrl($url) {
$parameters = array();
$matches = array();
if(preg_match_all('/'.self::PARAMETERS_REGEX_MATCH.'/is', $url, $matches)) {
if(preg_match_all('/' . static::PARAMETERS_REGEX_MATCH . '/is', $url, $matches)) {
$parameters = $matches[1];
}
if(count($parameters)) {
$tmp = array();
foreach($parameters as $param) {
$tmp[$param] = null;
foreach(array_keys($parameters) as $key) {
$parameters[$key] = null;
}
$this->parameters = $tmp;
$this->settings['parameters'] = $parameters;
}
$this->url = $url;
@@ -86,23 +80,24 @@ class RouterRoute extends RouterEntry {
* @return string|array
*/
public function getAlias(){
return $this->alias;
return $this->setting('alias');
}
/**
* Check if route has given alias.
*
* @param $name
* @param string $name
* @return bool
*/
public function hasAlias($name) {
if(is_array($this->alias)) {
foreach($this->alias as $alias) {
if(strtolower($alias) === strtolower($name)) {
return true;
if ($this->getAlias() !== null) {
if (is_array($this->getAlias())) {
foreach ($this->setting('alias') as $alias) {
if (strtolower($alias) === strtolower($name)) {
return true;
}
}
}
} else {
return strtolower($this->getAlias()) === strtolower($name);
}
@@ -112,21 +107,21 @@ class RouterRoute extends RouterEntry {
/**
* Set the url alias for easier getting the url route.
* @param string|array $alias
* @return self
* @return static
*/
public function setAlias($alias){
$this->alias = $alias;
$this->settings['alias'] = $alias;
return $this;
}
public function setSettings($settings) {
public function addSettings(array $settings) {
// Change as to alias
if(isset($settings{'as'})) {
if(isset($settings['as'])) {
$this->setAlias($settings['as']);
}
return parent::setSettings($settings);
return parent::addSettings($settings);
}
}
+24 -17
View File
@@ -13,17 +13,20 @@ use Pecee\Http\Middleware\BaseCsrfVerifier;
class SimpleRouter {
public function __construct() {
die('test');
/**
* Start/route request
* @throws \Pecee\Exception\RouterException
*/
public static function start() {
RouterBase::getInstance()->routeRequest();
}
/**
* Start/route request
* @param null $defaultNamespace
* @throws \Pecee\Exception\RouterException
* Set default namespace for all routes
* @param string $defaultNamespace
*/
public static function start($defaultNamespace = null) {
RouterBase::getInstance()->setDefaultNamespace($defaultNamespace)->routeRequest();
public static function setDefaultNamespace($defaultNamespace) {
RouterBase::getInstance()->setDefaultNamespace($defaultNamespace);
}
/**
@@ -31,7 +34,7 @@ class SimpleRouter {
* @param BaseCsrfVerifier $baseCsrfVerifier
*/
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) {
RouterBase::getInstance()->setBaseCsrfVerifier($baseCsrfVerifier);
RouterBase::getInstance()->setCsrfVerifier($baseCsrfVerifier);
}
public static function addBootManager(RouterBootManager $bootManager) {
@@ -39,19 +42,19 @@ class SimpleRouter {
}
public static function get($url, $callback, array $settings = null) {
return self::match(['get'], $url, $callback, $settings);
return static::match(['get'], $url, $callback, $settings);
}
public static function post($url, $callback, array $settings = null) {
return self::match(['post'], $url, $callback, $settings);
return static::match(['post'], $url, $callback, $settings);
}
public static function put($url, $callback, array $settings = null) {
return self::match(['put'], $url, $callback, $settings);
return static::match(['put'], $url, $callback, $settings);
}
public static function delete($url, $callback, array $settings = null) {
return self::match(['delete'], $url, $callback, $settings);
return static::match(['delete'], $url, $callback, $settings);
}
public static function group($settings = array(), $callback) {
@@ -76,7 +79,7 @@ class SimpleRouter {
* @return RouterRoute
*/
public static function basic($url, $callback, array $settings = null) {
return self::match(['get', 'post'], $url, $callback, $settings);
return static::match(['get', 'post'], $url, $callback, $settings);
}
public static function match(array $requestMethods, $url, $callback, array $settings = null) {
@@ -123,21 +126,25 @@ class SimpleRouter {
$route->addSettings($settings);
}
RouterBase::getInstance()->addRoute($route);
static::router()->addRoute($route);
return $route;
}
public static function getRoute($controller = null, $parameters = null, $getParams = null) {
return RouterBase::getInstance()->getRoute($controller, $parameters, $getParams);
return static::router()->getRoute($controller, $parameters, $getParams);
}
public static function request() {
return RouterBase::getInstance()->getRequest();
return static::router()->getRequest();
}
public static function response() {
return RouterBase::getInstance()->getResponse();
return static::router()->getResponse();
}
protected static function router() {
return RouterBase::getInstance();
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ use Pecee\Http\Request;
class DummyMiddleware implements IMiddleware {
public function handle(Request $request) {
public function handle(Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null) {
throw new MiddlewareLoadedException('Middleware loaded!');
}
+1 -1
View File
@@ -1,7 +1,7 @@
<?php
class ExceptionHandler implements \Pecee\Handler\IExceptionHandler {
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry $router = null, \Exception $error){
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null, \Exception $error){
throw $error;
}
+40 -7
View File
@@ -7,20 +7,18 @@ class GroupTest extends PHPUnit_Framework_TestCase {
protected $result;
protected function group() {
$this->result = true;
}
public function testGroup() {
public function testGroupLoad() {
$this->result = false;
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/group'], $this->group());
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/group'], function() {
$this->result = true;
});
try {
\Pecee\SimpleRouter\SimpleRouter::start();
} catch(Exception $e) {
echo $e->getMessage();
}
$this->assertTrue($this->result);
@@ -41,4 +39,39 @@ class GroupTest extends PHPUnit_Framework_TestCase {
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testManyRoutes() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/match');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start');
});
});
\Pecee\SimpleRouter\SimpleRouter::get('/my/match', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/service'], function() {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/no-match', 'DummyController@start');
});
});
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testUrls() {
\Pecee\SimpleRouter\SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']);
\Pecee\SimpleRouter\SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setAlias('fancy2');
\Pecee\SimpleRouter\SimpleRouter::start();
$this->assertTrue((\Pecee\SimpleRouter\SimpleRouter::getRoute('fancy1') === '/my/fancy/url/1/'));
$this->assertTrue((\Pecee\SimpleRouter\SimpleRouter::getRoute('fancy2') === '/my/fancy/url/2/'));
}
}