Merge pull request #113 from skipperbent/development

Development
This commit is contained in:
Simon Sessingø
2016-10-20 08:35:20 +02:00
committed by GitHub
13 changed files with 165 additions and 195 deletions
+8 -49
View File
@@ -116,7 +116,7 @@ SimpleRouter::group(['prefix' => 'v1', 'middleware' => '\MyWebsite\Middleware\So
This is a basic example of an ExceptionHandler implementation: This is a basic example of an ExceptionHandler implementation:
```php ```php
namespace BB\Handlers; namespace Demo\Handlers;
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry; use Pecee\SimpleRouter\RouterEntry;
@@ -140,7 +140,7 @@ class CustomExceptionHandler implements IExceptionHandler {
} }
} }
`` ```
### Sub-domain routing ### Sub-domain routing
@@ -184,40 +184,22 @@ This is a simple example of an integration into a framework.
The framework has it's own ```Router``` class which inherits from the ```SimpleRouter``` class. This allows the framework to add custom functionality. The framework has it's own ```Router``` class which inherits from the ```SimpleRouter``` class. This allows the framework to add custom functionality.
```php ```php
namespace Pecee; namespace Demo;
use Pecee\Exception\RouterException; use Pecee\Exception\RouterException;
use Pecee\Handler\ExceptionHandler;
use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Middleware\IMiddleware;
use Pecee\SimpleRouter\RouterBase; use Pecee\SimpleRouter\RouterBase;
use Pecee\SimpleRouter\SimpleRouter; use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter { class Router extends SimpleRouter {
protected static $defaultExceptionHandler;
protected static $defaultMiddlewares = array(); protected static $defaultMiddlewares = array();
public static function start($defaultNamespace = null) { public static function start($defaultNamespace = null) {
// Debug information // change this to whatever makes sense in your project
Debug::getInstance()->add('Router initialised.'); require_once 'routes.php';
// Load framework specific controllers
static::get('/js-wrap', 'ControllerJs@wrap', ['namespace' => '\Pecee\Controller'])->setAlias('pecee.js.wrap');
static::get('/css-wrap', 'ControllerCss@wrap', ['namespace' => '\Pecee\Controller'])->setAlias('pecee.css.wrap');
static::get('/captcha', 'ControllerCaptcha@show', ['namespace' => '\Pecee\Controller']);
// Load routes.php
$file = $_ENV['base_path'] . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'routes.php';
if(file_exists($file)) {
require_once $file;
}
// Set default namespace
$defaultNamespace = '\\'.$_ENV['app_name'] . '\\Controller';
// Handle exceptions
try {
if(count(static::$defaultMiddlewares)) { if(count(static::$defaultMiddlewares)) {
/* @var $middleware \Pecee\Http\Middleware\IMiddleware */ /* @var $middleware \Pecee\Http\Middleware\IMiddleware */
@@ -230,33 +212,11 @@ class Router extends SimpleRouter {
} }
} }
// Set default namespace
$defaultNamespace = '\\Demo\\Controllers';
parent::start($defaultNamespace); parent::start($defaultNamespace);
} catch(\Exception $e) {
$route = RouterBase::getInstance()->getLoadedRoute();
// Otherwise use the fallback default exceptions handler
if(static::$defaultExceptionHandler !== null) {
static::loadExceptionHandler(static::$defaultExceptionHandler, $route, $e);
}
throw $e;
}
}
protected static function loadExceptionHandler($class, $route, $e) {
$class = new $class();
if(!($class instanceof ExceptionHandler)) {
throw new \ErrorException('Exception handler must be an instance of \Pecee\Handler\ExceptionHandler');
}
$class->handleError(RouterBase::getInstance()->getRequest(), $route, $e);
}
public static function defaultExceptionHandler($handler) {
static::$defaultExceptionHandler = $handler;
} }
/** /**
@@ -348,7 +308,6 @@ Sometimes it can be necessary to keep urls stored in the database, file or simil
To interfere with the router, we create a class that inherits from ```RouterBootManager```. This class will be loaded before any other rules in ```routes.php``` and allow us to "change" the current route, if any of our criteria are fulfilled (like coming from the url ```/my-cat-is-beatiful```). To interfere with the router, we create a class that inherits from ```RouterBootManager```. This class will be loaded before any other rules in ```routes.php``` and allow us to "change" the current route, if any of our criteria are fulfilled (like coming from the url ```/my-cat-is-beatiful```).
```php ```php
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterBootManager; use Pecee\SimpleRouter\RouterBootManager;
+14 -18
View File
@@ -2,31 +2,30 @@
This project is here to give you a basic understanding of how to setup and using simple-php-router. This project is here to give you a basic understanding of how to setup and using simple-php-router.
Please note that this demo-project only covers how to integrate simple-php-project in a project without a framework. If you are using some sort of PHP framework in your project Please note that this demo-project only covers how to integrate the `simple-php-router` in a project without a framework. If you are using some sort of PHP framework in your project the implementation might vary.
the implementation might vary.
**What we won't cover:** **What we won't cover:**
- How to setup a solution that fits your need. This is a basic demo to help you get started. - How to setup a solution that fits your need. This is a basic demo to help you get started.
- How to add Controllers, Middlewares or ExceptionHandlers with cool functionality. - Understanding of MVC; including Controllers, Middlewares or ExceptionHandlers.
- How to integrate into third party frameworks. - How to integrate into third party frameworks.
**What we cover:** **What we cover:**
- How to get up and running fast - from scratch. - How to get up and running fast - from scratch.
- How to set ExceptionHandlers, Middlewares and Controllers working. - How to get ExceptionHandlers, Middlewares and Controllers working.
- How to setup your webservers. - How to setup your webservers.
## Installation ## Installation
- Navigate to the `demo-project` folder and run `composer install`. - Navigate to the `demo-project` folder in terminal and run `composer update` to install the latest version.
- Point your webserver to `demo-project/public`. - Point your webserver to `demo-project/public`.
### Setting up Nginx ### Setting up Nginx
If you are using Nginx remember to enable url-rewriting. If you are using Nginx please make sure that url-rewriting is enabled.
You can easily do this by adding the following configuration for the Nginx configuration for the demo-project. You can easily enable url-rewriting by adding the following configuration for the Nginx configuration-file for the demo-project.
``` ```
location / { location / {
@@ -36,8 +35,7 @@ location / {
### Setting up Apache ### Setting up Apache
Nothing special is required for Apache to work. We've include the `.htaccess` file in the `public` folder. If rewriting is not working for you, please Nothing special is required for Apache to work. We've include the `.htaccess` file in the `public` folder. If rewriting is not working for you, please check that the `mod_rewrite` module (htaccess support) is enabled in the Apache configuration.
check that `.htaccess` support is enabled in the Apache configuration - or add the rules manually.
## Folder structure ## Folder structure
@@ -48,26 +46,24 @@ check that `.htaccess` support is enabled in the Apache configuration - or add t
## Notes ## Notes
The demo project has it's own `Router` class implemented which extends the `SimpleRouter` class with further functionality such as The demo project has it's own `Router` class implementation which extends the `SimpleRouter` class with further functionality.
default exceptionhandlers and middlewares. This class can be useful adding functionality that are required before and after routing This class can be useful adding additional functionality that are required before and after routing occurs or any extra functionality belonging to the router itself.
occurs or add extra functionality to the router.
In this project we also use our custom router-class to autoload the `routes.php` file. In this project we also use our custom router-class to autoload the `routes.php` file from our custom location (`app/routes.php`).
Please check the `routes.php` file in `demo-project/app` for all the urls/rules in the project. Please check the `routes.php` file in `demo-project/app` for all the urls/rules available in the project.
### CSRF-verifier ### CSRF-verifier
We've added a custom CSRF-verifier middleware called `CsrfVerifier` and disabled CSRF checks for all calls to `/api/*`. For the purpose of this demo, we've added a custom CSRF-verifier middleware called `CsrfVerifier` and disabled CSRF checks for all calls to `/api/*`. This will ensure that CSRF form-checks are not applied when calling our demo api url.
### Exception handlers ### Exception handlers
The included `CustomExceptionHandler` class returns a json response for errors received on calls to `/api/*` or otherwise just forms a simple formatted error response. The included `CustomExceptionHandler` class returns a very basic json response for errors received on calls to `/api/*` or otherwise just a simple formatted error response.
### Middlewares ### Middlewares
`ApiVerification` class is added to all calls to `/api/*`. This simple class just adds some data to the `Request` object, which is returned in one of the methods in the `ApiVerification` class is added to all calls to `/api/*`. This simple class just adds some data to the `Request` object, which is returned in one of the methods in the `ApiController` class. We've added this class to demonstrate that you can use middlewares to ensure that the user has the correct authentication - before router loads the controller itself.
`ApiController` class.
### Urls ### Urls
@@ -22,4 +22,8 @@ class DefaultController {
} }
public function notFound() {
echo 'Page not found';
}
} }
@@ -21,8 +21,14 @@ class CustomExceptionHandler implements IExceptionHandler {
// else we just throw the error // else we just throw the error
if($error->getCode() == 404) { if($error->getCode() == 404) {
die(sprintf('An error occurred (%s):<br/>%s', $error->getCode(), $error->getMessage()));
// Return 404 path
$request->setUri('/404');
return $request;
} }
throw $error;
} }
} }
-29
View File
@@ -8,14 +8,12 @@
namespace Demo; namespace Demo;
use Pecee\Exception\RouterException; use Pecee\Exception\RouterException;
use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Middleware\IMiddleware;
use Pecee\SimpleRouter\RouterBase; use Pecee\SimpleRouter\RouterBase;
use Pecee\SimpleRouter\SimpleRouter; use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter { class Router extends SimpleRouter {
protected static $defaultExceptionHandler;
protected static $defaultMiddlewares = array(); protected static $defaultMiddlewares = array();
public static function start($defaultNamespace = null) { public static function start($defaultNamespace = null) {
@@ -23,8 +21,6 @@ class Router extends SimpleRouter {
// change this to whatever makes sense in your project // change this to whatever makes sense in your project
require_once 'routes.php'; require_once 'routes.php';
// Handle exceptions
try {
if(count(static::$defaultMiddlewares)) { if(count(static::$defaultMiddlewares)) {
/* @var $middleware \Pecee\Http\Middleware\IMiddleware */ /* @var $middleware \Pecee\Http\Middleware\IMiddleware */
@@ -41,32 +37,7 @@ class Router extends SimpleRouter {
$defaultNamespace = '\\Demo\\Controllers'; $defaultNamespace = '\\Demo\\Controllers';
parent::start($defaultNamespace); parent::start($defaultNamespace);
} catch(\Exception $e) {
$route = RouterBase::getInstance()->getLoadedRoute();
// Otherwise use the fallback default exceptions handler
if(static::$defaultExceptionHandler !== null) {
static::loadExceptionHandler(static::$defaultExceptionHandler, $route, $e);
}
throw $e;
}
}
protected static function loadExceptionHandler($class, $route, $e) {
$class = new $class();
if(!($class instanceof IExceptionHandler)) {
throw new \ErrorException('Exception handler must be an instance of \Pecee\Handler\IExceptionHandler');
}
$class->handleError(RouterBase::getInstance()->getRequest(), $route, $e);
}
public static function defaultExceptionHandler($handler) {
static::$defaultExceptionHandler = $handler;
} }
/** /**
+5 -1
View File
@@ -6,10 +6,12 @@
use Demo\Router; use Demo\Router;
Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier()); Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
Router::defaultExceptionHandler('\Demo\Handlers\CustomExceptionHandler');
Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function() {
Router::get('/', 'DefaultController@index')->setAlias('home'); Router::get('/', 'DefaultController@index')->setAlias('home');
Router::get('/contact', 'DefaultController@contact')->setAlias('contact'); Router::get('/contact', 'DefaultController@contact')->setAlias('contact');
Router::get('/404', 'DefaultController@notFound')->setAlias('404');
Router::basic('/companies', 'DefaultController@companies')->setAlias('companies'); Router::basic('/companies', 'DefaultController@companies')->setAlias('companies');
Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('companies'); Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('companies');
@@ -17,3 +19,5 @@ Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('compa
Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function() { Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function() {
Router::resource('/demo', 'ApiController'); Router::resource('/demo', 'ApiController');
}); });
});
+25 -13
View File
@@ -109,10 +109,12 @@ class RouterBase {
} }
} }
public function routeRequest() { public function routeRequest($original = true) {
$originalUri = $this->request->getUri(); $originalUri = $this->request->getUri();
try {
// Initialize boot-managers // Initialize boot-managers
if(count($this->bootManagers)) { if(count($this->bootManagers)) {
/* @var $manager RouterBootManager */ /* @var $manager RouterBootManager */
@@ -125,13 +127,15 @@ class RouterBase {
} }
} }
// Loop through each route-request
$this->processRoutes($this->routes);
if($original === true) {
// Verify csrf token for request // Verify csrf token for request
if ($this->baseCsrfVerifier !== null) { if ($this->baseCsrfVerifier !== null) {
$this->baseCsrfVerifier->handle($this->request); $this->baseCsrfVerifier->handle($this->request);
} }
}
// Loop through each route-request
$this->processRoutes($this->routes);
$routeNotAllowed = false; $routeNotAllowed = false;
@@ -159,16 +163,16 @@ class RouterBase {
$this->request->loadedRoute = $route; $this->request->loadedRoute = $route;
$route->loadMiddleware($this->request); $route->loadMiddleware($this->request);
try {
$this->request->loadedRoute->renderRoute($this->request); $this->request->loadedRoute->renderRoute($this->request);
} catch(\Exception $e) {
$this->handleException($e);
}
break; break;
} }
} }
} catch(\Exception $e) {
$this->handleException($e);
}
if($routeNotAllowed) { if($routeNotAllowed) {
$this->handleException(new RouterException('Route or method not allowed', 403)); $this->handleException(new RouterException('Route or method not allowed', 403));
} }
@@ -180,6 +184,8 @@ class RouterBase {
protected function handleException(\Exception $e) { protected function handleException(\Exception $e) {
$request = null;
/* @var $route RouterGroup */ /* @var $route RouterGroup */
foreach ($this->exceptionHandlers as $route) { foreach ($this->exceptionHandlers as $route) {
$route->loadMiddleware($this->request); $route->loadMiddleware($this->request);
@@ -190,7 +196,13 @@ class RouterBase {
throw new RouterException('Exception handler must implement the IExceptionHandler interface.'); throw new RouterException('Exception handler must implement the IExceptionHandler interface.');
} }
$handler->handleError($this->request, $this->request->loadedRoute, $e); $request = $handler->handleError($this->request, $this->request->loadedRoute, $e);
}
if($request !== null) {
$this->request = $request;
$this->routeRequest(false);
return;
} }
throw $e; throw $e;
@@ -457,14 +469,14 @@ class RouterBase {
} }
public static function getInstance() { public static function getInstance() {
if(self::$instance === null) { if(static::$instance === null) {
self::$instance = new static(); static::$instance = new static();
} }
return self::$instance; return static::$instance;
} }
public static function reset() { public static function reset() {
self::$instance = null; static::$instance = null;
} }
} }
+1 -1
View File
@@ -310,7 +310,7 @@ abstract class RouterEntry {
if($max) { if($max) {
for($i = 0; $i < $max; $i++) { for($i = 0; $i < $max; $i++) {
$name = $parameterNames[$i]; $name = $parameterNames[$i];
$parameterValue = (isset($parameterValues[$name['name']]) && !empty($parameterValues[$name['name']])) ? $parameterValues[$name['name']] : null; $parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if($name['required'] && $parameterValue === null) { if($name['required'] && $parameterValue === null) {
throw new RouterException('Missing required parameter ' . $name['name'], 404); throw new RouterException('Missing required parameter ' . $name['name'], 404);
+4
View File
@@ -11,4 +11,8 @@ class DummyController {
echo 'Params: ' . join(', ', $params); echo 'Params: ' . join(', ', $params);
} }
public function notFound() {
echo 'not found';
}
} }
+8
View File
@@ -0,0 +1,8 @@
<?php
class ExceptionHandler implements \Pecee\Handler\IExceptionHandler {
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry $router = null, \Exception $error){
throw $error;
}
}
-7
View File
@@ -7,13 +7,6 @@ class GroupTest extends PHPUnit_Framework_TestCase {
protected $result; protected $result;
public function __construct() {
// Initial setup
$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['REQUEST_URI'] = '/api/v1/test';
$_SERVER['REQUEST_METHOD'] = 'get';
}
protected function group() { protected function group() {
$this->result = true; $this->result = true;
} }
+7 -11
View File
@@ -2,29 +2,25 @@
require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php'; require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class MiddlewareTest extends PHPUnit_Framework_TestCase { class MiddlewareTest extends PHPUnit_Framework_TestCase {
public function __construct() {
// Initial setup
$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['REQUEST_URI'] = '/my/test/url';
$_SERVER['REQUEST_METHOD'] = 'get';
}
public function testMiddlewareFound() { public function testMiddlewareFound() {
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\SimpleRouter\RouterBase::reset(); \Pecee\SimpleRouter\RouterBase::reset();
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/my/test/url');
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']); \Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
});
$found = false; $found = false;
try { try {
\Pecee\SimpleRouter\SimpleRouter::start(); \Pecee\SimpleRouter\SimpleRouter::start();
}catch(Exception $e) { }catch(\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException); $found = ($e instanceof MiddlewareLoadedException);
} }
+22 -5
View File
@@ -2,14 +2,31 @@
require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php'; require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class RouterRouteTest extends PHPUnit_Framework_TestCase { class RouterRouteTest extends PHPUnit_Framework_TestCase {
public function __construct() {
// Initial setup public function testNotFound() {
$_SERVER['HTTP_HOST'] = 'example.com'; \Pecee\SimpleRouter\RouterBase::reset();
$_SERVER['REQUEST_URI'] = '/my/test/url';
$_SERVER['REQUEST_METHOD'] = 'get'; \Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test-param1-param2');
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/non-existing-path', 'DummyController@start');
});
$found = false;
try {
\Pecee\SimpleRouter\SimpleRouter::start();
}catch(\Exception $e) {
$found = ($e instanceof \Pecee\Exception\RouterException && $e->getCode() == 404);
}
$this->assertTrue($found);
} }
public function testGet() { public function testGet() {