From ed1ac74e7a0bdb20b39bcdeec6c78a0d7477965b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sat, 19 Nov 2016 02:48:19 +0100 Subject: [PATCH] Development - Fixed updatae causing middlewares to sometimes load on wrong routes. - Converted project to PSR/2. - Updated InputCollection class and added get method for easy access to values. - Complete refactor of RouterBase. - Added findRoute method to RouterBase. - It's now possible to change parameter modifiers and symbol by overwriting properties on RouterBase. - Added RouterUrlTest unit-test for testing route-urls. - Added IRestController that can be easily implemented in custom ResourceController-classes. - It's now possible to use "-" instead of "_" when using getHeader method in Request class. - Added PHPDocs. - Fixed "/" route sometimes returning "//" as url. - Optimisations and bugfixes. --- README.md | 22 +- .../app/Controllers/ApiController.php | 24 +- .../app/Controllers/DefaultController.php | 40 +- .../app/Handlers/CustomExceptionHandler.php | 42 +- .../app/Middlewares/ApiVerification.php | 17 +- demo-project/app/Middlewares/CsrfVerifier.php | 12 +- demo-project/app/Router.php | 27 +- demo-project/app/helpers.php | 27 +- demo-project/app/routes.php | 20 +- src/Pecee/Controller/IRestController.php | 46 + src/Pecee/CsrfToken.php | 108 +- src/Pecee/Exception/RouterException.php | 4 +- .../Exception/TokenMismatchException.php | 4 +- src/Pecee/Handler/IExceptionHandler.php | 16 +- src/Pecee/Http/Input/Input.php | 322 +++--- src/Pecee/Http/Input/InputCollection.php | 143 +-- src/Pecee/Http/Input/InputFile.php | 95 +- src/Pecee/Http/Input/InputItem.php | 107 +- .../Http/Middleware/BaseCsrfVerifier.php | 129 +-- src/Pecee/Http/Middleware/IMiddleware.php | 16 +- src/Pecee/Http/Request.php | 327 +++--- src/Pecee/Http/Response.php | 187 ++-- src/Pecee/SimpleRouter/IControllerRoute.php | 12 +- src/Pecee/SimpleRouter/ILoadableRoute.php | 8 +- src/Pecee/SimpleRouter/LoadableRoute.php | 140 +-- src/Pecee/SimpleRouter/RouterBase.php | 983 +++++++++--------- src/Pecee/SimpleRouter/RouterBootManager.php | 7 +- src/Pecee/SimpleRouter/RouterController.php | 153 +-- src/Pecee/SimpleRouter/RouterEntry.php | 815 ++++++++------- src/Pecee/SimpleRouter/RouterGroup.php | 143 +-- src/Pecee/SimpleRouter/RouterResource.php | 174 ++-- src/Pecee/SimpleRouter/RouterRoute.php | 58 +- src/Pecee/SimpleRouter/SimpleRouter.php | 409 ++++++-- test/Dummy/DummyController.php | 29 +- test/Dummy/DummyMiddleware.php | 12 +- .../Exceptions/MiddlewareLoadedException.php | 4 +- test/Dummy/Handler/ExceptionHandler.php | 10 +- test/GroupTest.php | 115 +- test/MiddlewareTest.php | 37 +- test/RouterRouteTest.php | 204 ++-- test/RouterUrlTest.php | 83 ++ 41 files changed, 2813 insertions(+), 2318 deletions(-) create mode 100644 src/Pecee/Controller/IRestController.php create mode 100644 test/RouterUrlTest.php diff --git a/README.md b/README.md index 403f5f4..da46842 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ The goal of this project is to create a router that is 100% compatible with the - CSRF protection. - Optional parameters - Sub-domain routing -- Custom boot managers to redirect urls to other routes -- Input manager; to manage `GET`, `POST` params. +- Custom boot managers to rewrite urls to "nicer" ones. +- Input manager; easily manage `GET`, `POST` and `FILE` values. ## Installation and demo @@ -71,6 +71,7 @@ This router is heavily inspired by the Laravel 5.* router, so anything you find - ExceptionsHandlers must implement the `IExceptionHandler` interface. - Middlewares must implement the `IMiddleware` interface. +- Resource controllers can inherit the `IRestController` interface, but is not required. ```php use Pecee\SimpleRouter\SimpleRouter; @@ -103,7 +104,7 @@ SimpleRouter::group(['prefix' => '/v1', 'middleware' => '\MyWebsite\Middleware\S */ SimpleRouter::all('/ajax', 'ControllerAjax@process')->match('.*?\\/ajax\\/([A-Za-z0-9\\/]+)'); - // Restful resource + // Restful resource (see IRestController interface for available methods) SimpleRouter::resource('/rest', 'ControllerRessource'); // Load the entire controller (where url matches method names - getIndex(), postIndex() etc) @@ -146,11 +147,10 @@ class CustomExceptionHandler implements IExceptionHandler { // Output error as json if on api path. if(stripos($request->getUri(), '/api') !== false) { - response()->json(['error' => $error->getMessage()]); + response()->json([ 'error' => $error->getMessage() ]); } // Otherwise default exception will be thrown by the router. - } } @@ -210,7 +210,7 @@ class Router extends SimpleRouter { require_once 'routes.php'; // change default namespace for all routes - parent::setDefaultNamespace('\Demo\Controllers'); + parent::setDefaultNamespace('\Demo'); // Do initial stuff parent::start(); @@ -449,6 +449,12 @@ $object = input()->getObject('name'); $object = input()->get->name; $object = input()->post->name; $object = input()->file->name; + +// -- or -- + +$object = input()->get->get($key, $defaultValue); +$object = input()->post->get($key, $defaultValue); +$object = input()->file->get($key, $defaultValue); ``` **Return all parameters:** @@ -464,6 +470,7 @@ $values = input()->all([ ``` All object inherits from `InputItem` class and will always contain these methods: + - `getValue()` - returns the value of the input. - `getIndex()` - returns the index/key of the input. - `getName()` - returns a human friendly name for the input (company_name will be Company Name etc). @@ -476,9 +483,6 @@ All object inherits from `InputItem` class and will always contain these methods - `getType()` - get mime-type for file. - `getError()` - get file upload error. - -### Easy access to methods - Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation. ```php diff --git a/demo-project/app/Controllers/ApiController.php b/demo-project/app/Controllers/ApiController.php index c8e951f..e163233 100644 --- a/demo-project/app/Controllers/ApiController.php +++ b/demo-project/app/Controllers/ApiController.php @@ -1,20 +1,16 @@ request()->authenticated - ]); - - } + echo json_encode([ + 'authenticated' => request()->authenticated + ]); + } } \ No newline at end of file diff --git a/demo-project/app/Controllers/DefaultController.php b/demo-project/app/Controllers/DefaultController.php index 29c538f..ee1f9ff 100644 --- a/demo-project/app/Controllers/DefaultController.php +++ b/demo-project/app/Controllers/DefaultController.php @@ -1,29 +1,27 @@ index (?fun=%s)', input()->get('fun')); + } - public function index() { + public function contact() + { + echo 'DefaultController -> contact'; + } - // implement - echo sprintf('DefaultController -> index (?fun=%s)', input()->get('fun')); + public function companies($id = null) + { + echo 'DefaultController -> companies -> id: ' . $id; + } - } - - public function contact() { - - echo 'DefaultController -> contact'; - - } - - public function companies($id = null) { - - echo 'DefaultController -> companies -> id: ' . $id; - - } - - public function notFound() { - echo 'Page not found'; - } + public function notFound() + { + echo 'Page not found'; + } } \ No newline at end of file diff --git a/demo-project/app/Handlers/CustomExceptionHandler.php b/demo-project/app/Handlers/CustomExceptionHandler.php index d758ae7..1d9171a 100644 --- a/demo-project/app/Handlers/CustomExceptionHandler.php +++ b/demo-project/app/Handlers/CustomExceptionHandler.php @@ -5,30 +5,30 @@ use Pecee\Handler\IExceptionHandler; use Pecee\Http\Request; use Pecee\SimpleRouter\RouterEntry; -class CustomExceptionHandler implements IExceptionHandler { +class CustomExceptionHandler implements IExceptionHandler +{ + 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) { + header('content-type: application/json'); + echo json_encode([ + 'error' => $error->getMessage(), + 'code' => $error->getCode() + ]); + die(); + } - public function handleError( Request $request, RouterEntry &$route = null, \Exception $error) { + // else we just throw the error + if ($error->getCode() == 404) { - // Return json errors if we encounter an error on /api. - if(stripos($request->getUri(), '/api') !== false) { - header('content-type: application/json'); - echo json_encode([ - 'error' => $error->getMessage(), - 'code' => $error->getCode() - ]); - die(); - } + // Return 404 path + $request->setUri('/404'); + return $request; - // else we just throw the error - if($error->getCode() == 404) { + } - // Return 404 path - $request->setUri('/404'); - return $request; - - } - - throw $error; - } + throw $error; + } } \ No newline at end of file diff --git a/demo-project/app/Middlewares/ApiVerification.php b/demo-project/app/Middlewares/ApiVerification.php index b73a8a0..308bc29 100644 --- a/demo-project/app/Middlewares/ApiVerification.php +++ b/demo-project/app/Middlewares/ApiVerification.php @@ -5,15 +5,14 @@ use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Request; use Pecee\SimpleRouter\RouterEntry; -class ApiVerification implements IMiddleware { +class ApiVerification implements IMiddleware +{ + public function handle(Request $request, RouterEntry &$route) + { + // Do authentication + $request->authenticated = true; - public function handle(Request $request, RouterEntry &$route) { - - // Do authentication - $request->authenticated = true; - - return $request; - - } + return $request; + } } \ No newline at end of file diff --git a/demo-project/app/Middlewares/CsrfVerifier.php b/demo-project/app/Middlewares/CsrfVerifier.php index 0c65ff7..636dd12 100644 --- a/demo-project/app/Middlewares/CsrfVerifier.php +++ b/demo-project/app/Middlewares/CsrfVerifier.php @@ -3,11 +3,11 @@ namespace Demo\Middlewares; use Pecee\Http\Middleware\BaseCsrfVerifier; -class CsrfVerifier extends BaseCsrfVerifier { - - /** - * CSRF validation will be ignored on the following urls. - */ - protected $except = ['/api/*']; +class CsrfVerifier extends BaseCsrfVerifier +{ + /** + * CSRF validation will be ignored on the following urls. + */ + protected $except = ['/api/*']; } \ No newline at end of file diff --git a/demo-project/app/Router.php b/demo-project/app/Router.php index 2fd10db..414f895 100644 --- a/demo-project/app/Router.php +++ b/demo-project/app/Router.php @@ -1,29 +1,26 @@ getToken(); +function csrf_token() +{ + $token = new \Pecee\CsrfToken(); + return $token->getToken(); } /** * Get request object * @return \Pecee\Http\Request */ -function request() { - return SimpleRouter::request(); +function request() +{ + return SimpleRouter::request(); } /** * Get response object * @return \Pecee\Http\Response */ -function response() { - return SimpleRouter::response(); +function response() +{ + return SimpleRouter::response(); } /** * Get input class * @return \Pecee\Http\Input\Input */ -function input() { - return SimpleRouter::request()->getInput(); +function input() +{ + return SimpleRouter::request()->getInput(); } \ No newline at end of file diff --git a/demo-project/app/routes.php b/demo-project/app/routes.php index 6125596..2acdc52 100644 --- a/demo-project/app/routes.php +++ b/demo-project/app/routes.php @@ -7,17 +7,17 @@ use Demo\Router; Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier()); -Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function() { +Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function () { - Router::get('/', 'DefaultController@index')->setAlias('home'); - Router::get('/contact', 'DefaultController@contact')->setAlias('contact'); - Router::get('/404', 'DefaultController@notFound')->setAlias('404'); - Router::basic('/companies', 'DefaultController@companies')->setAlias('companies'); - Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('companies'); + Router::get('/', 'DefaultController@index')->setAlias('home'); + Router::get('/contact', 'DefaultController@contact')->setAlias('contact'); + Router::get('/404', 'DefaultController@notFound')->setAlias('404'); + Router::basic('/companies', 'DefaultController@companies')->setAlias('companies'); + Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('companies'); - // Api - Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function() { - Router::resource('/demo', 'ApiController'); - }); + // Api + Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function () { + Router::resource('/demo', 'ApiController'); + }); }); \ No newline at end of file diff --git a/src/Pecee/Controller/IRestController.php b/src/Pecee/Controller/IRestController.php new file mode 100644 index 0000000..e7994b9 --- /dev/null +++ b/src/Pecee/Controller/IRestController.php @@ -0,0 +1,46 @@ +getToken() !== null) { + return hash_equals($token, $this->getToken()); + } + return false; + } - /** - * Validate valid CSRF token - * - * @param string $token - * @return bool - */ - public function validate($token) { - 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(static::CSRF_KEY, $token, time() + 60 * 120, '/'); + } - /** - * Set csrf token cookie - * - * @param $token - */ - public function setToken($token) { - setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/'); - } + /** + * Get csrf token + * @return string|null + */ + public function getToken() + { + if ($this->hasToken()) { + return $_COOKIE[static::CSRF_KEY]; + } + return null; + } - /** - * Get csrf token - * @return string|null - */ - public function getToken(){ - if($this->hasToken()) { - return $_COOKIE[static::CSRF_KEY]; - } - return null; - } - - /** - * Returns whether the csrf token has been defined - * @return bool - */ - public function hasToken() { - return isset($_COOKIE[static::CSRF_KEY]); - } + /** + * Returns whether the csrf token has been defined + * @return bool + */ + public function hasToken() + { + return isset($_COOKIE[static::CSRF_KEY]); + } } \ No newline at end of file diff --git a/src/Pecee/Exception/RouterException.php b/src/Pecee/Exception/RouterException.php index a9eb41c..97e9667 100644 --- a/src/Pecee/Exception/RouterException.php +++ b/src/Pecee/Exception/RouterException.php @@ -1,4 +1,6 @@ request = $request; + $this->setGet(); + $this->setPost(); + $this->setFile(); + } - public function __construct(Request $request) { - $this->request = $request; - $this->setGet(); - $this->setPost(); - $this->setFile(); - } + /** + * Get all get/post items + * @param array|null $filter Only take items in filter + * @return array + */ + public function all(array $filter = null) + { + $output = $_POST; - /** - * Get all get/post items - * @param array|null $filter Only take items in filter - * @return array - */ - public function all(array $filter = null) { + if ($this->request->getMethod() === 'post') { - $output = $_POST; + $contents = file_get_contents('php://input'); - if($this->request->getMethod() === 'post') { + if (stripos(trim($contents), '{') === 0) { + $output = json_decode($contents, true); + if ($output === false) { + $output = array(); + } + } + } - $contents = file_get_contents('php://input'); + $output = array_merge($_GET, $output); - if (stripos(trim($contents), '{') === 0) { - $output = json_decode($contents, true); - if($output === false) { - $output = array(); - } - } - } + if ($filter !== null) { + $output = array_filter($output, function ($key) use ($filter) { + if (in_array($key, $filter)) { + return true; + } - $output = array_merge($_GET, $output); + return false; + }, ARRAY_FILTER_USE_KEY); + } - if($filter !== null) { - $output = array_filter($output, function ($key) use ($filter) { - if (in_array($key, $filter)) { - return true; - } + return $output; + } - return false; - }, ARRAY_FILTER_USE_KEY); - } + public function getObject($index, $default = null) + { + $key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[') + 1, strpos($index, ']') - strlen($index)) : null; + $index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index; - return $output; - } + $element = $this->get->findFirst($index); - public function getObject($index, $default = null) { - $key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[')+1, strpos($index, ']') - strlen($index)) : null; - $index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index; + if ($element !== null) { + return ($key !== null) ? $element[$key] : $element; + } - $element = $this->get->findFirst($index); + if ($this->request->getMethod() !== 'get') { - if($element !== null) { - return ($key !== null) ? $element[$key] : $element; - } + $element = $this->post->findFirst($index); + if ($element !== null) { + return ($key !== null) ? $element[$key] : $element; + } - if($this->request->getMethod() !== 'get') { + $element = $this->file->findFirst($index); + if ($element !== null) { + return ($key !== null) ? $element[$key] : $element; + } + } - $element = $this->post->findFirst($index); + return $default; + } - if ($element !== null) { - return ($key !== null) ? $element[$key] : $element; - } + /** + * Get input element value matching index + * @param string $index + * @param string|null $default + * @return string|null + */ + public function get($index, $default = null) + { + $item = $this->getObject($index); - $element = $this->file->findFirst($index); - if ($element !== null) { - return ($key !== null) ? $element[$key] : $element; - } - } + if ($item !== null) { - return $default; - } + if ($item instanceof InputCollection || $item instanceof InputFile) { + return $item; + } - /** - * Get input element value matching index - * @param string $index - * @param string|null $default - * @return string|null - */ - public function get($index, $default = null) { + return (trim($item->getValue()) === '') ? $default : $item->getValue(); + } - $item = $this->getObject($index); + return $default; + } - if($item !== null) { + public function exists($index) + { + return ($this->getObject($index) !== null); + } - if($item instanceof InputCollection || $item instanceof InputFile) { - return $item; - } + public function setGet() + { + $this->get = new InputCollection(); - return (trim($item->getValue()) === '') ? $default : $item->getValue(); - } + if (count($_GET) > 0) { + foreach ($_GET as $key => $get) { + if (is_array($get) === false) { + $this->get->{$key} = new InputItem($key, $get); + continue; + } - return $default; - } + $output = new InputCollection(); - public function exists($index) { - return ($this->getObject($index) !== null); - } + foreach ($get as $k => $g) { + $output->{$k} = new InputItem($k, $g); + } - public function setGet() { - $this->get = new InputCollection(); + $this->get->{$key} = $output; + } + } + } - if(count($_GET)) { - foreach($_GET as $key => $get) { - if(!is_array($get)) { - $this->get->{$key} = new InputItem($key, $get); - continue; - } + public function setPost() + { + $this->post = new InputCollection(); - $output = new InputCollection(); + $postVars = $_POST; - foreach($get as $k => $g) { - $output->{$k} = new InputItem($k, $g); - } + if (in_array($this->request->getMethod(), ['put', 'patch', 'delete']) === true) { + parse_str(file_get_contents('php://input'), $postVars); + } - $this->get->{$key} = $output; - } - } - } + if (count($postVars) > 0) { - public function setPost() { - $this->post = new InputCollection(); + foreach ($postVars as $key => $post) { + if (is_array($post) === false) { + $this->post->{strtolower($key)} = new InputItem($key, $post); + continue; + } - $postVars = $_POST; + $output = new InputCollection(); - if(in_array($this->request->getMethod(), ['put', 'patch', 'delete'])) { - parse_str(file_get_contents('php://input'), $postVars); - } + foreach ($post as $k => $p) { + $output->{$k} = new InputItem($k, $p); + } - if(count($postVars)) { + $this->post->{strtolower($key)} = $output; + } + } + } - foreach($postVars as $key => $post) { - if(!is_array($post)) { - $this->post->{strtolower($key)} = new InputItem($key, $post); - continue; - } + public function setFile() + { + $this->file = new InputCollection(); - $output = new InputCollection(); + if (count($_FILES) > 0) { + foreach ($_FILES as $key => $values) { - foreach($post as $k => $p) { - $output->{$k} = new InputItem($k, $p); - } + // Handle array input + if (is_array($values['name']) === false && trim($values['error']) !== '4') { + $values['index'] = $key; + $this->file->{strtolower($key)} = InputFile::createFromArray($values); + continue; + } - $this->post->{strtolower($key)} = $output; - } - } - } + $output = new InputCollection(); - public function setFile() { - $this->file = new InputCollection(); + foreach ($values['name'] as $k => $val) { + if (trim($val['error'][$k]) !== '4') { + $output->{$k} = InputFile::createFromArray([ + 'index' => $k, + 'error' => $val['error'][$k], + 'tmp_name' => $val['tmp_name'][$k], + 'type' => $val['type'][$k], + 'size' => $val['size'][$k], + 'name' => $val['name'][$k] + ]); + } + } - if(count($_FILES)) { - foreach($_FILES as $key => $value) { - // Multiple files - if(!is_array($value['name'])) { - // Strip empty values - if($value['error'] != '4') { - $file = new InputFile($key); - $file->setName($value['name']); - $file->setSize($value['size']); - $file->setType($value['type']); - $file->setTmpName($value['tmp_name']); - $file->setError($value['error']); - $this->file->{strtolower($key)} = $file; - } - continue; - } - - $output = new InputCollection(); - - foreach($value['name'] as $k=>$val) { - // Strip empty values - if($value['error'][$k] != '4') { - $file = new InputFile($k); - $file->setName($value['name'][$k]); - $file->setSize($value['size'][$k]); - $file->setType($value['type'][$k]); - $file->setTmpName($value['tmp_name'][$k]); - $file->setError($value['error'][$k]); - $output->{$k} = $file; - } - } - - $this->file->{strtolower($key)} = $output; - } - } - } + $this->file->{strtolower($key)} = $output; + } + } + } } \ No newline at end of file diff --git a/src/Pecee/Http/Input/InputCollection.php b/src/Pecee/Http/Input/InputCollection.php index cf81977..b1d00c1 100644 --- a/src/Pecee/Http/Input/InputCollection.php +++ b/src/Pecee/Http/Input/InputCollection.php @@ -1,85 +1,94 @@ data) > 0) { - /** - * Search for input element matching index. - * 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, $defaultValue = null) { - if(count($this->data)) { + if (isset($this->data[$index])) { + return $this->data[$index]; + } - if(isset($this->data[$index])) { - return $this->data[$index]; - } + foreach ($this->data as $key => $input) { + if (strtolower($index) === strtolower($key)) { + return $input; + } + } + } - foreach($this->data as $key => $value) { - if(strtolower($index) === strtolower($key)) { - return $value; - } - } - } + return $default; + } - return $defaultValue; - } + /** + * Get input element value matching index + * + * @param string $index + * @param string|null $default + * @return string|null + */ + public function get($index, $default = null) + { + $input = $this->findFirst($index); - public function getValue($index, $defaultValue = null) { - if(count($this->data)) { + if($input !== null) { + if(trim($input->getValue()) === '') { + return $default; + } - if(isset($this->data[$index])) { - return $this->data[$index]->getValue(); - } + return $input; + } - foreach($this->data as $key => $value) { - if(strtolower($index) === strtolower($key)) { - return $value->getValue(); - } - } - } + return $default; + } - return $defaultValue; - } + /** + * @param string $index + * @throws \InvalidArgumentException + * @return InputItem + */ + public function __get($index) + { + $item = $this->findFirst($index); + // Ensure that item are always available + if ($item === null) { + $this->data[$index] = new InputItem($index, null); + return $this->data[$index]; + } - /** - * @param $index - * @throws \InvalidArgumentException - * @return InputItem - */ - public function __get($index) { - $item = $this->findFirst($index); - // Ensure that item are always available - if($item === null) { - $this->data[$index] = new InputItem($index, null); - return $this->data[$index]; - } + return $item; + } - return $item; - } + public function __set($index, $value) + { + $this->data[$index] = $value; + } - public function __set($index, $value) { - $this->data[$index] = $value; - } + public function getData() + { + return $this->data; + } - public function getData() { - return $this->data; - } - - /** - * Retrieve an external iterator - * @link http://php.net/manual/en/iteratoraggregate.getiterator.php - * @return \Traversable An instance of an object implementing Iterator or - * Traversable - * @since 5.0.0 - */ - public function getIterator() { - return new \ArrayIterator($this->data); - } + /** + * Retrieve an external iterator + * @link http://php.net/manual/en/iteratoraggregate.getiterator.php + * @return \Traversable An instance of an object implementing Iterator or + * Traversable + * @since 5.0.0 + */ + public function getIterator() + { + return new \ArrayIterator($this->data); + } } \ No newline at end of file diff --git a/src/Pecee/Http/Input/InputFile.php b/src/Pecee/Http/Input/InputFile.php index 429d3f3..71c2484 100644 --- a/src/Pecee/Http/Input/InputFile.php +++ b/src/Pecee/Http/Input/InputFile.php @@ -1,68 +1,109 @@ size; } /** * @return string */ - public function getType() { + public function getType() + { return $this->type; } /** * @return string */ - public function getError() { + public function getError() + { return $this->error; } + public function getMime() + { + return $this->getType(); + } + /** * @return string */ - public function getTmpName() { + public function getTmpName() + { return $this->tmpName; } - public function getExtension() { + public function getExtension() + { return pathinfo($this->getName(), PATHINFO_EXTENSION); - } + } - public function move($destination) { + public function move($destination) + { return move_uploaded_file($this->tmpName, $destination); } - public function getContents() { + public function getContents() + { return file_get_contents($this->tmpName); } - public function setTmpName($name) { - $this->tmpName = $name; - } + public function setTmpName($name) + { + $this->tmpName = $name; + } - public function setSize($size) { - $this->size = $size; - } + public function setSize($size) + { + $this->size = $size; + } - public function setType($type) { - $this->type = $type; - } + public function setType($type) + { + $this->type = $type; + } - public function setError($error) { - $this->error = $error; - } + public function setError($error) + { + $this->error = $error; + } + + /** + * Create from array + * @param array $values + * @return static + */ + public static function createFromArray(array $values) + { + if(!isset($values['index'])) { + throw new \InvalidArgumentException('Index key is required'); + } + + $input = new static($values['index']); + $input->setTmpName((isset($values['error']) ? $values['error'] : null)); + $input->setName((isset($values['name']) ? $values['name'] : null)); + $input->setSize((isset($values['size']) ? $values['size'] : null)); + $input->setType((isset($values['type']) ? $values['type'] : null)); + $input->setError((isset($values['tmp_name']) ? $values['tmp_name'] : null)); + + return $input; + } + + public function __toString() + { + return (string)$this->tmpName; + } } \ No newline at end of file diff --git a/src/Pecee/Http/Input/InputItem.php b/src/Pecee/Http/Input/InputItem.php index cad36f4..ff66845 100644 --- a/src/Pecee/Http/Input/InputItem.php +++ b/src/Pecee/Http/Input/InputItem.php @@ -1,63 +1,70 @@ index = $index; + $this->value = $value; - public function __construct($index, $value = null) { - $this->index = $index; - $this->value = $value; + // Make the name human friendly, by replace _ with space + $this->name = ucfirst(str_replace('_', ' ', $this->index)); + } - // Make the name human friendly, by replace _ with space - $this->name = ucfirst(str_replace('_', ' ', $this->index)); - } + /** + * @return array + */ + public function getName() + { + return $this->name; + } - /** - * @return array - */ - public function getName() { - return $this->name; - } + /** + * @return array + */ + public function getValue() + { + return $this->value; + } - /** - * @return array - */ - public function getValue() { - return $this->value; - } + /** + * @return string + */ + public function getIndex() + { + return $this->index; + } - /** - * @return string - */ - public function getIndex() { - return $this->index; - } + /** + * Set input name + * @param string $name + * @return static $this + */ + public function setName($name) + { + $this->name = $name; + return $this; + } - /** - * Set input name - * @param string $name - * @return static $this - */ - public function setName($name) { - $this->name = $name; - return $this; - } + /** + * Set input value + * @param string $value + * @return static $this + */ + public function setValue($value) + { + $this->value = $value; + return $this; + } - /** - * Set input value - * @param string $value - * @return static $this - */ - public function setValue($value) { - $this->value = $value; - return $this; - } - - public function __toString() { - return (string)$this->getValue(); - } + public function __toString() + { + return (string)$this->value; + } } \ No newline at end of file diff --git a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php index 7739bdd..68a9419 100644 --- a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php +++ b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php @@ -6,85 +6,90 @@ use Pecee\Exception\TokenMismatchException; use Pecee\Http\Request; use Pecee\SimpleRouter\RouterEntry; -class BaseCsrfVerifier implements IMiddleware { +class BaseCsrfVerifier implements IMiddleware +{ + const POST_KEY = 'csrf-token'; + const HEADER_KEY = 'X-CSRF-TOKEN'; - const POST_KEY = 'csrf-token'; - const HEADER_KEY = 'X-CSRF-TOKEN'; + protected $except; + protected $csrfToken; + protected $token; - protected $except; - protected $csrfToken; - protected $token; + public function __construct() + { + $this->csrfToken = new CsrfToken(); - public function __construct() { - $this->csrfToken = new CsrfToken(); + // Generate or get the CSRF-Token from Cookie. + $this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken(); + } - // Generate or get the CSRF-Token from Cookie. - $this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken(); - } + /** + * Check if the url matches the urls in the except property + * @param Request $request + * @return bool + */ + protected function skip(Request $request) + { + if ($this->except === null || is_array($this->except) === false) { + return false; + } - /** - * Check if the url matches the urls in the except property - * @param Request $request - * @return bool - */ - protected function skip(Request $request) { + foreach ($this->except as $url) { + $url = rtrim($url, '/'); + if ($url[strlen($url) - 1] === '*') { + $url = rtrim($url, '*'); + $skip = (stripos($request->getUri(), $url) === 0); + } else { + $skip = ($url === rtrim($request->getUri(), '/')); + } - if($this->except === null || !is_array($this->except)) { - return false; - } + if ($skip) { + return true; + } + } - foreach($this->except as $url) { - $url = rtrim($url, '/'); - if($url[strlen($url)-1] === '*') { - $url = rtrim($url, '*'); - $skip = (stripos($request->getUri(), $url) === 0); - } else { - $skip = ($url === rtrim($request->getUri(), '/')); - } + return false; + } - if($skip) { - return true; - } - } + public function handle(Request $request, RouterEntry &$route = null) + { - return false; - } + if ($request->getMethod() !== 'get' && !$this->skip($request)) { - public function handle(Request $request, RouterEntry &$route = null) { + $token = $request->getInput()->post->getValue(static::POST_KEY); - if($request->getMethod() !== 'get' && !$this->skip($request)) { + // If the token is not posted, check headers for valid x-csrf-token + if ($token === null) { + $token = $request->getHeader(static::HEADER_KEY); + } - $token = $request->getInput()->post->getValue(static::POST_KEY); + if (!$this->csrfToken->validate($token)) { + throw new TokenMismatchException('Invalid csrf-token.'); + } - // If the token is not posted, check headers for valid x-csrf-token - if($token === null) { - $token = $request->getHeader(static::HEADER_KEY); - } + } - if( !$this->csrfToken->validate($token) ) { - throw new TokenMismatchException('Invalid csrf-token.'); - } + } - } + public function generateToken() + { + $token = $this->csrfToken->generateToken(); + $this->csrfToken->setToken($token); + return $token; + } - } + public function hasToken() + { + if ($this->token != null) { + return true; + } - public function generateToken() { - $token = $this->csrfToken->generateToken(); - $this->csrfToken->setToken($token); - return $token; - } + return $this->csrfToken->hasToken(); + } - public function hasToken() { - if($this->token != null) { - return true; - } - - return $this->csrfToken->hasToken(); - } - - public function getToken() { - return $this->token; - } + public function getToken() + { + return $this->token; + } } \ No newline at end of file diff --git a/src/Pecee/Http/Middleware/IMiddleware.php b/src/Pecee/Http/Middleware/IMiddleware.php index a210f71..454c487 100644 --- a/src/Pecee/Http/Middleware/IMiddleware.php +++ b/src/Pecee/Http/Middleware/IMiddleware.php @@ -4,13 +4,13 @@ namespace Pecee\Http\Middleware; use Pecee\Http\Request; use Pecee\SimpleRouter\RouterEntry; -interface IMiddleware { - - /** - * @param Request $request - * @param RouterEntry $route - * @return Request|null - */ - public function handle(Request $request, RouterEntry &$route); +interface IMiddleware +{ + /** + * @param Request $request + * @param RouterEntry $route + * @return Request|null + */ + public function handle(Request $request, RouterEntry &$route); } \ No newline at end of file diff --git a/src/Pecee/Http/Request.php b/src/Pecee/Http/Request.php index 9e7185c..11ac53e 100644 --- a/src/Pecee/Http/Request.php +++ b/src/Pecee/Http/Request.php @@ -3,184 +3,205 @@ namespace Pecee\Http; use Pecee\Http\Input\Input; -class Request { +class Request +{ + protected $data = array(); + protected $headers; + protected $host; + protected $uri; + protected $method; + protected $input; - protected $data = array(); - protected $headers; - protected $host; - protected $uri; - protected $method; - protected $input; + public function __construct() + { + $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'))); + } - public function __construct() { - $this->parseHeaders(); - $this->input = new Input($this); + protected function parseHeaders() + { + $this->headers = array(); - $this->host = $this->getHeader('http_host');; - $this->uri = $this->getHeader('request_uri'); - $this->method = strtolower($this->input->post->findFirst('_method', $this->getHeader('request_method'))); - } + foreach ($_SERVER as $name => $value) { + $this->headers[strtolower($name)] = $value; + $this->headers[strtolower(str_replace('_', '-', $name))] = $value; + } + } - protected function parseHeaders() { - $this->headers = array(); + public function isSecure() + { + if ($this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443) { + return true; + } - foreach ($_SERVER as $name => $value) { - $this->headers[strtolower($name)] = $value; - } - } + return false; + } - public function isSecure() { - if($this->getHeader('http_x_forwarded_proto') === 'https') { - return true; - } + /** + * @return string + */ + public function getUri() + { + return $this->uri; + } - if($this->getHeader('https') !== null) { - return true; - } + /** + * @return string + */ + public function getHost() + { + return $this->host; + } - return ($this->getHeader('server_port') === 443); - } + /** + * @return string + */ + public function getMethod() + { + return $this->method; + } - /** - * @return string - */ - public function getUri() { - return $this->uri; - } + /** + * Get http basic auth user + * @return string|null + */ + public function getUser() + { + return $this->getHeader('php-auth-user'); + } - /** - * @return string - */ - public function getHost() { - return $this->host; - } + /** + * Get http basic auth password + * @return string|null + */ + public function getPassword() + { + return $this->getHeader('php-auth-pw'); + } - /** - * @return string - */ - public function getMethod() { - return $this->method; - } + /** + * Get all headers + * @return array + */ + public function getHeaders() + { + return $this->headers; + } - /** - * Get http basic auth user - * @return string|null - */ - public function getUser() { - return $this->getHeader('php_auth_user'); - } + /** + * Get id address + * @return string + */ + public function getIp() + { + if ($this->getHeader('http-cf-connecting-ip') !== null) { + return $this->getHeader('http-cf-connecting-ip'); + } - /** - * Get http basic auth password - * @return string|null - */ - public function getPassword() { - return $this->getHeader('php_auth_pw'); - } + if ($this->getHeader('http-x-forwarded-for') !== null && strlen($this->getHeader('http-x-forwarded-for'))) { + return $this->getHeader('http-x-forwarded_for'); + } - /** - * Get all headers - * @return array - */ - public function getHeaders() { - return $this->headers; - } + return $this->getHeader('remote-addr'); + } - /** - * Get id address - * @return string - */ - public function getIp() { - if($this->getHeader('http_cf_connecting_ip') !== null) { - return $this->getHeader('http_cf_connecting_ip'); - } + /** + * Get referer + * @return string + */ + public function getReferer() + { + return $this->getHeader('http-referer'); + } - if($this->getHeader('http_x_forwarded_for') !== null && strlen($this->getHeader('http_x_forwarded_for'))) { - return $this->getHeader('http_x_forwarded_for'); - } + /** + * Get user agent + * @return string + */ + public function getUserAgent() + { + return $this->getHeader('http-user-agent'); + } - return $this->getHeader('remote_addr'); - } + /** + * Get header value by name + * + * @param string $name + * @param object|null $defaultValue + * + * @return string|null + */ + public function getHeader($name, $defaultValue = null) + { + return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue; + } - /** - * Get referer - * @return string - */ - public function getReferer() { - return $this->getHeader('http_referer'); - } + /** + * Get input class + * @return Input + */ + public function getInput() + { + return $this->input; + } - /** - * Get user agent - * @return string - */ - public function getUserAgent() { - return $this->getHeader('http_user_agent'); - } + /** + * Is format accepted + * + * @param string $format + * + * @return bool + */ + public function isFormatAccepted($format) + { + return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) > -1); + } - /** - * Get header value by name - * @param string $name - * @param object|null $defaultValue - * @return string|null - */ - public function getHeader($name, $defaultValue = null) { - return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue; - } + /** + * Get accept formats + * @return array + */ + public function getAcceptFormats() + { + return explode(',', $this->getHeader('http-accept')); + } - /** - * Get input class - * @return Input - */ - public function getInput() { - return $this->input; - } + /** + * @param string $uri + */ + public function setUri($uri) + { + $this->uri = $uri; + } - /** - * Is format accepted - * @param string $format - * @return bool - */ - public function isFormatAccepted($format) { - return ($this->getHeader('http_accept') !== null && stripos($this->getHeader('http_accept'), $format) > -1); - } + /** + * @param string $host + */ + public function setHost($host) + { + $this->host = $host; + } - /** - * Get accept formats - * @return array - */ - public function getAcceptFormats() { - return explode(',', $this->getHeader('http_accept')); - } + /** + * @param string $method + */ + public function setMethod($method) + { + $this->method = $method; + } - /** - * @param string $uri - */ - public function setUri($uri) { - $this->uri = $uri; - } + public function __set($name, $value = null) + { + $this->data[$name] = $value; + } - /** - * @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) { - $this->data[$name] = $value; - } - - public function __get($name) { - return isset($this->data[$name]) ? $this->data[$name] : null; - } + public function __get($name) + { + return isset($this->data[$name]) ? $this->data[$name] : null; + } } \ No newline at end of file diff --git a/src/Pecee/Http/Response.php b/src/Pecee/Http/Response.php index a1a3106..b50918e 100644 --- a/src/Pecee/Http/Response.php +++ b/src/Pecee/Http/Response.php @@ -1,109 +1,118 @@ request = $request; + } - public function __construct(Request $request) { - $this->request = $request; - } + /** + * Set the http status code + * + * @param int $code + * @return static + */ + public function httpCode($code) + { + http_response_code($code); + return $this; + } - /** - * Set the http status code - * - * @param int $code - * @return static - */ - public function httpCode($code) { - http_response_code($code); - return $this; - } + /** + * Redirect the response + * + * @param string $url + * @param int $httpCode + */ + public function redirect($url, $httpCode = null) + { + if ($httpCode !== null) { + $this->httpCode($httpCode); + } - /** - * Redirect the response - * - * @param string $url - * @param int $httpCode - */ - public function redirect($url, $httpCode = null) { - if($httpCode !== null) { - $this->httpCode($httpCode); - } + $this->header('location: ' . $url); + die(); + } - $this->header('location: ' . $url); - die(); - } + public function refresh() + { + $this->redirect($this->request->getUri()); + } - public function refresh() { - $this->redirect($this->request->getUri()); - } + /** + * Add http authorisation + * @param string $name + * @return static + */ + public function auth($name = '') + { + $this->headers([ + 'WWW-Authenticate: Basic realm="' . $name . '"', + 'HTTP/1.0 401 Unauthorized' + ]); + return $this; + } - /** - * Add http authorisation - * @param string $name - * @return static - */ - public function auth($name = '') { - $this->headers([ - 'WWW-Authenticate: Basic realm="' . $name . '"', - 'HTTP/1.0 401 Unauthorized' - ]); - return $this; - } + public function cache($eTag, $lastModified = 2592000) + { - public function cache($eTag, $lastModified = 2592000) { + $this->headers([ + 'Cache-Control: public', + 'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT', + 'Etag: ' . $eTag + ]); - $this->headers([ - 'Cache-Control: public', - 'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT', - 'Etag: ' . $eTag - ]); + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $lastModified || + isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $eTag + ) { - if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $lastModified || - isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $eTag) { + $this->headers([ + 'HTTP/1.1 304 Not Modified' + ]); - $this->headers([ - 'HTTP/1.1 304 Not Modified' - ]); + exit(); + } - exit(); - } + return $this; + } - return $this; - } + /** + * Json encode array + * @param array $value + */ + public function json(array $value) + { + $this->header('Content-type: application/json'); + echo json_encode($value); + die(); + } - /** - * Json encode array - * @param array $value - */ - public function json(array $value) { - $this->header('Content-type: application/json'); - echo json_encode($value); - die(); - } + /** + * Add header to response + * @param string $value + * @return static + */ + public function header($value) + { + header($value); + return $this; + } - /** - * Add header to response - * @param string $value - * @return static - */ - public function header($value) { - header($value); - return $this; - } - - /** - * Add multiple headers to response - * @param array $headers - * @return static - */ - public function headers(array $headers) { - foreach($headers as $header) { - header($header); - } - return $this; - } + /** + * Add multiple headers to response + * @param array $headers + * @return static + */ + public function headers(array $headers) + { + foreach ($headers as $header) { + header($header); + } + return $this; + } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/IControllerRoute.php b/src/Pecee/SimpleRouter/IControllerRoute.php index fae219d..242deeb 100644 --- a/src/Pecee/SimpleRouter/IControllerRoute.php +++ b/src/Pecee/SimpleRouter/IControllerRoute.php @@ -1,11 +1,13 @@ url; + } - public function getUrl() { - return $this->url; - } + /** + * Set url + * + * @param string $url + * @return static + */ + public function setUrl($url) + { + $this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/'; - /** - * Set url - * - * @param string $url - * @return static - */ - public function setUrl($url) { - $this->url = '/' . trim($url, '/') . '/'; + if (preg_match_all('/' . static::PARAMETERS_REGEX_MATCH . '/is', $this->url, $matches)) { + if (count($matches[1]) > 0) { + foreach ($matches[1] as $key) { + $this->parameters[$key] = null; + } + } + } - if(preg_match_all('/' . static::PARAMETERS_REGEX_MATCH . '/is', $this->url, $matches)) { - if (count($matches[1])) { - foreach ($matches[1] as $key) { - $this->parameters[$key] = null; - } - } - } + return $this; + } - return $this; - } + /** + * Get alias for the url which can be used when getting the url route. + * @return string|array + */ + public function getAlias() + { + return $this->alias; + } - /** - * Get alias for the url which can be used when getting the url route. - * @return string|array - */ - public function getAlias(){ - return $this->alias; - } + /** + * Check if route has given alias. + * + * @param string $name + * @return bool + */ + public function hasAlias($name) + { + if ($this->getAlias() !== null) { + if (is_array($this->getAlias()) === true) { + foreach ($this->getAlias() as $alias) { + if (strtolower($alias) === strtolower($name)) { + return true; + } + } + } + return strtolower($this->getAlias()) === strtolower($name); + } - /** - * Check if route has given alias. - * - * @param string $name - * @return bool - */ - public function hasAlias($name) { - if ($this->getAlias() !== null) { - if (is_array($this->getAlias())) { - foreach ($this->getAlias() as $alias) { - if (strtolower($alias) === strtolower($name)) { - return true; - } - } - } - return strtolower($this->getAlias()) === strtolower($name); - } + return false; + } - return false; - } + /** + * Set the url alias for easier getting the url route. + * @param string|array $alias + * @return static + */ + public function setAlias($alias) + { + $this->alias = $alias; + return $this; + } - /** - * Set the url alias for easier getting the url route. - * @param string|array $alias - * @return static - */ - public function setAlias($alias){ - $this->alias = $alias; - return $this; - } + public function setData(array $settings) + { - public function setData(array $settings) { + // Change as to alias + if (isset($settings['as'])) { + $this->setAlias($settings['as']); + } - // Change as to alias - if(isset($settings['as'])) { - $this->setAlias($settings['as']); - } - - return parent::setData($settings); - } + return parent::setData($settings); + } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/RouterBase.php b/src/Pecee/SimpleRouter/RouterBase.php index 7d8f57b..67cad5a 100644 --- a/src/Pecee/SimpleRouter/RouterBase.php +++ b/src/Pecee/SimpleRouter/RouterBase.php @@ -7,551 +7,554 @@ use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Request; use Pecee\Http\Response; -class RouterBase { - - protected static $instance; - - /** - * Current request - * @var Request - */ - protected $request; - - /** - * Response - * @var Response - */ - protected $response; - - /** - * Used to keep track of whether or not a should should be added to - * the backstack-list for group-processing or not. - * @var bool - */ - protected $processingRoute; - - /** - * All added routes - * @var array - */ - protected $routes; - - /** - * 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; - - /** - * 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 endless-looping) - * @var array - */ - protected $routeRewrites = array(); - - protected $originalUrl; - - /** - * Get current router instance - * @return static - */ - public static function getInstance() { - if(static::$instance === null) { - static::$instance = new static(); - } - - return static::$instance; - } - - public function __construct() { - $this->reset(); - } - - public function reset() { - $this->processingRoute = false; - $this->request = new Request(); - $this->response = new Response($this->request); - $this->routes = array(); - $this->bootManagers = array(); - $this->backStack = array(); - $this->controllerUrlMap = array(); - $this->exceptionHandlers = array(); - } - - /** - * Add route - * @param RouterEntry $route - * @return RouterEntry - */ - public function addRoute(RouterEntry $route) { - if($this->processingRoute) { - $this->backStack[] = $route; - } else { - $this->routes[] = $route; - } - - return $route; - } - - protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), RouterEntry $parent = null) { - // Loop through each route-request - - /* @var $route RouterEntry */ - for($i = 0; $i < count($routes); $i++) { - - $route = $routes[$i]; - - $route->setData($settings); - - if($parent !== null) { - - if($parent instanceof RouterGroup) { - if ($parent->getPrefix() !== null && trim($parent->getPrefix(), '/') !== '') { - $prefixes[] = trim($parent->getPrefix(), '/'); - } - } - - $route->setParent($parent); - } - - if($route->getNamespace() === null && $this->defaultNamespace !== null) { - $namespace = $this->defaultNamespace; - if ($route->getNamespace()) { - $namespace .= '\\' . $route->getNamespace(); - } - - $route->setNamespace($namespace); - } - - if($route instanceof ILoadableRoute) { +class RouterBase +{ + + protected static $instance; + + protected $parameterModifiers = '{}'; + protected $parameterOptionalSymbol = '?'; + + /** + * Current request + * @var Request + */ + protected $request; + + /** + * Response + * @var Response + */ + protected $response; + + /** + * Used to keep track of whether or not a should should be added to + * the backstack-list for group-processing or not. + * @var bool + */ + protected $processingRoute; + + /** + * All added routes + * @var array + */ + protected $routes; + + /** + * List of + * @var array + */ + protected $controllerUrlMap; + + /** + * Backstack array used to keep track of sub-routes + * @var array + */ + protected $backStack; + + /** + * List of added bootmanagers + * @var array + */ + protected $bootManagers; + + /** + * 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 endless-looping) + * @var array + */ + protected $routeRewrites = []; + + /** + * If the route has been rewritten/changed this property will contain the original url. + * @var string + */ + protected $originalUrl; + + /** + * Get current router instance + * @return static + */ + public static function getInstance() + { + if (static::$instance === null) { + static::$instance = new static(); + } + + return static::$instance; + } + + public function __construct() + { + $this->reset(); + } + + public function reset() + { + $this->processingRoute = false; + $this->request = new Request(); + $this->response = new Response($this->request); + $this->routes = array(); + $this->bootManagers = array(); + $this->backStack = array(); + $this->controllerUrlMap = array(); + $this->exceptionHandlers = array(); + } + + /** + * Add route + * @param RouterEntry $route + * @return RouterEntry + */ + public function addRoute(RouterEntry $route) + { + if ($this->processingRoute) { + $this->backStack[] = $route; + } else { + $this->routes[] = $route; + } + + return $route; + } + + protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), RouterEntry $parent = null) { + + $mergedSettings = []; + + // Loop through each route-request + /* @var $route RouterEntry */ + foreach ($routes as $route) { + + $route->setData($settings); + + if ($parent !== null) { + $route->setParent($parent); + } + + if ($route instanceof ILoadableRoute) { + + if($parent !== null && count($prefixes)) { + $route->setUrl(trim(join('/', $prefixes) . $route->getUrl(), '/')); + } + + $this->controllerUrlMap[] = $route; + + } elseif ($route instanceof RouterGroup) { + + if ($route->getPrefix() !== null && trim($route->getPrefix(), '/') !== '') { + $prefixes[] = trim($route->getPrefix(), '/'); + } + + if ($route->getCallback() !== null && is_callable($route->getCallback())) { + + $this->processingRoute = true; + $route->renderRoute($this->request); + $this->processingRoute = false; + + if ($route->matchRoute($this->request)) { + + $mergedSettings = array_merge($mergedSettings, $route->getMergeableData()); + + // Add ExceptionHandler + if (count($route->getExceptionHandlers()) > 0) { + $this->exceptionHandlers = array_merge($route->getExceptionHandlers(), + $this->exceptionHandlers); + } + } + } + } - $route->setUrl( trim(join('/', $prefixes) . $route->getUrl(), '/') ); - $this->controllerUrlMap[] = $route; - } elseif($route instanceof RouterGroup) { + if (count($this->backStack) > 0) { + $backStack = $this->backStack; + $this->backStack = array(); - if ($route->getCallback() !== null && is_callable($route->getCallback())) { - $this->processingRoute = true; - $route->renderRoute($this->request); - $this->processingRoute = false; + // Route any routes added to the backstack + $this->processRoutes($backStack, $mergedSettings, $prefixes, $route); + } - if ($route->matchRoute($this->request)) { - $settings = array_merge($settings, $route->getMergeableData()); + } + } - // Add ExceptionHandler - if (count($route->getExceptionHandlers())) { - $this->exceptionHandlers = array_merge($route->getExceptionHandlers(), $this->exceptionHandlers); - } - } + public function routeRequest($rewrite = false) + { + $this->loadedRoute = null; + $routeNotAllowed = false; - } - } + try { - if(count($this->backStack)) { - $backStack = $this->backStack; - $this->backStack = array(); + // Initialize boot-managers + if (count($this->bootManagers) > 0) { + /* @var $manager RouterBootManager */ + foreach ($this->bootManagers as $manager) { + $this->request = $manager->boot($this->request); - // Route any routes added to the backstack - $this->processRoutes($backStack, $settings, $prefixes, $route); - } + if (!($this->request instanceof Request)) { + throw new RouterException('Custom router bootmanager "' . get_class($manager) . '" must return instance of Request.'); + } + } + } - $prefixes = []; - } - } + if ($rewrite === false) { - public function routeRequest($rewrite = false) { + // Loop through each route-request + $this->processRoutes($this->routes); - $this->loadedRoute = null; - $routeNotAllowed = false; + if ($this->csrfVerifier !== null) { - try { + // Verify csrf token for request + $this->csrfVerifier->handle($this->request); + } - // Initialize boot-managers - if(count($this->bootManagers)) { - /* @var $manager RouterBootManager */ - foreach($this->bootManagers as $manager) { - $this->request = $manager->boot($this->request); + $this->originalUrl = $this->request->getUri(); + } - if(!($this->request instanceof Request)) { - throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.'); - } - } - } + /* @var $route RouterEntry */ + foreach ($this->controllerUrlMap as $route) { - if($rewrite === false) { + if ($route->matchRoute($this->request)) { - // Loop through each route-request - $this->processRoutes($this->routes); + if (count($route->getRequestMethods()) > 0 && !in_array($this->request->getMethod(), $route->getRequestMethods())) { + $routeNotAllowed = true; + continue; + } - if($this->csrfVerifier !== null) { + $this->loadedRoute = $route; + $this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute); - // Verify csrf token for request - $this->csrfVerifier->handle($this->request); - } + if ($this->request->getUri() !== $this->originalUrl && !in_array($this->request->getUri(), $this->routeRewrites)) { + $this->routeRewrites[] = $this->request->getUri(); + $this->routeRequest(true); + return; + } - $this->originalUrl = $this->request->getUri(); - } + $routeNotAllowed = false; + $this->request->setUri($this->originalUrl); + $this->loadedRoute->renderRoute($this->request); - /* @var $route RouterEntry */ - for ($i = 0; $i < count($this->controllerUrlMap); $i++) { + break; + } + } - $route = $this->controllerUrlMap[$i]; + } catch (\Exception $e) { + $this->handleException($e); + } - if ($route->matchRoute($this->request)) { + if ($routeNotAllowed) { + $this->handleException(new RouterException('Route or method not allowed', 403)); + } - if (count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) { - $routeNotAllowed = true; - continue; - } + if ($this->loadedRoute === null) { + $this->handleException(new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404)); + } + } - $this->loadedRoute = $route; - $this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute); + protected function handleException(\Exception $e) + { + /* @var $handler IExceptionHandler */ + foreach ($this->exceptionHandlers as $handler) { - if($this->request->getUri() !== $this->originalUrl && !in_array($this->request->getUri(), $this->routeRewrites)) { - $this->routeRewrites[] = $this->request->getUri(); - $this->routeRequest(true); - return; - } + $handler = new $handler(); - $routeNotAllowed = false; - $this->request->setUri($this->originalUrl); - $this->loadedRoute->renderRoute($this->request); + if (!($handler instanceof IExceptionHandler)) { + throw new RouterException('Exception handler must implement the IExceptionHandler interface.'); + } - break; - } - } + $request = $handler->handleError($this->request, $this->loadedRoute, $e); - } catch(\Exception $e) { - $this->handleException($e); - } + if ($request !== null && $request->getUri() !== $this->originalUrl && !in_array($request->getUri(), $this->routeRewrites)) { + $this->routeRewrites[] = $request->getUri(); + $this->routeRequest(true); + return; + } - if($routeNotAllowed) { - $this->handleException(new RouterException('Route or method not allowed', 403)); - } + } - if($this->loadedRoute === null) { - $this->handleException(new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404)); - } - } + throw $e; + } - protected function handleException(\Exception $e) { + public function arrayToParams(array $getParams = null, $includeEmpty = true) + { + if (is_array($getParams) === true && count($getParams) > 0) { - /* @var $handler IExceptionHandler */ - foreach ($this->exceptionHandlers as $handler) { - $handler = new $handler(); + if ($includeEmpty === false) { + $getParams = array_filter($getParams, function ($item) { + return (!empty($item)); + }); + } - if (!($handler instanceof IExceptionHandler)) { - throw new RouterException('Exception handler must implement the IExceptionHandler interface.'); - } + return '?' . http_build_query($getParams); + } - $request = $handler->handleError($this->request, $this->loadedRoute, $e); + return ''; + } - if($request !== null && $request->getUri() !== $this->originalUrl && !in_array($request->getUri(), $this->routeRewrites)) { - $this->routeRewrites[] = $request->getUri(); - $this->routeRequest(true); - return; - } + protected function processUrl(LoadableRoute $route, $method = null, $parameters = null, $getParams = null) + { + $domain = ''; + $parent = $route->getParent(); - } + $parameters = (array)$parameters; - throw $e; - } + if ($parent !== null && $parent instanceof RouterGroup && count($parent->getDomains()) > 0) { + $domain = $parent->getDomains(); + $domain = '//' . $domain[0]; + } - public function arrayToParams(array $getParams = null, $includeEmpty = true) { + $url = $domain . '/' . trim($route->getUrl(), '/'); - if(is_array($getParams) && count($getParams)) { - if ($includeEmpty === false) { - $getParams = array_filter($getParams, function ($item) { - return (!empty($item)); - }); - } + if ($route instanceof IControllerRoute && $method !== null) { - return '?' . http_build_query($getParams); - } + $url .= '/' . $method . '/'; - return ''; - } + if (count($parameters) > 0) { + $url .= join('/', (array)$parameters); + } - protected function processUrl(RouterRoute $route, $method = null, $parameters = null, $getParams = null) { + } else { - $domain = ''; + if ($parameters !== null && count($parameters) > 0) { + $params = array_merge($route->getParameters(), (array)$parameters); + } else { + $params = $route->getParameters(); + } - $parent = $route->getParent(); + $otherParams = array(); - if($parent !== null && $parent instanceof RouterGroup && count($parent->getDomains())) { - $domain = $parent->getDomains(); - $domain = '//' . $domain[0]; - } + foreach ($params as $param => $value) { + $value = (isset($parameters[$param])) ? $parameters[$param] : $value; - $url = $domain . '/' . trim($route->getUrl(), '/'); + $param1 = $this->parameterModifiers[0] . $param . $this->parameterModifiers[1]; + $param2 = $this->parameterModifiers[0] . $param . $this->parameterOptionalSymbol . $this->parameterModifiers[1]; - if($route instanceof IControllerRoute && $method !== null) { - $url .= $method; + if (stripos($url, $param1) !== false || stripos($url, $param) !== false) { + $url = str_ireplace([$param1, $param2], $value, $url); + } else { + $otherParams[$param] = $value; + } + } - if(count($parameters)) { - $url .= join('/', $parameters); - } - } else { - if($parameters !== null && is_array($parameters)) { - $params = array_merge($route->getParameters(), $parameters); - } else { - $params = $route->getParameters(); - } + $url = rtrim($url, '/') . '/' . join('/', $otherParams); + } - $otherParams = array(); - $i = 0; + $url = rtrim($url, '/') . '/'; - foreach($params as $param => $value) { - $value = (isset($parameters[$param])) ? $parameters[$param] : $value; - if(stripos($url, '{' . $param. '}') !== false || stripos($url, '{' . $param . '?}') !== false) { - $url = str_ireplace(array('{' . $param . '}', '{' . $param . '?}'), $value, $url); - } else { - $otherParams[$param] = $value; - } - $i++; - } + if ($getParams !== null) { + $url .= $this->arrayToParams($getParams); + } - $url = rtrim($url, '/') . '/' . join('/', $otherParams); - } + return $url; + } - $url = rtrim($url, '/') . '/'; + /** + * Find route by alias, class, callback or method. + * + * @param string $query + * @return LoadableRoute|null + */ + public function findRoute($query) + { + /* @var $route LoadableRoute */ + foreach ($this->controllerUrlMap as $route) { - if($getParams !== null) { - $url .= $this->arrayToParams($getParams); - } + // Check an alias exist, if the matches - use it + // Matches either Router alias or controller alias. + if ($route->hasAlias($query)) { + return $route; + } - return $url; - } + // Direct match to controller + if ($route instanceof IControllerRoute) { + if (strtolower($route->getController()) === strtolower($query)) { + return $route; + } + } - public function getRoute($controller = null, $parameters = null, $getParams = null) { + // Using @ is most definitely a controller@method or alias@method + if (strpos($query, '@') !== false) { + list($controller, $method) = array_map('strtolower', explode('@', $query)); - if($parameters !== null && !is_array($parameters)) { - throw new \InvalidArgumentException('Invalid type for parameter. Must be array or null'); - } - - if($getParams !== null && !is_array($getParams)) { - throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null'); - } - - // Return current route if no options has been specified - if($controller === null && $parameters === null) { - $getParams = ($getParams !== null && is_array($getParams)) ? array_merge($_GET, $getParams) : $_GET; - - $url = parse_url($this->request->getUri(), PHP_URL_PATH); - - if($getParams !== null) { - $url .= $this->arrayToParams($getParams); - } - - return $url; - } - - 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 */ - for($i = 0; $i < $max; $i++) { - - $route = $this->controllerUrlMap[$i]; - - // Check an alias exist, if the matches - use it - if($route instanceof LoadableRoute) { - - // Check for alias - if ($route->hasAlias($controller)) { - return $this->processUrl($route, $route->getMethod(), $parameters, $getParams); - } - - // Use controller name - if($route instanceof RouterController) { - $c = $route->getController(); - } else { - - // Use callback if it's not a function - if (stripos($route->getCallback(), '@') !== false && !is_callable($route->getCallback())) { - $c = $route->getCallback(); - } - - } - - } - - if($c === $controller || strpos($c, $controller) === 0) { - return $this->processUrl($route, $route->getMethod(), $parameters, $getParams); - } - } - - $c = ''; - - // No match has yet been found, let's try to guess what url that should be returned - for($i = 0; $i < $max; $i++) { - - $route = $this->controllerUrlMap[$i]; - - if($route instanceof IControllerRoute) { - $c = $route->getController(); - } else if(!is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) { - $c = $route->getClass(); - } - - if(stripos($controller, '@') !== false) { - $tmp = explode('@', $controller); - $controller = $tmp[0]; - $method = $tmp[1]; - } - - if($controller === $c) { - return $this->processUrl($route, $method, $parameters, $getParams); - } - } - - $controller = ($controller === null) ? '/' : $controller; - $url = array($controller); - - if($parameters !== null && is_array($parameters) && count($parameters)) { - $url = array_merge($url, $parameters); - } - - $url = '/' . trim(join('/', $url), '/') . '/'; - - if($getParams !== null) { - $url .= $this->arrayToParams($getParams); - } - - return $url; - } - - /** - * Get default namespace - * @return string - */ - public function getDefaultNamespace(){ - return $this->defaultNamespace; - } - - /** - * Set the main default namespace that all routes will inherit - * @param string $defaultNamespace - * @return static - */ - public function setDefaultNamespace($defaultNamespace) { - $this->defaultNamespace = $defaultNamespace; - return $this; - } - - /** - * Get bootmanagers - * @return array - */ - public function getBootManagers() { - return $this->bootManagers; - } - - /** - * 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 array - */ - public function getRoutes(){ - return $this->routes; - } - - /** - * Get current request - * - * @return Request - */ - public function getRequest() { - return $this->request; - } - - /** - * Get response - * @return Response - */ - public function getResponse() { - return $this->response; - } - - /** - * Get csrf verifier class - * @return BaseCsrfVerifier - */ - public function getCsrfVerifier() { - return $this->csrfVerifier; - } - - /** - * Set csrf verifier class - * - * @param BaseCsrfVerifier $csrfVerifier - * @return static - */ - public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier) { - $this->csrfVerifier = $csrfVerifier; - return $this; - } - - /** - * Get loaded route - * @return RouterRoute|null - */ - public function getLoadedRoute() { - return $this->loadedRoute; - } + if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) { + return $route; + } + } + + // Use callback if it's not a function + if (strpos($query, '@') !== false && strpos($route->getCallback(), '@') !== false && !is_callable($route->getCallback())) { + + if (strtolower($query) === strtolower($route->getClass())) { + return $route; + } + + if (strtolower($route->getCallback()) === strtolower($query) || strpos($route->getCallback(), $query) === 0) { + return $route; + } + + } + } + + return null; + } + + public function getRoute($controller = null, $parameters = null, $getParams = null) + { + if ($getParams !== null && is_array($getParams) === false) { + throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null'); + } + + // Return current route if no options has been specified + if ($controller === null && $parameters === null) { + + $getParams = ($getParams !== null) ? $getParams : $_GET; + $url = parse_url($this->request->getUri(), PHP_URL_PATH) . $this->arrayToParams($getParams); + + return $url; + } + + // If nothing is defined and a route is loaded we use that + if ($controller === null && $this->loadedRoute !== null) { + return $this->processUrl($this->loadedRoute, $this->loadedRoute->getMethod(), $parameters, $getParams); + } + + $route = $this->findRoute($controller); + + if ($route !== null) { + return $this->processUrl($route, $route->getMethod(), $parameters, $getParams); + } + + // Using @ is most definitely a controller@method or alias@method + if (stripos($controller, '@') !== false) { + list($controller, $method) = explode('@', $controller); + + /* @var $route LoadableRoute */ + foreach ($this->controllerUrlMap as $route) { + + if ($route->hasAlias($controller)) { + return $this->processUrl($route, $method, $parameters, $getParams); + } + + // Match controllers either by: "alias @ method" or "controller@method" + if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($controller)) { + return $this->processUrl($route, $method, $parameters, $getParams); + } + + } + } + + $url = [($controller === null) ? '/' : $controller]; + + if ($parameters !== null && count($parameters) > 0) { + $url = array_merge($url, (array)$parameters); + } + + $url = '/' . trim(join('/', $url), '/') . '/'; + + if ($getParams !== null) { + $url .= $this->arrayToParams($getParams); + } + + return $url; + } + + /** + * Get bootmanagers + * @return array + */ + public function getBootManagers() + { + return $this->bootManagers; + } + + /** + * 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 array + */ + public function getRoutes() + { + return $this->routes; + } + + /** + * Get current request + * + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get response + * @return Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Get csrf verifier class + * @return BaseCsrfVerifier + */ + public function getCsrfVerifier() + { + return $this->csrfVerifier; + } + + /** + * Set csrf verifier class + * + * @param BaseCsrfVerifier $csrfVerifier + * @return static + */ + public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier) + { + $this->csrfVerifier = $csrfVerifier; + return $this; + } + + /** + * Get loaded route + * @return RouterRoute|null + */ + public function getLoadedRoute() + { + return $this->loadedRoute; + } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/RouterBootManager.php b/src/Pecee/SimpleRouter/RouterBootManager.php index 0358972..5419f70 100644 --- a/src/Pecee/SimpleRouter/RouterBootManager.php +++ b/src/Pecee/SimpleRouter/RouterBootManager.php @@ -3,8 +3,7 @@ namespace Pecee\SimpleRouter; use Pecee\Http\Request; -abstract class RouterBootManager { - - abstract public function boot(Request $request); - +abstract class RouterBootManager +{ + abstract public function boot(Request $request); } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/RouterController.php b/src/Pecee/SimpleRouter/RouterController.php index 8d07f65..83e9332 100644 --- a/src/Pecee/SimpleRouter/RouterController.php +++ b/src/Pecee/SimpleRouter/RouterController.php @@ -4,100 +4,107 @@ namespace Pecee\SimpleRouter; use Pecee\Exception\RouterException; use Pecee\Http\Request; -class RouterController extends LoadableRoute implements IControllerRoute { +class RouterController extends LoadableRoute implements IControllerRoute +{ + protected $defaultMethod = 'index'; + protected $controller; + protected $method; - const DEFAULT_METHOD = 'index'; + public function __construct($url, $controller) + { + $this->setUrl($url); + $this->controller = $controller; + } - protected $controller; - protected $method; + public function renderRoute(Request $request) + { + if ($this->getCallback() !== null && is_callable($this->getCallback())) { - public function __construct($url, $controller) { - $this->setUrl($url); - $this->controller = $controller; - } + // When the callback is a function + call_user_func_array($this->getCallback(), $this->getParameters()); + } else { + // When the callback is a method + $controller = explode('@', $this->getCallback()); + $className = $this->getNamespace() . '\\' . $controller[0]; - public function renderRoute(Request $request) { - if($this->getCallback() !== null && is_callable($this->getCallback())) { + $class = $this->loadClass($className); + $method = $request->getMethod() . ucfirst($controller[1]); - // When the callback is a function - call_user_func_array($this->getCallback(), $this->getParameters()); - } else { - // When the callback is a method - $controller = explode('@', $this->getCallback()); - $className = $this->getNamespace() . '\\' . $controller[0]; + if (!method_exists($class, $method)) { + throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404); + } - $class = $this->loadClass($className); - $method = $request->getMethod() . ucfirst($controller[1]); + call_user_func_array(array($class, $method), $this->getParameters()); - if (!method_exists($class, $method)) { - throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404); - } + return $class; + } - call_user_func_array(array($class, $method), $this->getParameters()); + return null; + } - return $class; - } + public function matchRoute(Request $request) + { + $url = parse_url(urldecode($request->getUri()), PHP_URL_PATH); + $url = rtrim($url, '/') . '/'; - return null; - } + if (strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) { - public function matchRoute(Request $request) { - $url = parse_url(urldecode($request->getUri()), PHP_URL_PATH); - $url = rtrim($url, '/') . '/'; + $strippedUrl = trim(str_ireplace($this->url, '/', $url), '/'); - if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) { + $path = explode('/', $strippedUrl); - $strippedUrl = trim(str_ireplace($this->url, '/', $url), '/'); + if (count($path) > 0) { - $path = explode('/', $strippedUrl); + $method = (!isset($path[0]) || trim($path[0]) === '') ? $this->defaultMethod : $path[0]; + $this->method = $method; - if(count($path)) { + array_shift($path); + $this->parameters = $path; - $method = (!isset($path[0]) || trim($path[0]) === '') ? static::DEFAULT_METHOD : $path[0]; - $this->method = $method; + // Set callback + $this->setCallback($this->controller . '@' . $this->method); - array_shift($path); - $this->parameters = $path; + return true; + } + } - // Set callback - $this->setCallback($this->controller . '@' . $this->method); + return null; + } - return true; - } - } - return null; - } + /** + * @return string + */ + public function getController() + { + return $this->controller; + } - /** - * @return string - */ - public function getController() { - return $this->controller; - } + /** + * @param string $controller + * @return static + */ + public function setController($controller) + { + $this->controller = $controller; + return $this; + } - /** - * @param string $controller - * @return static - */ - public function setController($controller) { - $this->controller = $controller; - return $this; - } + /** + * @return string + */ + public function getMethod() + { + return $this->method; + } - /** - * @return string - */ - public function getMethod() { - return $this->method; - } - - /** - * @param string $method - * @return static - */ - public function setMethod($method) { - $this->method = $method; - return $this; - } + /** + * @param string $method + * @return static + */ + public function setMethod($method) + { + $this->method = $method; + return $this; + } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/RouterEntry.php b/src/Pecee/SimpleRouter/RouterEntry.php index 56ddc7a..5b5ad89 100644 --- a/src/Pecee/SimpleRouter/RouterEntry.php +++ b/src/Pecee/SimpleRouter/RouterEntry.php @@ -1,405 +1,428 @@ where) && isset($this->where[$parameter])) { - $parameterRegex = $this->where[$parameter]; - } - - if($lastCharacter === '?') { - $parameter = substr($parameter, 0, strlen($parameter)-1); - $regex .= '(?:\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?)?'; - $required = false; - } else { - $regex .= '\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?'; - } - - $parameterNames[] = [ - 'name' => $parameter, - 'required' => $required - ]; - - $parameter = ''; - $isParameter = false; - - } elseif($isParameter) { - $parameter .= $character; - } elseif($character === '/') { - $regex .= '\\' . $character; - } else { - $regex .= str_replace('.', '\\.', $character); - } - - $lastCharacter = $character; - } - - $parameterValues = array(); - - if(preg_match('/^'.$regex.'\/?$/is', $url, $parameterValues)) { - - $parameters = array(); - - $max = count($parameterNames); - - 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; - } - - return $parameters; - } - - return null; - } - - public function loadMiddleware(Request $request, RouterEntry &$route) { - if(count($this->getMiddlewares())) { - foreach($this->getMiddlewares() 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, $route); - } - } - } - - public function renderRoute(Request $request) { - if($this->getCallback() !== null && is_callable($this->getCallback())) { - // When the callback is a function - call_user_func_array($this->getCallback(), $this->getParameters()); - } else { - // When the callback is a method - $controller = explode('@', $this->getCallback()); - $className = $this->getNamespace() . '\\' . $controller[0]; - - $class = $this->loadClass($className); - $method = $controller[1]; - - if (!method_exists($class, $method)) { - throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404); - } - - $parameters = array_filter($this->getParameters(), function($var){ - return ($var !== null); - }); - - call_user_func_array(array($class, $method), $parameters); - - return $class; - } - - return null; - } - - /** - * 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); - } - - /** - * Set allowed request methods - * - * @param array $methods - * @return static $this - */ - public function setRequestMethods(array $methods) { - $this->requestMethods = $methods; - return $this; - } - - /** - * Get allowed request methods - * - * @return array - */ - public function getRequestMethods() { - return $this->requestMethods; - } - - /** - * @return RouterEntry - */ - public function getParent() { - return $this->parent; - } - - /** - * Set parent route - * @param RouterEntry $parent - * @return static $this - */ - public function setParent(RouterEntry $parent) { - $this->parent = $parent; - return $this; - } - - /** - * @param string $callback - * @return static - */ - public function setCallback($callback) { - $this->callback = $callback; - return $this; - } - - /** - * @return mixed - */ - public function getCallback() { - return $this->callback; - } - - public function getMethod() { - if(strpos($this->callback, '@') !== false) { - $tmp = explode('@', $this->callback); - return $tmp[1]; - } - return null; - } - - public function getClass() { - if(strpos($this->callback, '@') !== false) { - $tmp = explode('@', $this->callback); - return $tmp[0]; - } - return null; - } - - public function setMethod($method) { - $this->callback = sprintf('%s@%s', $this->getClass(), $method); - return $this; - } - - public function setClass($class) { - $this->callback = sprintf('%s@%s', $class, $this->getMethod()); - return $this; - } - - /** - * @param string $middleware - * @return static - */ - public function setMiddleware($middleware) { - $this->middlewares[] = $middleware; - return $this; - } - - public function setMiddlewares(array $middlewares) { - $this->middlewares = $middlewares; - return $this; - } - - /** - * @param string $namespace - * @return static - */ - public function setNamespace($namespace) { - $this->namespace = $namespace; - return $this; - } - - /** - * @return string|array - */ - public function getMiddlewares() { - return $this->middlewares; - } - - /** - * @return string - */ - public function getNamespace() { - return $this->namespace; - } - - /** - * @return array - */ - public function getParameters(){ - return $this->parameters; - } - - /** - * @param mixed $parameters - * @return static - */ - public function setParameters($parameters) { - $this->parameters = $parameters; - return $this; - } - - /** - * Add regular expression parameter match - * - * @param array $options - * @return static - */ - public function where(array $options) { - $this->where = $options; - return $this; - } - - /** - * Add regular expression match for url - * - * @param string $regex - * @return static - */ - public function match($regex) { - $this->regex = $regex; - return $this; - } - - /** - * Get arguments that can be inherited by child routes. - * - * @return array - */ - public function getMergeableData() { - - $output = array(); - - if($this->namespace !== null) { - $output['namespace'] = $this->namespace; - } - - if(count($this->middlewares)) { - $output['middleware'] = $this->middlewares; - } - - if(count($this->where)) { - $output['where'] = $this->where; - } - - if(count($this->requestMethods)) { - $output['method'] = $this->requestMethods; - } - - if(count($this->parameters)) { - $output['parameters'] = $this->parameters; - } - - return $output; - } - - /** - * Set arguments/data by array - * - * @param array $settings - * @return static - */ - public function setData(array $settings) { - - if (isset($settings['namespace']) && $this->namespace === null) { - $this->setNamespace($settings['namespace']); - } - - // Push middleware if multiple - if (isset($settings['middleware'])) { - $this->middlewares = array_merge((array)$settings['middleware'], $this->middlewares); - } - - if(isset($settings['method'])) { - $this->setRequestMethods((array)$settings['method']); - } - - if(isset($settings['where'])) { - $this->where($settings['where']); - } - - if(isset($settings['parameters'])) { - $this->setParameters($settings['parameters']); - } - - return $this; - } - - abstract function matchRoute(Request $request); +abstract class RouterEntry +{ + const REQUEST_TYPE_GET = 'get'; + const REQUEST_TYPE_POST = 'post'; + const REQUEST_TYPE_PUT = 'put'; + const REQUEST_TYPE_PATCH = 'patch'; + const REQUEST_TYPE_OPTIONS = 'options'; + const REQUEST_TYPE_DELETE = 'delete'; + + public static $requestTypes = [ + self::REQUEST_TYPE_GET, + self::REQUEST_TYPE_POST, + self::REQUEST_TYPE_PUT, + self::REQUEST_TYPE_PATCH, + self::REQUEST_TYPE_OPTIONS, + self::REQUEST_TYPE_DELETE, + ]; + + protected $parent; + protected $callback; + + protected $namespace; + protected $regex; + protected $requestMethods = array(); + protected $where = array(); + protected $parameters = array(); + protected $middlewares = array(); + + protected function loadClass($name) + { + if (!class_exists($name)) { + throw new RouterException(sprintf('Class %s does not exist', $name)); + } + + return new $name(); + } + + protected function parseParameters($route, $url, $parameterRegex = '[\w]+') + { + $parameterNames = array(); + $regex = ''; + $lastCharacter = ''; + $isParameter = false; + $parameter = ''; + + $routeLength = strlen($route); + for ($i = 0; $i < $routeLength; $i++) { + + $character = $route[$i]; + + if ($character === '{') { + // Remove "/" and "\" from regex + if (substr($regex, strlen($regex) - 1) === '/') { + $regex = substr($regex, 0, strlen($regex) - 2); + } + + $isParameter = true; + } elseif ($isParameter && $character === '}') { + $required = true; + + // Check for optional parameter and use custom parameter regex if it exists + if (is_array($this->where) === true && isset($this->where[$parameter])) { + $parameterRegex = $this->where[$parameter]; + } + + if ($lastCharacter === '?') { + $parameter = substr($parameter, 0, strlen($parameter) - 1); + $regex .= '(?:\/?(?P<' . $parameter . '>' . $parameterRegex . ')[^\/]?)?'; + $required = false; + } else { + $regex .= '\/?(?P<' . $parameter . '>' . $parameterRegex . ')[^\/]?'; + } + + $parameterNames[] = [ + 'name' => $parameter, + 'required' => $required + ]; + + $parameter = ''; + $isParameter = false; + } elseif ($isParameter) { + $parameter .= $character; + } elseif ($character === '/') { + $regex .= '\\' . $character; + } else { + $regex .= str_replace('.', '\\.', $character); + } + + $lastCharacter = $character; + } + + $parameterValues = array(); + + if (preg_match('/^' . $regex . '\/?$/is', $url, $parameterValues)) { + + $parameters = array(); + + foreach ($parameterNames as $name) { + $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'] === false && $parameterValue === null) { + continue; + } + + $parameters[$name['name']] = $parameterValue; + } + + return $parameters; + } + + return null; + } + + public function loadMiddleware(Request $request, RouterEntry &$route) + { + if (count($this->getMiddlewares()) > 0) { + foreach ($this->getMiddlewares() 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, $route); + + } + } + } + + public function renderRoute(Request $request) + { + if ($this->getCallback() !== null && is_callable($this->getCallback())) { + + // When the callback is a function + call_user_func_array($this->getCallback(), $this->getParameters()); + + } else { + + // When the callback is a method + $controller = explode('@', $this->getCallback()); + $className = $this->getNamespace() . '\\' . $controller[0]; + + $class = $this->loadClass($className); + $method = $controller[1]; + + if (!method_exists($class, $method)) { + throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404); + } + + $parameters = array_filter($this->getParameters(), function ($var) { + return ($var !== null); + }); + + call_user_func_array(array($class, $method), $parameters); + + return $class; + } + + return null; + } + + /** + * 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); + } + + /** + * Set allowed request methods + * + * @param array $methods + * @return static $this + */ + public function setRequestMethods(array $methods) + { + $this->requestMethods = $methods; + return $this; + } + + /** + * Get allowed request methods + * @return array + */ + public function getRequestMethods() + { + return $this->requestMethods; + } + + /** + * @return RouterEntry + */ + public function getParent() + { + return $this->parent; + } + + /** + * Set parent route + * + * @param RouterEntry $parent + * @return static $this + */ + public function setParent(RouterEntry $parent) + { + $this->parent = $parent; + return $this; + } + + /** + * @param string $callback + * @return static + */ + public function setCallback($callback) + { + $this->callback = $callback; + return $this; + } + + /** + * @return mixed + */ + public function getCallback() + { + return $this->callback; + } + + public function getMethod() + { + if (strpos($this->callback, '@') !== false) { + $tmp = explode('@', $this->callback); + return $tmp[1]; + } + return null; + } + + public function getClass() + { + if (strpos($this->callback, '@') !== false) { + $tmp = explode('@', $this->callback); + return $tmp[0]; + } + return null; + } + + public function setMethod($method) + { + $this->callback = sprintf('%s@%s', $this->getClass(), $method); + return $this; + } + + public function setClass($class) + { + $this->callback = sprintf('%s@%s', $class, $this->getMethod()); + return $this; + } + + /** + * @param string $middleware + * @return static + */ + public function setMiddleware($middleware) + { + $this->middlewares[] = $middleware; + return $this; + } + + public function setMiddlewares(array $middlewares) + { + $this->middlewares = $middlewares; + return $this; + } + + /** + * @param string $namespace + * @return static + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + return $this; + } + + /** + * @return string|array + */ + public function getMiddlewares() + { + return $this->middlewares; + } + + /** + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @param mixed $parameters + * @return static + */ + public function setParameters($parameters) + { + $this->parameters = $parameters; + return $this; + } + + /** + * Add regular expression parameter match + * + * @param array $options + * @return static + */ + public function where(array $options) + { + $this->where = $options; + return $this; + } + + /** + * Add regular expression match for url + * + * @param string $regex + * @return static + */ + public function match($regex) + { + $this->regex = $regex; + return $this; + } + + /** + * Get arguments that can be inherited by child routes. + * + * @return array + */ + public function getMergeableData() + { + $output = array(); + + if ($this->namespace !== null) { + $output['namespace'] = $this->namespace; + } + + if (count($this->middlewares) > 0) { + $output['middleware'] = $this->middlewares; + } + + if (count($this->where) > 0) { + $output['where'] = $this->where; + } + + if (count($this->requestMethods) > 0) { + $output['method'] = $this->requestMethods; + } + + if (count($this->parameters) > 0) { + $output['parameters'] = $this->parameters; + } + + return $output; + } + + /** + * Set arguments/data by array + * + * @param array $settings + * @return static + */ + public function setData(array $settings) + { + if (isset($settings['namespace']) && $this->namespace === null) { + $this->setNamespace($settings['namespace']); + } + + // Push middleware if multiple + if (isset($settings['middleware'])) { + $this->middlewares = array_merge((array)$settings['middleware'], $this->middlewares); + } + + if (isset($settings['method'])) { + $this->setRequestMethods((array)$settings['method']); + } + + if (isset($settings['where'])) { + $this->where($settings['where']); + } + + if (isset($settings['parameters'])) { + $this->setParameters($settings['parameters']); + } + + return $this; + } + + abstract function matchRoute(Request $request); } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/RouterGroup.php b/src/Pecee/SimpleRouter/RouterGroup.php index a352ac4..a1104f4 100644 --- a/src/Pecee/SimpleRouter/RouterGroup.php +++ b/src/Pecee/SimpleRouter/RouterGroup.php @@ -1,93 +1,98 @@ domains) > 0) { + foreach ($this->domains as $domain) { - public function matchDomain(Request $request) { - if(count($this->domains)) { - for($i = 0; $i < count($this->domains); $i++) { - $domain = $this->domains[$i]; + $parameters = $this->parseParameters($domain, $request->getHost(), '.*'); - $parameters = $this->parseParameters($domain, $request->getHost(), '.*'); + if ($parameters !== null) { + $this->parameters = $parameters; + return true; + } + } - if($parameters !== null) { - $this->parameters = $parameters; - return true; - } - } + return false; + } - return false; - } + return true; + } - return true; - } + public function matchRoute(Request $request) + { + // Skip if prefix doesn't match + if ($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) { + return false; + } - public function matchRoute(Request $request) { - // Skip if prefix doesn't match - if($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) { - return false; - } + return $this->matchDomain($request); + } - return $this->matchDomain($request); - } + public function setExceptionHandlers(array $handlers) + { + $this->exceptionHandlers = $handlers; + return $this; + } - public function setExceptionHandlers(array $handlers) { - $this->exceptionHandlers = $handlers; - return $this; - } + public function getExceptionHandlers() + { + return $this->exceptionHandlers; + } - public function getExceptionHandlers() { - return $this->exceptionHandlers; - } + public function getDomains() + { + return $this->domains; + } - public function getDomains() { - return $this->domains; - } + public function setDomains(array $domains) + { + $this->domains = $domains; + return $this; + } - public function setDomains(array $domains) { - $this->domains = $domains; - return $this; - } + /** + * @param string $prefix + * @return static + */ + public function setPrefix($prefix) + { + $this->prefix = '/' . trim($prefix, '/'); + return $this; + } - /** - * @param string $prefix - * @return static - */ - public function setPrefix($prefix) { - $this->prefix = '/' . trim($prefix, '/'); - return $this; - } + /** + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } - /** - * @return string - */ - public function getPrefix() { - return $this->prefix; - } + public function setData(array $settings) + { + if (isset($settings['prefix'])) { + $this->setPrefix($settings['prefix']); + } - public function setData(array $settings) { + if (isset($settings['exceptionHandler'])) { + $this->setExceptionHandlers((array)$settings['exceptionHandler']); + } - if(isset($settings['prefix'])) { - $this->setPrefix($settings['prefix']); - } + if (isset($settings['domain'])) { + $this->setDomains((array)$settings['domain']); + } - if(isset($settings['exceptionHandler'])) { - $this->setExceptionHandlers((array)$settings['exceptionHandler']); - } - - if(isset($settings['domain'])) { - $this->setDomains((array)$settings['domain']); - } - - return parent::setData($settings); - - } + return parent::setData($settings); + } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/RouterResource.php b/src/Pecee/SimpleRouter/RouterResource.php index fad730b..8847b1c 100644 --- a/src/Pecee/SimpleRouter/RouterResource.php +++ b/src/Pecee/SimpleRouter/RouterResource.php @@ -4,112 +4,120 @@ namespace Pecee\SimpleRouter; use Pecee\Exception\RouterException; use Pecee\Http\Request; -class RouterResource extends LoadableRoute implements IControllerRoute { +class RouterResource extends LoadableRoute implements IControllerRoute +{ + protected $controller; - protected $controller; + public function __construct($url, $controller) + { + $this->setUrl($url); + $this->controller = $controller; + } - public function __construct($url, $controller) { - $this->setUrl($url); - $this->controller = $controller; - } + public function renderRoute(Request $request) + { + if ($this->getCallback() !== null && is_callable($this->getCallback())) { + // When the callback is a function + call_user_func_array($this->getCallback(), $this->getParameters()); + } else { + // When the callback is a method + $controller = explode('@', $this->getCallback()); + $className = $this->getNamespace() . '\\' . $controller[0]; + $class = $this->loadClass($className); + $method = strtolower($controller[1]); - public function renderRoute(Request $request) { - if($this->getCallback() !== null && is_callable($this->getCallback())) { - // When the callback is a function - call_user_func_array($this->getCallback(), $this->getParameters()); - } else { - // When the callback is a method - $controller = explode('@', $this->getCallback()); - $className = $this->getNamespace() . '\\' . $controller[0]; - $class = $this->loadClass($className); - $method = strtolower($controller[1]); + if (!method_exists($class, $method)) { + throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404); + } - if (!method_exists($class, $method)) { - throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404); - } + call_user_func_array([$class, $method], $this->getParameters()); - call_user_func_array(array($class, $method), $this->getParameters()); + return $class; + } - return $class; - } + return null; + } - return null; - } + protected function call($method, $parameters) + { + $this->setCallback($this->controller . '@' . $method); + $this->parameters = $parameters; - protected function call($method, $parameters) { - $this->setCallback($this->controller . '@' . $method); - $this->parameters = $parameters; - return true; - } + return true; + } - public function matchRoute(Request $request) { - $url = parse_url(urldecode($request->getUri()), PHP_URL_PATH); - $url = rtrim($url, '/') . '/'; + public function matchRoute(Request $request) + { + $url = parse_url(urldecode($request->getUri()), PHP_URL_PATH); + $url = rtrim($url, '/') . '/'; - $route = rtrim($this->url, '/') . '/{id?}/{action?}'; + $route = rtrim($this->url, '/') . '/{id?}/{action?}'; - $parameters = $this->parseParameters($route, $url); + $parameters = $this->parseParameters($route, $url); - if($parameters !== null) { + if ($parameters !== null) { - if(is_array($parameters)) { - $parameters = array_merge($this->parameters, $parameters); - } + $parameters = array_merge($this->parameters, (array)$parameters); - $action = isset($parameters['action']) ? $parameters['action'] : null; - unset($parameters['action']); + $action = isset($parameters['action']) ? $parameters['action'] : null; + unset($parameters['action']); - // Delete - if($request->getMethod() === static::REQUEST_TYPE_DELETE && $request->getMethod() === static::REQUEST_TYPE_POST) { - return $this->call('destroy', $parameters); - } + $method = request()->getMethod(); - // Update - 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); - } + // Delete + if (isset($parameters['id']) && $method === static::REQUEST_TYPE_DELETE) { + return $this->call('destroy', $parameters); + } - // Edit - if(isset($action) && strtolower($action) === 'edit' && $request->getMethod() === static::REQUEST_TYPE_GET) { - return $this->call('edit', $parameters); - } + // Update + if (isset($parameters['id']) && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT])) { + return $this->call('update', $parameters); + } - // Create - if(strtolower($action) === 'create' && $request->getMethod() === static::REQUEST_TYPE_GET) { - return $this->call('create', $parameters); - } + // Edit + if (isset($parameters['id']) && strtolower($action) === 'edit' && $method === static::REQUEST_TYPE_GET) { + return $this->call('edit', $parameters); + } - // Save - if($request->getMethod() === static::REQUEST_TYPE_POST) { - return $this->call('store', $parameters); - } + // Create + if (strtolower($action) === 'create' && $method === static::REQUEST_TYPE_GET) { + return $this->call('create', $parameters); + } - // Show - if(isset($parameters['id']) && $request->getMethod() === static::REQUEST_TYPE_GET) { - return $this->call('show', $parameters); - } + // Save + if ($method === static::REQUEST_TYPE_POST) { + return $this->call('store', $parameters); + } - // Index - return $this->call('index', $parameters); - } + // Show + if (isset($parameters['id']) && $method === static::REQUEST_TYPE_GET) { + return $this->call('show', $parameters); + } - return null; - } + // Index + return $this->call('index', $parameters); + } - /** - * @return string - */ - public function getController() { - return $this->controller; - } + return null; + } - /** - * @param string $controller - * @return static - */ - public function setController($controller) { - $this->controller = $controller; - return $this; - } + /** + * @return string + */ + public function getController() + { + return $this->controller; + } + + /** + * @param string $controller + * @return static + */ + public function setController($controller) + { + $this->controller = $controller; + + return $this; + } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/RouterRoute.php b/src/Pecee/SimpleRouter/RouterRoute.php index 22a17af..6e0f472 100644 --- a/src/Pecee/SimpleRouter/RouterRoute.php +++ b/src/Pecee/SimpleRouter/RouterRoute.php @@ -1,42 +1,42 @@ setUrl($url); + $this->setCallback($callback); + } - public function __construct($url, $callback) { - $this->setUrl($url); - $this->setCallback($callback); - } + public function matchRoute(Request $request) + { + $url = parse_url(urldecode($request->getUri()), PHP_URL_PATH); + $url = rtrim($url, '/') . '/'; - public function matchRoute(Request $request) { + // Match on custom defined regular expression + if ($this->regex !== null) { + $parameters = array(); + if (preg_match('/(' . $this->regex . ')/is', $request->getHost() . $url, $parameters)) { + $this->parameters = (array)$parameters[0]; + return true; + } + return null; + } - $url = parse_url(urldecode($request->getUri()), PHP_URL_PATH); - $url = rtrim($url, '/') . '/'; + // Make regular expression based on route + $route = rtrim($this->url, '/') . '/'; - // Match on custom defined regular expression - if($this->regex !== null) { - $parameters = array(); - if(preg_match('/(' . $this->regex . ')/is', $request->getHost() . $url, $parameters)) { - $this->parameters = (!is_array($parameters[0]) ? array($parameters[0]) : $parameters[0]); - return true; - } - return null; - } + $parameters = $this->parseParameters($route, $url); - // Make regular expression based on route - $route = rtrim($this->url, '/') . '/'; + if ($parameters !== null) { + $this->parameters = array_merge($this->parameters, $parameters); + return true; + } - $parameters = $this->parseParameters($route, $url); - - if($parameters !== null) { - $this->parameters = array_merge($this->parameters, $parameters); - return true; - } - - return null; - } + return null; + } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index c2760c4..dc4af8c 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -1,158 +1,347 @@ routeRequest(); - } + /** + * Start/route request + * + * @throws \Pecee\Exception\RouterException + */ + public static function start() + { + static::router()->routeRequest(); + } - /** - * Set default namespace for all routes - * @param string $defaultNamespace - */ - public static function setDefaultNamespace($defaultNamespace) { - RouterBase::getInstance()->setDefaultNamespace($defaultNamespace); - } + /** + * Set default namespace which will be prepended to all routes. + * + * @param string $defaultNamespace + */ + public static function setDefaultNamespace($defaultNamespace) + { + static::$defaultNamespace = $defaultNamespace; + } - /** - * Set base csrf verifier - * @param BaseCsrfVerifier $baseCsrfVerifier - */ - public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) { - RouterBase::getInstance()->setCsrfVerifier($baseCsrfVerifier); - } + /** + * Base CSRF verifier + * + * @param BaseCsrfVerifier $baseCsrfVerifier + */ + public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) + { + static::router()->setCsrfVerifier($baseCsrfVerifier); + } - public static function addBootManager(RouterBootManager $bootManager) { - RouterBase::getInstance()->addBootManager($bootManager); - } + /** + * Boot managers allows you to alter the routes before the routing occurs. + * Perfect if you want to load pretty-urls from a file or database. + * + * @param RouterBootManager $bootManager + */ + public static function addBootManager(RouterBootManager $bootManager) + { + static::router()->addBootManager($bootManager); + } - public static function get($url, $callback, array $settings = null) { - return static::match(['get'], $url, $callback, $settings); - } + /** + * Route the given url to your callback on GET request method. + * + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @return RouterRoute + */ + public static function get($url, $callback, array $settings = null) + { + return static::match(['get'], $url, $callback, $settings); + } - public static function post($url, $callback, array $settings = null) { - return static::match(['post'], $url, $callback, $settings); - } + /** + * Route the given url to your callback on POST request method. + * + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @return RouterRoute + */ + public static function post($url, $callback, array $settings = null) + { + return static::match(['post'], $url, $callback, $settings); + } - public static function put($url, $callback, array $settings = null) { - return static::match(['put'], $url, $callback, $settings); - } + /** + * Route the given url to your callback on PUT request method. + * + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @return RouterRoute + */ + public static function put($url, $callback, array $settings = null) + { + return static::match(['put'], $url, $callback, $settings); + } - public static function patch($url, $callback, array $settings = null) { - return static::match(['patch'], $url, $callback, $settings); - } + /** + * Route the given url to your callback on PATCH request method. + * + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @return RouterRoute + */ + public static function patch($url, $callback, array $settings = null) + { + return static::match(['patch'], $url, $callback, $settings); + } - public static function options($url, $callback, array $settings = null) { - return static::match(['options'], $url, $callback, $settings); - } + /** + * Route the given url to your callback on OPTIONS request method. + * + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @return RouterRoute + */ + public static function options($url, $callback, array $settings = null) + { + return static::match(['options'], $url, $callback, $settings); + } - public static function delete($url, $callback, array $settings = null) { - return static::match(['delete'], $url, $callback, $settings); - } + /** + * Route the given url to your callback on DELETE request method. + * + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @return RouterRoute + */ + public static function delete($url, $callback, array $settings = null) + { + return static::match(['delete'], $url, $callback, $settings); + } - public static function group($settings = array(), $callback) { - $group = new RouterGroup(); - $group->setCallback($callback); + /** + * Groups allows for encapsulating routes with special settings. + * + * @param array $settings + * @param \Closure $callback + * @throws RouterException + * @return RouterGroup + */ + public static function group($settings = array(), \Closure $callback) + { + $group = new RouterGroup(); + $group->setCallback($callback); - if($settings !== null && is_array($settings)) { - $group->setData($settings); - } + if ($settings !== null && is_array($settings) === true) { + $group->setData($settings); + } - RouterBase::getInstance()->addRoute($group); + if (is_callable($callback) === false) { + throw new RouterException('Invalid callback provided. Only functions or methods supported'); + } - return $group; - } + static::router()->addRoute($group); - /** - * Adds get + post route - * - * @param string $url - * @param callable $callback - * @param array|null $settings - * @return RouterRoute - */ - public static function basic($url, $callback, array $settings = null) { - return static::match(['get', 'post'], $url, $callback, $settings); - } + return $group; + } - public static function match(array $requestMethods, $url, $callback, array $settings = null) { - $route = new RouterRoute($url, $callback); - $route->setRequestMethods($requestMethods); + /** + * Alias for the form method + * + * @param string $url + * @param callable $callback + * @param array|null $settings + * @see SimpleRouter::form + * @return RouterRoute + */ + public static function basic($url, $callback, array $settings = null) + { + return static::match(['get', 'post'], $url, $callback, $settings); + } - if($settings !== null) { - $route->setData($settings); - } + /** + * This type will route the given url to your callback on the provided request methods. + * Route the given url to your callback on POST and GET request method. + * + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @see SimpleRouter::form + * @return RouterRoute + */ + public static function form($url, $callback, array $settings = null) + { + return static::match(['get', 'post'], $url, $callback, $settings); + } - RouterBase::getInstance()->addRoute($route); + /** + * This type will route the given url to your callback on the provided request methods. + * + * @param array $requestMethods + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @return RouterEntry|RouterRoute + */ + public static function match(array $requestMethods, $url, $callback, array $settings = null) + { + $route = new RouterRoute($url, $callback); + $route->setRequestMethods($requestMethods); - return $route; - } + if ($settings !== null) { + $route->setData($settings); + } - public static function all($url, $callback, array $settings = null) { - $route = new RouterRoute($url, $callback); + $route = static::addDefaultNamespace($route); - if($settings !== null) { - $route->setData($settings); - } + static::router()->addRoute($route); - RouterBase::getInstance()->addRoute($route); + return $route; + } - return $route; - } + /** + * This type will route the given url to your callback and allow any type of request method + * + * @param string $url + * @param string|\Closure $callback + * @param array|null $settings + * @return RouterRoute + */ + public static function all($url, $callback, array $settings = null) + { + $route = new RouterRoute($url, $callback); - public static function controller($url, $controller, array $settings = null) { - $route = new RouterController($url, $controller); + if ($settings !== null) { + $route->setData($settings); + } - if($settings !== null) { - $route->setData($settings); - } + $route = static::addDefaultNamespace($route); - RouterBase::getInstance()->addRoute($route); + static::router()->addRoute($route); - return $route; - } + return $route; + } - public static function resource($url, $controller, array $settings = null) { - $route = new RouterResource($url, $controller); + /** + * This route will route request from the given url to the controller. + * + * @param string $url + * @param string $controller + * @param array|null $settings + * @return RouterController + */ + public static function controller($url, $controller, array $settings = null) + { + $route = new RouterController($url, $controller); - if($settings !== null) { - $route->setData($settings); - } + if ($settings !== null) { + $route->setData($settings); + } - static::router()->addRoute($route); + $route = static::addDefaultNamespace($route); - return $route; - } + static::router()->addRoute($route); - public static function getRoute($controller = null, $parameters = null, $getParams = null) { - return static::router()->getRoute($controller, $parameters, $getParams); - } + return $route; + } - public static function request() { - return static::router()->getRequest(); - } + /** + * This type will route all REST-supported requests to different methods in the provided controller. + * + * @param string $url + * @param string $controller + * @param array|null $settings + * @return RouterResource + */ + public static function resource($url, $controller, array $settings = null) + { + $route = new RouterResource($url, $controller); - public static function response() { - return static::router()->getResponse(); - } + if ($settings !== null) { + $route->setData($settings); + } - protected static function router() { - return RouterBase::getInstance(); - } + static::router()->addRoute($route); + + return $route; + } + + /** + * Get url by controller or alias. + * + * @param string $controller + * @param array|null $parameters + * @param array|null $getParams + * @return string + */ + public static function getRoute($controller = null, $parameters = null, $getParams = null) + { + return static::router()->getRoute($controller, $parameters, $getParams); + } + + /** + * Get the request + * + * @return \Pecee\Http\Request + */ + public static function request() + { + return static::router()->getRequest(); + } + + /** + * Get the response object + * + * @return \Pecee\Http\Response + */ + public static function response() + { + return static::router()->getResponse(); + } + + /** + * Returns the router instance + * + * @return RouterBase + */ + public static function router() + { + return RouterBase::getInstance(); + } + + /** + * Prepends the default namespace to all new routes added. + * + * @param RouterEntry $route + * @return RouterEntry + */ + protected static function addDefaultNamespace(RouterEntry $route) + { + if (static::$defaultNamespace !== null) { + $namespace = static::$defaultNamespace; + + if ($route->getNamespace() !== null) { + $namespace .= '\\' . $route->getNamespace(); + } + + $route->setNamespace($namespace); + } + + return $route; + } } \ No newline at end of file diff --git a/test/Dummy/DummyController.php b/test/Dummy/DummyController.php index a760d74..77cc02b 100644 --- a/test/Dummy/DummyController.php +++ b/test/Dummy/DummyController.php @@ -1,18 +1,25 @@ result = false; - $this->result = false; + SimpleRouter::group(['prefix' => '/group'], function () { + $this->result = true; + }); - \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/group'], function() { - $this->result = true; - }); + try { + SimpleRouter::start(); + } catch (Exception $e) { + // ignore RouteNotFound exception + } - try { - \Pecee\SimpleRouter\SimpleRouter::start(); - } catch(Exception $e) { - // ignore RouteNotFound exception - } + $this->assertTrue($this->result); + } - $this->assertTrue($this->result); - } + public function testNestedGroup() + { - public function testNestedGroup() { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setUri('/api/v1/test'); + SimpleRouter::request()->setMethod('get'); - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/api/v1/test'); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); + SimpleRouter::group(['prefix' => '/api'], function () { - \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() { - \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() { - \Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start'); - }); - }); + SimpleRouter::group(['prefix' => '/v1'], function () { + SimpleRouter::get('/test', 'DummyController@start'); + }); - \Pecee\SimpleRouter\SimpleRouter::start(); - } + }); - public function testManyRoutes() { + SimpleRouter::start(); + } - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/match'); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); + public function testManyRoutes() + { - \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() { - \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() { - \Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start'); - }); - }); + SimpleRouter::router()->reset(); + SimpleRouter::request()->setUri('/my/match'); + SimpleRouter::request()->setMethod('get'); - \Pecee\SimpleRouter\SimpleRouter::get('/my/match', 'DummyController@start'); + SimpleRouter::group(['prefix' => '/api'], function () { - \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/service'], function() { - \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() { - \Pecee\SimpleRouter\SimpleRouter::get('/no-match', 'DummyController@start'); - }); - }); + SimpleRouter::group(['prefix' => '/v1'], function () { + SimpleRouter::get('/test', 'DummyController@start'); + }); - \Pecee\SimpleRouter\SimpleRouter::start(); - } + }); - public function testUrls() { + SimpleRouter::get('/my/match', 'DummyController@start'); - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/fancy/url/1'); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); + SimpleRouter::group(['prefix' => '/service'], function () { - \Pecee\SimpleRouter\SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']); - \Pecee\SimpleRouter\SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setAlias('fancy2'); + SimpleRouter::group(['prefix' => '/v1'], function () { + SimpleRouter::get('/no-match', 'DummyController@start'); + }); - \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/')); + SimpleRouter::start(); + } - } + public function testUrls() + { + + SimpleRouter::router()->reset(); + SimpleRouter::request()->setUri('/my/fancy/url/1'); + SimpleRouter::request()->setMethod('get'); + + SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']); + SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setAlias('fancy2'); + + SimpleRouter::start(); + + $this->assertTrue((SimpleRouter::getRoute('fancy1') === '/my/fancy/url/1/')); + $this->assertTrue((SimpleRouter::getRoute('fancy2') === '/my/fancy/url/2/')); + + } } \ No newline at end of file diff --git a/test/MiddlewareTest.php b/test/MiddlewareTest.php index 6e18f71..d746298 100644 --- a/test/MiddlewareTest.php +++ b/test/MiddlewareTest.php @@ -4,28 +4,29 @@ require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyController.php'; require_once 'Dummy/Handler/ExceptionHandler.php'; -class MiddlewareTest extends PHPUnit_Framework_TestCase { +use Pecee\SimpleRouter\SimpleRouter as SimpleRouter; - public function testMiddlewareFound() { +class MiddlewareTest extends PHPUnit_Framework_TestCase +{ + public function testMiddlewareFound() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setMethod('get'); + SimpleRouter::request()->setUri('/my/test/url'); - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); + SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () { + SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']); + }); - \Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() { - \Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']); - }); + $found = false; - $found = false; + try { + SimpleRouter::start(); + } catch (\Exception $e) { + $found = ($e instanceof MiddlewareLoadedException); + } - try { - \Pecee\SimpleRouter\SimpleRouter::start(); - }catch(\Exception $e) { - $found = ($e instanceof MiddlewareLoadedException); - } - - $this->assertTrue($found); - - } + $this->assertTrue($found); + } } \ No newline at end of file diff --git a/test/RouterRouteTest.php b/test/RouterRouteTest.php index 3bfd9cc..4788a47 100644 --- a/test/RouterRouteTest.php +++ b/test/RouterRouteTest.php @@ -4,135 +4,137 @@ require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyController.php'; require_once 'Dummy/Handler/ExceptionHandler.php'; -class RouterRouteTest extends PHPUnit_Framework_TestCase { +use Pecee\SimpleRouter\SimpleRouter as SimpleRouter; - protected $result = false; +class RouterRouteTest extends PHPUnit_Framework_TestCase +{ + protected $result = false; - public function testNotFound() { - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1-param2'); + public function testNotFound() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setMethod('get'); + SimpleRouter::request()->setUri('/test-param1-param2'); - \Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() { - \Pecee\SimpleRouter\SimpleRouter::get('/non-existing-path', 'DummyController@start'); - }); + SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () { + SimpleRouter::get('/non-existing-path', 'DummyController@start'); + }); - $found = false; + $found = false; - try { - \Pecee\SimpleRouter\SimpleRouter::start(); - }catch(\Exception $e) { - $found = ($e instanceof \Pecee\Exception\RouterException && $e->getCode() == 404); - } + try { + SimpleRouter::start(); + } catch (\Exception $e) { + $found = ($e instanceof \Pecee\Exception\RouterException && $e->getCode() == 404); + } - $this->assertTrue($found); + $this->assertTrue($found); + } - } + public function testGet() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setUri('/my/test/url'); + SimpleRouter::request()->setMethod('get'); - public function testGet() { - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); + SimpleRouter::get('/my/test/url', 'DummyController@start'); + SimpleRouter::start(); + } - \Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start'); - \Pecee\SimpleRouter\SimpleRouter::start(); - } + public function testPost() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setUri('/my/test/url'); + SimpleRouter::request()->setMethod('post'); - public function testPost() { - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('post'); + SimpleRouter::post('/my/test/url', 'DummyController@start'); + SimpleRouter::start(); + } - \Pecee\SimpleRouter\SimpleRouter::post('/my/test/url', 'DummyController@start'); - \Pecee\SimpleRouter\SimpleRouter::start(); - } + public function testPut() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setUri('/my/test/url'); + SimpleRouter::request()->setMethod('put'); - public function testPut() { - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('put'); + SimpleRouter::put('/my/test/url', 'DummyController@start'); + SimpleRouter::start(); + } - \Pecee\SimpleRouter\SimpleRouter::put('/my/test/url', 'DummyController@start'); - \Pecee\SimpleRouter\SimpleRouter::start(); - } + public function testDelete() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setUri('/my/test/url'); + SimpleRouter::request()->setMethod('delete'); - public function testDelete() { - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('delete'); + SimpleRouter::delete('/my/test/url', 'DummyController@start'); + SimpleRouter::start(); + } - \Pecee\SimpleRouter\SimpleRouter::delete('/my/test/url', 'DummyController@start'); - \Pecee\SimpleRouter\SimpleRouter::start(); + public function testMethodNotAllowed() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setUri('/my/test/url'); + SimpleRouter::request()->setMethod('post'); - } + SimpleRouter::get('/my/test/url', 'DummyController@start'); - public function testMethodNotAllowed() { - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('post'); + try { + SimpleRouter::start(); + } catch (\Exception $e) { + $this->assertEquals(403, $e->getCode()); + } + } - \Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start'); + public function testSimpleParam() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setMethod('get'); + SimpleRouter::request()->setUri('/test-param1'); - try { - \Pecee\SimpleRouter\SimpleRouter::start(); - } catch(\Exception $e) { - $this->assertEquals(403, $e->getCode()); - } + SimpleRouter::get('/test-{param1}', 'DummyController@param'); + SimpleRouter::start(); + } - } + public function testMultiParam() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setMethod('get'); + SimpleRouter::request()->setUri('/test-param1-param2'); - public function testSimpleParam() { + SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param'); + SimpleRouter::start(); + } - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1'); + public function testPathParamRegex() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setMethod('get'); + SimpleRouter::request()->setUri('/test/path/123123'); - \Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}', 'DummyController@param'); - \Pecee\SimpleRouter\SimpleRouter::start(); + SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]); + SimpleRouter::start(); + } - } + public function testDomainRoute() + { + SimpleRouter::router()->reset(); + SimpleRouter::request()->setMethod('get'); + SimpleRouter::request()->setUri('/test'); + SimpleRouter::request()->setHost('hello.world.com'); - public function testMultiParam() { + $this->result = false; - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1-param2'); + SimpleRouter::group(['domain' => '{subdomain}.world.com'], function () { + SimpleRouter::get('test', function ($subdomain = null) { + $this->result = ($subdomain === 'hello'); + }); + }); - \Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param'); - \Pecee\SimpleRouter\SimpleRouter::start(); + SimpleRouter::start(); - } + $this->assertTrue($this->result); - public function testPathParamRegex() { - - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test/path/123123'); - - \Pecee\SimpleRouter\SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]); - \Pecee\SimpleRouter\SimpleRouter::start(); - - } - - public function testDomainRoute() { - - \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); - \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); - \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test'); - \Pecee\SimpleRouter\SimpleRouter::request()->setHost('hello.world.com'); - - $this->result = false; - - \Pecee\SimpleRouter\SimpleRouter::group(['domain' => '{subdomain}.world.com'], function() { - \Pecee\SimpleRouter\SimpleRouter::get('test', function($subdomain = null) { - $this->result = ($subdomain === 'hello'); - }); - }); - - \Pecee\SimpleRouter\SimpleRouter::start(); - - $this->assertTrue($this->result); - - } + } } \ No newline at end of file diff --git a/test/RouterUrlTest.php b/test/RouterUrlTest.php new file mode 100644 index 0000000..9515320 --- /dev/null +++ b/test/RouterUrlTest.php @@ -0,0 +1,83 @@ +reset(); + SimpleRouter::request()->setMethod('get'); + SimpleRouter::request()->setUri('/'); + + // Match normal route on alias + SimpleRouter::get('/', 'DummyController@silent', ['as' => 'home']); + + SimpleRouter::group(['prefix' => '/admin'], function() { + + // Match route with prefix on alias + SimpleRouter::get('/{id?}', 'DummyController@start', ['as' => 'admin.home']); + + // Match controller with prefix and alias + SimpleRouter::controller('/users', 'DummyController', ['as' => 'admin.users']); + + // Match controller with prefix and NO alias + SimpleRouter::controller('/pages', 'DummyController'); + + }); + + // Match controller with no prefix and no alias + SimpleRouter::controller('/cats', 'CatsController'); + + // Pretend to load page + SimpleRouter::start(); + + // Should match / + $this->assertEquals($this->getUrl('home'), '/'); + + // Should match /admin/ + $this->assertEquals($this->getUrl('DummyController@start'), '/admin/'); + + // Should match /admin/ + $this->assertEquals($this->getUrl('admin.home'), '/admin/'); + + // Should match /admin/2/ + $this->assertEquals($this->getUrl('admin.home', ['id' => 2]), '/admin/2/'); + + // Should match /admin/users/ + $this->assertEquals($this->getUrl('admin.users'), '/admin/users/'); + + // Should match /admin/users/home/ + $this->assertEquals($this->getUrl('admin.users@home'), '/admin/users/home/'); + + // Should match /cats/ + $this->assertEquals($this->getUrl('CatsController'), '/cats/'); + + // Should match /cats/view/ + $this->assertEquals($this->getUrl('CatsController', 'view'), '/cats/view/'); + + // Should match /cats/view/ + $this->assertEquals($this->getUrl('CatsController', ['view']), '/cats/view/'); + + // Should match /cats/view/666 + $this->assertEquals($this->getUrl('CatsController@view', ['666']), '/cats/view/666/'); + + // Should match /funny/man/ + $this->assertEquals($this->getUrl('/funny/man'), '/funny/man/'); + + // Should match /?jackdaniels=true + $this->assertEquals($this->getUrl('home', null, ['jackdaniels' => 'true']), '/?jackdaniels=true'); + + } + +} \ No newline at end of file