diff --git a/README.md b/README.md index cfb0fbf..6250a0a 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The goal of this project is to create a router that is 100% compatible with the ### Features -- Basic routing (get, post, put, delete) with support for custom multiple verbs. +- Basic routing (`GET`, `POST`, `PUT`, `DELETE`) with support for custom multiple verbs. - Regular Expression Constraints for parameters. - Named routes. - Generating url to routes. @@ -204,19 +204,17 @@ class Router extends SimpleRouter { } ``` -#### Helper functions examples -**This is a basic example of a helper function for generating urls.** +## Helper functions + +To simplify to use of simple-router functionality, we recommend you add these helper functions to your project. ```php -use Pecee\SimpleRouter\RouterBase; +use Pecee\SimpleRouter\SimpleRouter; + function url($controller, $parameters = null, $getParams = null) { - RouterBase::getInstance()->getRoute($controller, $parameters, $getParams); + SimpleRouter::getRoute($controller, $parameters, $getParams); } -``` -**This is a basic example for getting the current csrf token** - -```php /** * Get current csrf-token * @return null|string @@ -225,6 +223,30 @@ function csrf_token() { $token = new \Pecee\CsrfToken(); return $token->getToken(); } + +/** + * Get request object + * @return \Pecee\Http\Request + */ +function request() { + return SimpleRouter::request(); +} + +/** + * Get response object + * @return \Pecee\Http\Response + */ +function response() { + return SimpleRouter::response(); +} + +/** + * Get input class + * @return \Pecee\Http\Input\Input + */ +function input() { + return SimpleRouter::request()->getInput(); +} ``` ## Getting urls @@ -314,25 +336,17 @@ By doing this the route will now load the url ```/article/view/1``` instead of ` The last thing we need to do, is to add our custom boot-manager to the ```routes.php``` file. You can create as many bootmanagers as you like and easily add them in your ```routes.php``` file. -**routes.php example:** - -```php -// Add new bootmanager -SimpleRouter::addBootManager(new CustomRouterRules()); - -// This rule is what our custom bootmanager will use. -SimpleRouter::get('/article/view/{id}', 'ControllerArticle@view'); -``` - ## Easily overwrite route about to be loaded Sometimes it can be useful to manipulate the route that's about to be loaded, for instance if a user is not authenticated or if an error occurred within your Middleware that requires some other route to be initialised. Simple PHP Router allows you to easily change the route about to be executed. All information about the current route is stored in -the ```\Pecee\SimpleRouter\Http\Request``` object. +the ```\Pecee\SimpleRouter\Http\Request``` object. All information about the current route is as a ```\Pecee\SimpleRouter\Http\Request``` object which can always be obtained on +the `RouterBase` instance. For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::request()`. **Note:** Please note that it's only possible to change the route BEFORE any route has initially been loaded, so doing this in your custom ExceptionHandler or Middleware is highly recommended. ```php -$route = Request::getInstance()->getLoadedRoute(); +use Pecee\SimpleRouter; +$route = SimpleRouter::request()->getLoadedRoute(); $route->setCallback('Example\MyCustomClass@hello'); @@ -348,28 +362,28 @@ We've added the `Input` class to easy access parameters from your Controller-cla **Return single parameter value (matches both GET, POST, FILE):** ```php -$value = Request::getInstance()->getInput()->get('name'); +$value = SimpleRouter::request()->getInput()->get('name'); ``` **Return parameter object (matches both GET, POST, FILE):** ```php -$object = Request::getInstance()->getInput()->getObject('name'); +$object = SimpleRouter::request()->getInput()->getObject('name'); ``` **Return specific GET parameter (where name is the name of your parameter):** ```php -$object = Request::getInstance()->getInput()->get->name; -$object = Request::getInstance()->getInput()->post->name; -$object = Request::getInstance()->getInput()->file->name; +$object = SimpleRouter::request()->getInput()->get->name; +$object = SimpleRouter::request()->getInput()->post->name; +$object = SimpleRouter::request()->getInput()->file->name; ``` **Return all parameters:** ```php // Get all -$objects = Request::getInstance()->getInput()->all(); +$objects = SimpleRouter::request()->getInput()->all(); // Only match certain keys -$objects = Request::getInstance()->getInput()->all([ +$objects = SimpleRouter::request()->getInput()->all([ 'company_name', 'user_id' ]); @@ -389,22 +403,9 @@ All object inherits from `InputItem` class and will always contain these methods - `getError()` - get file upload error. -### Easy access your input -Create a helper function to easily get access to the input elements. +### Easy access to methods -Example: - -```php -/** - * Get input class - * @return \Pecee\Http\Input\Input - */ -function input() { - return \Pecee\Http\Request::getInstance()->getInput(); -} -``` - -Then you can easily do something like this in your controller: +Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation. ```php // Get parameter site_id or default-value 2 diff --git a/demo-project/app/Controllers/ApiController.php b/demo-project/app/Controllers/ApiController.php index 13048ab..e06b241 100644 --- a/demo-project/app/Controllers/ApiController.php +++ b/demo-project/app/Controllers/ApiController.php @@ -1,7 +1,7 @@ getToken(); +} + +/** + * Get request object + * @return \Pecee\Http\Request + */ +function request() { + return SimpleRouter::request(); +} + +/** + * Get response object + * @return \Pecee\Http\Response + */ +function response() { + return SimpleRouter::response(); +} \ No newline at end of file diff --git a/src/Pecee/Http/Input/Input.php b/src/Pecee/Http/Input/Input.php index caed6bc..4820588 100644 --- a/src/Pecee/Http/Input/Input.php +++ b/src/Pecee/Http/Input/Input.php @@ -20,7 +20,13 @@ class Input { */ public $file; - public function __construct() { + /** + * @var Request + */ + protected $request; + + public function __construct(Request &$request) { + $this->request = $request; $this->setGet(); $this->setPost(); $this->setFile(); @@ -32,17 +38,31 @@ class Input { * @return array */ public function all(array $filter = null) { - $output = $this->get->getData(); - $output = array_merge($output, $this->post->getData()); - if($filter !== null) { - $tmp = array(); - foreach($output as $key => $val) { - if(in_array($key, $filter)) { - $tmp[$key] = $val; + $output = $_POST; + + if($this->request->getMethod() === 'post') { + + $contents = file_get_contents('php://input'); + + if (stripos(trim($contents), '{') === 0) { + $output = json_decode($contents, true); + if($output === false) { + $output = array(); } } - return $tmp; + } + + $output = array_merge($_GET, $output); + + if($filter !== null) { + $output = array_filter($output, function ($key) use ($filter) { + if (in_array($key, $filter)) { + return true; + } + + return false; + }, ARRAY_FILTER_USE_KEY); } return $output; @@ -58,7 +78,7 @@ class Input { return ($key !== null) ? $element[$key] : $element; } - if(Request::getInstance()->getMethod() !== 'get') { + if($this->request->getMethod() !== 'get') { $element = $this->post->findFirst($index); @@ -87,7 +107,7 @@ class Input { if($item !== null) { - if(is_array($item) || $item instanceof InputFile) { + if($item instanceof InputCollection || $item instanceof InputFile) { return $item; } @@ -111,10 +131,10 @@ class Input { continue; } - $output = array(); + $output = new InputCollection(); foreach($get as $k => $g) { - $output[$k] = new InputItem($k, $g); + $output->{$k} = new InputItem($k, $g); } $this->get->{$key} = $output; @@ -125,12 +145,10 @@ class Input { public function setPost() { $this->post = new InputCollection(); - $postVars = array(); + $postVars = $_POST; - if(isset($_SERVER['REQUEST_METHOD']) && in_array($_SERVER['REQUEST_METHOD'], ['PUT', 'PATCH', 'DELETE'])) { + if(in_array($this->request->getMethod(), ['put', 'patch', 'delete'])) { parse_str(file_get_contents('php://input'), $postVars); - } else { - $postVars = $_POST; } if(count($postVars)) { @@ -141,10 +159,10 @@ class Input { continue; } - $output = array(); + $output = new InputCollection(); - foreach($post as $k=>$p) { - $output[$k] = new InputItem($k, $p); + foreach($post as $k => $p) { + $output->{$k} = new InputItem($k, $p); } $this->post->{strtolower($key)} = $output; @@ -172,7 +190,7 @@ class Input { continue; } - $output = array(); + $output = new InputCollection(); foreach($value['name'] as $k=>$val) { // Strip empty values @@ -183,7 +201,7 @@ class Input { $file->setType($value['type'][$k]); $file->setTmpName($value['tmp_name'][$k]); $file->setError($value['error'][$k]); - $output[$k] = $file; + $output->{$k} = $file; } } diff --git a/src/Pecee/Http/Request.php b/src/Pecee/Http/Request.php index 49c284b..f077f49 100644 --- a/src/Pecee/Http/Request.php +++ b/src/Pecee/Http/Request.php @@ -26,7 +26,7 @@ class Request { $this->uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : array(); $this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : (isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : array()); $this->headers = $this->getAllHeaders(); - $this->input = new Input(); + $this->input = new Input($this); } protected function getAllHeaders() { diff --git a/src/Pecee/Http/Response.php b/src/Pecee/Http/Response.php index c60743d..ba7bfeb 100644 --- a/src/Pecee/Http/Response.php +++ b/src/Pecee/Http/Response.php @@ -4,6 +4,12 @@ namespace Pecee\Http; class Response { + protected $request; + + public function __construct(Request &$request) { + $this->request = $request; + } + /** * Set the http status code * @@ -31,7 +37,7 @@ class Response { } public function refresh() { - $this->redirect(Request::getInstance()->getUri()); + $this->redirect($this->request->getUri()); } /** diff --git a/src/Pecee/SimpleRouter/RouterBase.php b/src/Pecee/SimpleRouter/RouterBase.php index e618d94..75e718a 100644 --- a/src/Pecee/SimpleRouter/RouterBase.php +++ b/src/Pecee/SimpleRouter/RouterBase.php @@ -5,12 +5,14 @@ use Pecee\Exception\RouterException; use Pecee\Handler\IExceptionHandler; use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Request; +use Pecee\Http\Response; class RouterBase { protected static $instance; protected $request; + protected $response; protected $currentRoute; protected $routes; protected $processedRoutes; @@ -21,10 +23,13 @@ class RouterBase { protected $baseCsrfVerifier; protected $exceptionHandlers; - // TODO: clean up - cut some of the methods down to smaller pieces - public function __construct() { - $this->request = Request::getInstance(); + $this->reset(); + } + + public function reset() { + $this->request = new Request(); + $this->response = new Response($this->request); $this->routes = array(); $this->backStack = array(); $this->controllerUrlMap = array(); @@ -289,6 +294,14 @@ class RouterBase { return $this->request; } + /** + * Get response + * @return Response + */ + public function getResponse() { + return $this->response; + } + /** * Get base csrf verifier class * @return BaseCsrfVerifier @@ -341,10 +354,10 @@ class RouterBase { $url = $domain . '/' . trim($route->getUrl(), '/'); if(($route instanceof RouterController || $route instanceof RouterResource) && $method !== null) { - $url .= $method; - } + if($method !== null) { + $url .= $method; + } - if($route instanceof RouterController || $route instanceof RouterResource) { if(count($parameters)) { $url .= join('/', $parameters); } @@ -419,12 +432,15 @@ class RouterBase { $route = $this->controllerUrlMap[$i]; // Check an alias exist, if the matches - use it - if($route instanceof RouterRoute && $route->hasAlias($controller)) { - return $this->processUrl($route, $route->getMethod(), $parameters, $getParams); - } + if($route instanceof RouterRoute) { - if($route instanceof RouterRoute && !is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) { - $c = $route->getCallback(); + if($route->hasAlias($controller)) { + return $this->processUrl($route, $route->getMethod(), $parameters, $getParams); + } + + if(!is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) { + $c = $route->getCallback(); + } } else if($route instanceof RouterController || $route instanceof RouterResource) { $c = $route->getController(); } @@ -476,6 +492,10 @@ class RouterBase { return $url; } + /** + * Get current router instance + * @return static + */ public static function getInstance() { if(static::$instance === null) { static::$instance = new static(); diff --git a/src/Pecee/SimpleRouter/RouterGroup.php b/src/Pecee/SimpleRouter/RouterGroup.php index 35683b7..11863b0 100644 --- a/src/Pecee/SimpleRouter/RouterGroup.php +++ b/src/Pecee/SimpleRouter/RouterGroup.php @@ -97,9 +97,12 @@ class RouterGroup extends RouterEntry { if($this->getMiddleware() !== null && isset($settings['middleware'])) { if(!is_array($this->getMiddleware())) { - $middlewares = [$this->getMiddleware(), $settings['middleware']]; + $middlewares = [ + $this->getMiddleware(), + $settings['middleware'] + ]; } else { - $middlewares = array_push($settings['middleware']); + $middlewares = array_push($settings['middleware'], $this->getMiddleware()); } $settings['middleware'] = array_unique(array_reverse($middlewares)); diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index aca2cfb..a173be0 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -13,6 +13,10 @@ use Pecee\Http\Middleware\BaseCsrfVerifier; class SimpleRouter { + public function __construct() { + die('test'); + } + /** * Start/route request * @param null $defaultNamespace @@ -128,4 +132,12 @@ class SimpleRouter { return RouterBase::getInstance()->getRoute($controller, $parameters, $getParams); } + public static function request() { + return RouterBase::getInstance()->getRequest(); + } + + public static function response() { + return RouterBase::getInstance()->getResponse(); + } + } \ No newline at end of file diff --git a/test/GroupTest.php b/test/GroupTest.php index e1ce45d..b9f6c23 100644 --- a/test/GroupTest.php +++ b/test/GroupTest.php @@ -28,8 +28,9 @@ class GroupTest extends PHPUnit_Framework_TestCase { public function testNestedGroup() { - \Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/api/v1/test'); - \Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setMethod('get'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/api/v1/test'); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() { \Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() { diff --git a/test/MiddlewareTest.php b/test/MiddlewareTest.php index 287f899..6e18f71 100644 --- a/test/MiddlewareTest.php +++ b/test/MiddlewareTest.php @@ -8,8 +8,9 @@ class MiddlewareTest extends PHPUnit_Framework_TestCase { public function testMiddlewareFound() { - \Pecee\Http\Request::getInstance()->setMethod('get'); - \Pecee\Http\Request::getInstance()->setUri('/my/test/url'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); \Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() { \Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']); diff --git a/test/RouterRouteTest.php b/test/RouterRouteTest.php index ffa0299..e0ae29d 100644 --- a/test/RouterRouteTest.php +++ b/test/RouterRouteTest.php @@ -5,12 +5,12 @@ require_once 'Dummy/DummyController.php'; require_once 'Dummy/Handler/ExceptionHandler.php'; class RouterRouteTest extends PHPUnit_Framework_TestCase { - + public function testNotFound() { - - \Pecee\Http\Request::getInstance()->setMethod('get'); - \Pecee\Http\Request::getInstance()->setUri('/test-param1-param2'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1-param2'); \Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() { \Pecee\SimpleRouter\SimpleRouter::get('/non-existing-path', 'DummyController@start'); @@ -29,33 +29,36 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase { } public function testGet() { - - \Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url'); - \Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setMethod('get'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); \Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start'); \Pecee\SimpleRouter\SimpleRouter::start(); } public function testPost() { - \Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url'); - \Pecee\Http\Request::getInstance()->setMethod('post'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('post'); \Pecee\SimpleRouter\SimpleRouter::post('/my/test/url', 'DummyController@start'); \Pecee\SimpleRouter\SimpleRouter::start(); } public function testPut() { - \Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url'); - \Pecee\Http\Request::getInstance()->setMethod('put'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('put'); \Pecee\SimpleRouter\SimpleRouter::put('/my/test/url', 'DummyController@start'); \Pecee\SimpleRouter\SimpleRouter::start(); } public function testDelete() { - \Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url'); - \Pecee\Http\Request::getInstance()->setMethod('delete'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('delete'); \Pecee\SimpleRouter\SimpleRouter::delete('/my/test/url', 'DummyController@start'); \Pecee\SimpleRouter\SimpleRouter::start(); @@ -63,8 +66,9 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase { } public function testMethodNotAllowed() { - \Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url'); - \Pecee\Http\Request::getInstance()->setMethod('post'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('post'); \Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start'); @@ -78,8 +82,9 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase { public function testSimpleParam() { - \Pecee\Http\Request::getInstance()->setMethod('get'); - \Pecee\Http\Request::getInstance()->setUri('/test-param1'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1'); \Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}', 'DummyController@param'); \Pecee\SimpleRouter\SimpleRouter::start(); @@ -88,20 +93,22 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase { public function testMultiParam() { - \Pecee\Http\Request::getInstance()->setMethod('get'); - \Pecee\Http\Request::getInstance()->setUri('/test-param1-param2'); + \Pecee\SimpleRouter\RouterBase::getInstance()->reset(); + \Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); + \Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1-param2'); \Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param'); \Pecee\SimpleRouter\SimpleRouter::start(); } - public function testPathParam() { + public function testPathParamRegex() { - \Pecee\Http\Request::getInstance()->setMethod('get'); - \Pecee\Http\Request::getInstance()->setUri('/test/path/param1'); + \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/{param}', 'DummyController@param'); + \Pecee\SimpleRouter\SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]); \Pecee\SimpleRouter\SimpleRouter::start(); }