Compare commits

...

16 Commits

Author SHA1 Message Date
Simon Sessingø 0947f6746e Merge pull request #242 from skipperbent/v3-development
V3 development
2017-07-08 12:03:05 +02:00
Simon Sessingø 31c8710ce7 Merge pull request #241 from skipperbent/v-3-3-1
Version 3.3.1
2017-07-08 12:02:41 +02:00
Simon Sessingø 1ac7761d35 Development
- Optimized `.gitignore`.
- Renamed `XSRF-TOKEN` constant to `CSRF-TOKEN` in `CsrfToken`.
2017-07-08 11:56:57 +02:00
Simon Sessingø 9eb7c5c13c Removed 'name' as it's set as the default value when calling the $getItem function. 2017-07-08 11:49:05 +02:00
Simon Sessingø 5151461a02 Added setMiddleware deprecated method in Route class. 2017-07-08 11:42:27 +02:00
Simon Sessingø b07348a3df Development
- Removed temporary `RouterException` class.
- Added object-types to parameters in `CallbackExceptionHandler` and `SimpleRouter` classes.
- Router now renders groups even if callback is null.
- Renamed `setMiddleware` to `addMiddleware` in `Route` class and `IRoute` interface.
- `addMiddleware` now accept both object and class strings in `Route` class.
- `addExceptionHandler` now accept both object and class strings in `RouteGroup` class.
- Added unit-test for rewrite-exception message change: `testRewriteExceptionMessage` in `RouterRewriteTest`.
- Fixed typo: renamed `testSimularUrls` to `testSimilarUrls` in `RouterUrlTest`.
2017-07-08 11:21:18 +02:00
Simon Sessingø d411b31cc2 Changed message-text thrown in NotFoundHttpException to show redirected routes (issue: #240) 2017-07-06 15:46:09 +02:00
Simon Sessingø ca381d445f Development
- Changed router so it supports both string and object as exception handlers.
- Added `Router::error($callback)` method to `Router` class (issue #238).
- Fixed issues calling `getController` and `getMethod` when callback is an object (issue #239).
- Updated documentation to reflect new changes.
- Added `addExceptionHandler` to `IGroupRoute` interface and `RouteGroup` class.
- Other minor bugfixes and optimisations.
2017-07-06 15:25:04 +02:00
Simon Sessingø 98ee60859f Merge pull request #236 from skipperbent/v3-development
Development
2017-05-09 07:06:18 +02:00
Simon Sessingø 9dd80dd1d9 Development
- Fixed global regex match not working properly.
- Feature: added option to change regular expression used for parameters on routes.
- Added unit-tests for custom parameter regular expression.
- Updated documentation to reflect new features.
2017-05-09 06:43:26 +02:00
Simon Sessingø fef997422e Merge pull request #234 from skipperbent/v3-development
V3 development
2017-05-09 02:51:58 +02:00
Simon Sessingø 8901e7c125 Development
- Added check in `CsrfToken` class to ensure that IV generation is strong and secure.
- Minor optimisations mostly related to PHPDocs and PHPStorm code-inspection.
2017-05-09 02:49:41 +02:00
Simon Sessingø 50e8926272 Added .htaccess example to documentation (issue #232) 2017-05-09 02:31:01 +02:00
Simon Sessingø 27ff761d18 Merge pull request #230 from skipperbent/v3-development
V3 development
2017-03-06 02:56:37 +01:00
Simon Sessingø 0a58d36606 Development
- Fixed: `RouteController` not matching certain urls.
- Made `RouteResource` more strict in url-matching.
- Added PHPUnit `RouterControllerTest` class.
- Fixed merged `testSimularUrls` method in `RouterUrlTest`.
2017-03-06 02:53:44 +01:00
Simon Sessingø db024b9588 Added simular routes PHPUnit test 2017-03-06 02:21:13 +01:00
22 changed files with 516 additions and 120 deletions
+1 -2
View File
@@ -1,4 +1,3 @@
.idea
composer.lock
vendor/
demo-project/vendor
vendor/
+84 -20
View File
@@ -51,8 +51,9 @@ If you want a great new feature or experience any issues what-so-ever, please fe
- [Middlewares](#middlewares)
- [Example](#example)
- [ExceptionHandler](#exceptionhandler)
- [Example](#example-1)
- [ExceptionHandlers](#exceptionhandlers)
- [Handling 404, 403 and other errors](#handling-404-403-and-other-errors)
- [Using custom exception handlers](#using-custom-exception-handlers)
- [Urls](#urls)
- [Get by name (single route)](#get-by-name-single-route)
@@ -70,12 +71,13 @@ If you want a great new feature or experience any issues what-so-ever, please fe
- [Get all parameters](#get-all-parameters)
- [Advanced](#advanced)
- [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically)
- [Url rewriting](#url-rewriting)
- [Rewrite using callback](#rewrite-using-callback)
- [Rewrite using url](#rewrite-using-url)
- [Adding routes manually](#adding-routes-manually)
- [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically)
- [Adding routes manually](#adding-routes-manually)
- [Parameters](#parameters)
- [Custom default regex for matching parameters](#custom-default-regex-for-matching-parameters)
- [Extending](#extending)
- [Credits](#credits)
@@ -155,6 +157,20 @@ location / {
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.
#### .htaccess example
Below is an example of an working `.htaccess` file used by simple-php-router.
Simply create a new `.htaccess` file in your projects `public` directory and paste the contents below in your newly created file. This will redirect all requests to your `index.php` file (see Configuration section below).
```
RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-l
RewriteRule ^(.*)$ index.php/$1
```
### Configuration
Create a new file, name it `routes.php` and place it in your library folder. This will be the file where you define all the routes for your project.
@@ -382,7 +398,7 @@ Named routes allow the convenient generation of URLs or redirects for specific r
```php
SimpleRouter::get('/user/profile', function () {
//
// Your code here
})->name('profile');
```
@@ -611,13 +627,30 @@ class CustomMiddleware implements Middleware {
---
# ExceptionHandler
# ExceptionHandlers
ExceptionHandler are classes that handles all exceptions. ExceptionsHandlers must implement the `IExceptionHandler` interface.
## Example
## Handling 404, 403 and other errors
Resource controllers can implement the `IRestController` interface, but is not required.
If you simply want to catch a 404 (page not found) etc. you can use the `Router::error($callback)` static helper method.
This will add a callback method which is fired whenever an error occurs on all routes.
The basic example below simply redirect the page to `/not-found` if an `NotFoundHttpException` (404) occurred.
The code should be placed in the file that contains your routes.
```php
Router::get('/not-found', 'PageController@notFound');
Router::error(function(Request $request, \Exception $exception) {
if($exception instanceof NotFoundHttpException && $exception->getCode == 404) {
response()->redirect('/not-found');
}
});
```
## Using custom exception handlers
This is a basic example of an ExceptionHandler implementation (please see "[Easily overwrite route about to be loaded](#easily-overwrite-route-about-to-be-loaded)" for examples on how to change callback).
@@ -888,11 +921,9 @@ $request->setRewriteCallback('Example\MyCustomClass@hello');
$request->setRewriteUrl('/my-rewrite-url');
```
### Examples
**Note:** It's only possible to change the route BEFORE the route has initially been rendered. You can use the `Request` object to manipulate the route which are about to be loaded.
It's only possible to change the route BEFORE the route has initially been rendered. You can use the `Request` object to manipulate the route which are about to be loaded.
#### Rewrite using callback
### Rewrite using callback
This method is most efficient, as it will render the route immediately.
@@ -908,7 +939,7 @@ The example below will render `DefaultController@notFound` regardless of the url
**NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.**
#### Middleware example
##### Middleware example
```php
namespace Demo\Middlewares;
@@ -928,7 +959,7 @@ class CustomMiddleware implements IMiddleware {
}
```
#### Exception handler example
##### Exception handler example
```php
namespace Demo\Handlers;
@@ -963,7 +994,7 @@ class CustomExceptionHandler implements IExceptionHandler
}
```
#### Rewrite using url
### Rewrite using url
The example below will cause the router to reload the request and reinitialize all the routes. This method is slower, but will ensure that all middlewares and rules for the route is loaded.
@@ -974,7 +1005,7 @@ We are using the `url()` helper function to get the uri to another route added i
**NOTE: Use this method if you want to fully load another route using it's settings (request method, middlewares etc).**
#### Middleware example
##### Middleware example
The example below will redirect the request to the `home`-route.
@@ -995,7 +1026,7 @@ class CustomMiddleware implements IMiddleware {
}
```
# Bootmanager: loading routes dynamically
### Bootmanager: loading routes dynamically
Sometimes it can be necessary to keep urls stored in the database, file or similar. In this example, we want the url ```/my-cat-is-beatiful``` to load the route ```/article/view/1``` which the router knows, because it's defined in the ```routes.php``` file.
@@ -1041,7 +1072,7 @@ The last thing we need to do, is to add our custom boot-manager to the ```routes
SimpleRouter::addBootManager(new CustomRouterRules());
```
## Adding routes manually
### Adding routes manually
The ```SimpleRouter``` class referenced in the previous example, is just a simple helper class that knows how to communicate with the ```Router``` class.
If you are up for a challenge, want the full control or simply just want to create your own ```Router``` helper class, this example is for you.
@@ -1059,7 +1090,7 @@ $route = new RouteUrl('/answer/1', function() {
});
$route->setMiddleware(\Demo\Middlewares\AuthMiddleware::class);
$route->addMiddleware(\Demo\Middlewares\AuthMiddleware::class);
$route->setNamespace('\Demo\Controllers');
$route->setPrefix('v1');
@@ -1067,6 +1098,39 @@ $route->setPrefix('v1');
$router->addRoute($route);
```
## Parameters
This section contains advanced tips & tricks on extending the usage for parameters.
### Custom default regex for matching parameters
By default simple-php-router uses the `\w` regular expression when matching parameters.
This decision was made with speed and reliability in mind, as this match will match both letters, number and most of the used symbols on the internet.
However, sometimes it can be nessesary to add a custom regular expression to match more advanced characters like `-` etc.
Instead of adding a custom regular expression to all your parameters, you can simply add a global regular expression which will be used on all the parameters on the route.
**Note:** If you the regular expression to be available across, we recommend using the global parameter on a group as demonstrated in the examples below.
#### Route
This example will ensure that all parameters use the `[\w\-]+` regular expression when parsing.
```php
SimpleRouter::get('/path/{parameter}', 'VideoController@home', ['defaultParameterRegex' => '[\w\-]+']);
```
You can also apply this setting to a group if you need multiple routes to use your custom regular expression when parsing parameters.
```php
SimpleRouter::group(['defaultParameterRegex' => '[\w\-]+'], function() {
SimpleRouter::get('/path/{parameter}', 'VideoController@home');
});
```
## Extending
This is a simple example of an integration into a framework.
+11 -3
View File
@@ -3,13 +3,14 @@ namespace Pecee;
class CsrfToken
{
const CSRF_KEY = 'XSRF-TOKEN';
const CSRF_KEY = 'CSRF-TOKEN';
protected $token;
/**
* Generate random identifier for CSRF token
*
* @throws \RuntimeException
* @return string
*/
public static function generateToken()
@@ -18,7 +19,14 @@ class CsrfToken
return bin2hex(random_bytes(32));
}
return bin2hex(openssl_random_pseudo_bytes(32));
$isSourceStrong = false;
$random = openssl_random_pseudo_bytes(32, $isSourceStrong);
if ($isSourceStrong === false || $random === false) {
throw new \RuntimeException('IV generation failed');
}
return $random;
}
/**
@@ -52,7 +60,7 @@ class CsrfToken
*/
public function getToken()
{
if ($this->hasToken()) {
if ($this->hasToken() === true) {
return $_COOKIE[static::CSRF_KEY];
}
@@ -0,0 +1,38 @@
<?php
namespace Pecee\Handlers;
use Pecee\Http\Request;
/**
* Class CallbackExceptionHandler
*
* Class is used to create callbacks which are fired when an exception is reached.
* This allows for easy handling 404-exception etc. without creating an custom ExceptionHandler.
*
* @package Pecee\Handlers
*/
class CallbackExceptionHandler implements IExceptionHandler
{
protected $callback;
public function __construct(\Closure $callback)
{
$this->callback = $callback;
}
/**
* @param Request $request
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, \Exception $error)
{
/* Fire exceptions */
return call_user_func($this->callback,
$request,
$error
);
}
}
+2 -2
View File
@@ -60,7 +60,7 @@ class Input
{
$list = [];
foreach ($_FILES as $key => $value) {
foreach ((array)$_FILES as $key => $value) {
// Handle array input
if (is_array($value['name']) === false) {
@@ -108,11 +108,11 @@ class Input
$file = InputFile::createFromArray([
'index' => $key,
'filename' => $getItem($key),
'error' => $getItem($key, 'error'),
'tmp_name' => $getItem($key, 'tmp_name'),
'type' => $getItem($key, 'type'),
'size' => $getItem($key, 'size'),
'filename' => $getItem($key, 'name'),
]);
if (isset($output[$key])) {
+1 -1
View File
@@ -8,7 +8,7 @@ use Pecee\SimpleRouter\SimpleRouter;
class Request
{
protected $data = [];
private $data = [];
protected $headers;
protected $host;
protected $uri;
@@ -1,6 +1,8 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
interface IGroupRoute extends IRoute
@@ -13,6 +15,14 @@ interface IGroupRoute extends IRoute
*/
public function matchDomain(Request $request);
/**
* Add exception handler
*
* @param IExceptionHandler|string $handler
* @return static $this;
*/
public function addExceptionHandler($handler);
/**
* Set exception-handlers for group
*
+2 -1
View File
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
@@ -173,7 +174,7 @@ interface IRoute
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware);
public function addMiddleware($middleware);
/**
* Set middlewares array
+11 -16
View File
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Middleware\IMiddleware;
@@ -35,10 +36,12 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
$middleware = $this->getMiddlewares()[$i];
$middleware = $this->loadClass($middleware);
if (is_object($middleware) === false) {
$middleware = $this->loadClass($middleware);
}
if (($middleware instanceof IMiddleware) === false) {
throw new HttpException($middleware . ' must be instance of Middleware');
throw new HttpException($middleware . ' must be inherit the IMiddleware interface');
}
$middleware->handle($request);
@@ -54,17 +57,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
return null;
}
$parameters = [];
if (preg_match($this->regex, $request->getHost() . $url, $parameters) > 0) {
/* Remove global match */
$this->parameters = array_slice($parameters, 1);
return true;
}
return false;
return (preg_match($this->regex, $request->getHost() . $url) > 0);
}
/**
@@ -79,7 +72,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
if (strpos($this->url, $this->paramModifiers[0]) !== false) {
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
$regex = sprintf(static::PARAMETERS_REGEX_FORMAT, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
if (preg_match_all('/' . $regex . '/', $this->url, $matches)) {
$this->parameters = array_fill_keys($matches[1], null);
@@ -107,8 +100,10 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
{
$url = $this->getUrl();
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
$url = '//' . $this->getGroup()->getDomains()[0] . $url;
$group = $this->getGroup();
if ($group !== null && count($group->getDomains()) > 0) {
$url = '//' . $group->getDomains()[0] . $url;
}
/* Contains parameters that aren't recognized and will be appended at the end of the url */
+107 -38
View File
@@ -1,12 +1,15 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
abstract class Route implements IRoute
{
const PARAMETERS_REGEX_MATCH = '%s([\w]+)(\%s?)%s';
const PARAMETERS_REGEX_FORMAT = '%s([\w]+)(\%s?)%s';
const PARAMETERS_DEFAULT_REGEX = '[\w]+';
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_POST = 'post';
@@ -31,6 +34,7 @@ abstract class Route implements IRoute
* @var bool
*/
protected $filterEmptyParams = false;
protected $defaultParameterRegex = null;
protected $paramModifiers = '{}';
protected $paramOptionalSymbol = '?';
protected $group;
@@ -49,7 +53,7 @@ abstract class Route implements IRoute
protected function loadClass($name)
{
if (class_exists($name) === false) {
throw new NotFoundHttpException(sprintf('Class %s does not exist', $name), 404);
throw new NotFoundHttpException(sprintf('Class "%s" does not exist', $name), 404);
}
return new $name();
@@ -59,44 +63,50 @@ abstract class Route implements IRoute
{
$callback = $this->getCallback();
if ($callback !== null && is_callable($callback)) {
if ($callback === null) {
return;
}
/* Render callback function */
if (is_callable($callback) === true) {
/* When the callback is a function */
call_user_func_array($callback, $this->getParameters());
} else {
return;
/* When the callback is a method */
$controller = explode('@', $callback);
$namespace = $this->getNamespace();
$className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (method_exists($class, $method) === false) {
throw new NotFoundHttpException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
$parameters = $this->getParameters();
/* Filter parameters with null-value */
if ($this->filterEmptyParams === true) {
$parameters = array_filter($parameters, function ($var) {
return ($var !== null);
});
}
call_user_func_array([$class, $method], $parameters);
}
/* When the callback is a class + method */
$controller = explode('@', $callback);
$namespace = $this->getNamespace();
$className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (method_exists($class, $method) === false) {
throw new NotFoundHttpException(sprintf('Method "%s" does not exist in class "%s"', $method, $className), 404);
}
$parameters = $this->getParameters();
/* Filter parameters with null-value */
if ($this->filterEmptyParams === true) {
$parameters = array_filter($parameters, function ($var) {
return ($var !== null);
});
}
call_user_func_array([$class, $method], $parameters);
}
protected function parseParameters($route, $url, $parameterRegex = '[\w]+')
protected function parseParameters($route, $url, $parameterRegex = null)
{
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
$regex = sprintf(static::PARAMETERS_REGEX_FORMAT, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
$parameters = [];
@@ -111,7 +121,20 @@ abstract class Route implements IRoute
if ($key < count($parameters[1])) {
$name = $parameters[1][$key];
$regex = isset($this->where[$name]) ? $this->where[$name] : $parameterRegex;
/* If custom regex is defined, use that */
if (isset($this->where[$name]) === true) {
$regex = $this->where[$name];
} else {
/* If method specific regex is defined use that, otherwise use the default parameter regex */
if ($parameterRegex !== null) {
$regex = $parameterRegex;
} else {
$regex = ($this->defaultParameterRegex === null) ? static::PARAMETERS_DEFAULT_REGEX : $this->defaultParameterRegex;
}
}
$regex = sprintf('\-?\/?(?P<%s>%s)', $name, $regex) . $parameters[2][$key];
}
@@ -129,7 +152,7 @@ abstract class Route implements IRoute
$values = [];
if (isset($parameters[1])) {
if (isset($parameters[1]) === true) {
/* Only take matched parameters with name */
foreach ($parameters[1] as $name) {
@@ -152,7 +175,7 @@ abstract class Route implements IRoute
*/
public function getIdentifier()
{
if (strpos($this->callback, '@') !== false) {
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
return $this->callback;
}
@@ -249,7 +272,7 @@ abstract class Route implements IRoute
public function getMethod()
{
if (strpos($this->callback, '@') !== false) {
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[1];
@@ -260,7 +283,7 @@ abstract class Route implements IRoute
public function getClass()
{
if (strpos($this->callback, '@') !== false) {
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[0];
@@ -343,6 +366,10 @@ abstract class Route implements IRoute
$values['middleware'] = $this->middlewares;
}
if ($this->defaultParameterRegex !== null) {
$values['defaultParameterRegex'] = $this->defaultParameterRegex;
}
return $values;
}
@@ -376,6 +403,10 @@ abstract class Route implements IRoute
$this->setMiddlewares(array_merge((array)$values['middleware'], $this->middlewares));
}
if (isset($values['defaultParameterRegex'])) {
$this->setDefaultParameterRegex($values['defaultParameterRegex']);
}
return $this;
}
@@ -454,9 +485,10 @@ abstract class Route implements IRoute
}
/**
* Set middleware class-name
* Add middleware class-name
*
* @param string $middleware
* @deprecated This method is deprecated and will be removed in the near future.
* @param IMiddleware|string $middleware
* @return static
*/
public function setMiddleware($middleware)
@@ -466,6 +498,19 @@ abstract class Route implements IRoute
return $this;
}
/**
* Add middleware class-name
*
* @param IMiddleware|string $middleware
* @return static
*/
public function addMiddleware($middleware)
{
$this->middlewares[] = $middleware;
return $this;
}
/**
* Set middlewares array
*
@@ -487,4 +532,28 @@ abstract class Route implements IRoute
return $this->middlewares;
}
/**
* Set default regular expression used when matching parameters.
* This is used when no custom parameter regex is found.
*
* @param string $regex
* @return static $this
*/
public function setDefaultParameterRegex($regex)
{
$this->defaultParameterRegex = $regex;
return $this;
}
/**
* Get default regular expression used when matching parameters.
*
* @return string
*/
public function getDefaultParameterRegex()
{
return $this->defaultParameterRegex;
}
}
@@ -66,7 +66,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
foreach (static::$requestTypes as $requestType) {
if (stripos($method, $requestType) === 0) {
$method = substr($method, strlen($requestType));
$method = (string)substr($method, strlen($requestType));
break;
}
}
@@ -74,8 +74,10 @@ class RouteController extends LoadableRoute implements IControllerRoute
$method .= '/';
}
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
$url .= '//' . $this->getGroup()->getDomains()[0];
$group = $this->getGroup();
if ($group !== null && count($group->getDomains()) > 0) {
$url .= '//' . $group->getDomains()[0];
}
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower($method) . join('/', $parameters);
@@ -91,7 +93,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false || stripos($url, $this->url) !== 0 || strtolower($url) !== strtolower($this->url)) {
if ($regexMatch === false || (stripos($url, $this->url) !== 0 && strtolower($url) !== strtolower($this->url))) {
return false;
}
+16 -1
View File
@@ -1,6 +1,8 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
class RouteGroup extends Route implements IGroupRoute
@@ -18,7 +20,7 @@ class RouteGroup extends Route implements IGroupRoute
*/
public function matchDomain(Request $request)
{
if (count($this->domains) === 0) {
if ($this->domains === null || count($this->domains) === 0) {
return true;
}
@@ -54,6 +56,19 @@ class RouteGroup extends Route implements IGroupRoute
return $this->matchDomain($request);
}
/**
* Add exception handler
*
* @param IExceptionHandler|string $handler
* @return static $this
*/
public function addExceptionHandler($handler)
{
$this->exceptionHandlers[] = $handler;
return $this;
}
/**
* Set exception-handlers for group
*
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
@@ -53,7 +54,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute
/* Remove method/type */
if (strpos($name, '.') !== false) {
$name = substr($name, 0, strrpos($name, '.'));
$name = (string)substr($name, 0, strrpos($name, '.'));
}
return (strtolower($this->name) === strtolower($name));
@@ -84,14 +85,17 @@ class RouteResource extends LoadableRoute implements IControllerRoute
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false) {
if ($regexMatch === false || (stripos($url, $this->url) !== 0 && strtolower($url) !== strtolower($this->url))) {
return false;
}
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
/* Parse parameters from current route */
$this->parameters = $this->parseParameters($route, $url);
if ($this->parameters === null) {
/* If no custom regular expression or parameters was found on this route, we stop */
if ($regexMatch === null && $this->parameters === null) {
return false;
}
+8 -3
View File
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
@@ -18,17 +19,21 @@ class RouteUrl extends LoadableRoute
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false) {
return false;
}
/* Make regular expression based on route */
/* Parse parameters from current route */
$parameters = $this->parseParameters($this->url, $url);
if ($parameters === null) {
/* If no custom regular expression or parameters was found on this route, we stop */
if ($regexMatch === null && $parameters === null) {
return false;
}
$this->setParameters($parameters);
/* Set the parameters */
$this->setParameters((array)$parameters);
return true;
}
+41 -17
View File
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Handlers\IExceptionHandler;
@@ -133,20 +134,18 @@ class Router
$group = $route;
if ($route->getCallback() !== null && is_callable($route->getCallback())) {
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($url, $this->request) === true) {
/* Add exception handlers */
if (count($route->getExceptionHandlers()) > 0) {
$exceptionHandlers += $route->getExceptionHandlers();
}
if ($route->matchRoute($url, $this->request) === true) {
/* Add exception handlers */
if (count($route->getExceptionHandlers()) > 0) {
/** @noinspection AdditionOperationOnArraysInspection */
$exceptionHandlers += $route->getExceptionHandlers();
}
}
}
@@ -183,7 +182,7 @@ class Router
}
}
$this->exceptionHandlers = array_unique(array_merge($exceptionHandlers, $this->exceptionHandlers));
$this->exceptionHandlers = array_merge($exceptionHandlers, $this->exceptionHandlers);
}
/**
@@ -283,7 +282,16 @@ class Router
}
if ($this->request->getLoadedRoute() === null) {
$this->handleException(new NotFoundHttpException('Route not found: ' . $this->request->getUri(), 404));
$rewriteUrl = $this->request->getRewriteUrl();
if ($rewriteUrl !== null) {
$message = sprintf('Route not found: "%s" (rewrite from: "%s")', $rewriteUrl, $this->request->getUri());
} else {
$message = sprintf('Route not found: "%s"', $this->request->getUri());
}
$this->handleException(new NotFoundHttpException($message, 404));
}
}
@@ -297,7 +305,10 @@ class Router
for ($i = 0; $i < $max; $i++) {
$handler = $this->exceptionHandlers[$i];
$handler = new $handler();
if (is_object($handler) === false) {
$handler = new $handler();
}
if (($handler instanceof IExceptionHandler) === false) {
throw new HttpException('Exception handler must implement the IExceptionHandler interface.', 500);
@@ -372,7 +383,7 @@ class Router
}
/* Using @ is most definitely a controller@method or alias@method */
if (strpos($name, '@') !== false) {
if (is_string($name) === true && strpos($name, '@') !== false) {
list($controller, $method) = array_map('strtolower', explode('@', $name));
if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) {
@@ -381,7 +392,7 @@ class Router
}
/* Check if callback matches (if it's not a function) */
if (strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && !is_callable($route->getCallback())) {
if (is_string($name) === true && is_string($route->getCallback()) && strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && is_callable($route->getCallback()) === false) {
/* Check if the entire callback is matching */
if (strpos($route->getCallback(), $name) === 0 || strtolower($route->getCallback()) === strtolower($name)) {
@@ -451,7 +462,7 @@ class Router
}
/* Using @ is most definitely a controller@method or alias@method */
if (strpos($name, '@') !== false) {
if (is_string($name) === true && strpos($name, '@') !== false) {
list($controller, $method) = explode('@', $name);
/* Loop through all the routes to see if we can find a match */
@@ -517,6 +528,19 @@ class Router
return $this->routes;
}
/**
* Set routes
*
* @param array $routes
* @return static $this
*/
public function setRoutes(array $routes)
{
$this->routes = $routes;
return $this;
}
/**
* Get current request
*
+33 -5
View File
@@ -7,8 +7,10 @@
* This class is added so calls can be made statically like Router::get() making the code look pretty.
* It also adds some extra functionality like default-namespace.
*/
namespace Pecee\SimpleRouter;
use Pecee\Handlers\CallbackExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Response;
use Pecee\SimpleRouter\Exceptions\HttpException;
@@ -33,6 +35,10 @@ class SimpleRouter
*/
protected static $response;
/**
* Router instance
* @var Router
*/
protected static $router;
/**
@@ -214,7 +220,7 @@ class SimpleRouter
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
* @return RouteUrl|IRoute
*/
public static function match(array $requestMethods, $url, $callback, array $settings = null)
{
@@ -237,7 +243,7 @@ class SimpleRouter
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
* @return RouteUrl|IRoute
*/
public static function all($url, $callback, array $settings = null)
{
@@ -259,7 +265,7 @@ class SimpleRouter
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteController
* @return RouteController|IRoute
*/
public static function controller($url, $controller, array $settings = null)
{
@@ -281,7 +287,7 @@ class SimpleRouter
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteResource
* @return RouteResource|IRoute
*/
public static function resource($url, $controller, array $settings = null)
{
@@ -297,6 +303,28 @@ class SimpleRouter
return $route;
}
/**
* Add exception callback handler.
*
* @param \Closure $callback
* @return CallbackExceptionHandler $callbackHandler
*/
public static function error(\Closure $callback)
{
$routes = static::router()->getRoutes();
$callbackHandler = new CallbackExceptionHandler($callback);
$group = new RouteGroup();
$group->addExceptionHandler($callbackHandler);
array_unshift($routes, $group);
static::router()->setRoutes($routes);
return $callbackHandler;
}
/**
* Get url for a route by using either name/alias, class or method name.
*
@@ -351,7 +379,7 @@ class SimpleRouter
*/
public static function router()
{
if(static::$router === null) {
if (static::$router === null) {
static::$router = new Router();
}
+15
View File
@@ -17,4 +17,19 @@ class DummyController
echo join(', ', func_get_args());
}
public function getTest()
{
echo 'getTest';
}
public function postTest()
{
echo 'postTest';
}
public function putTest()
{
echo 'putTest';
}
}
@@ -0,0 +1,27 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exceptions/ExceptionHandlerException.php';
require_once 'Helpers/TestRouter.php';
class RouterCallbackExceptionHandlerTest extends PHPUnit_Framework_TestCase
{
public function testCallbackExceptionHandler()
{
$this->setExpectedException(ExceptionHandlerException::class);
// Match normal route on alias
TestRouter::get('/my-new-url', 'DummyController@method2');
TestRouter::get('/my-url', 'DummyController@method1');
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $exception) {
throw new ExceptionHandlerException();
});
TestRouter::debugNoReset('/404-url', 'get');
TestRouter::router()->reset();
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
require_once 'Dummy/DummyController.php';
require_once 'Helpers/TestRouter.php';
class RouterControllerTest extends PHPUnit_Framework_TestCase
{
public function testGet()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'get');
$this->assertEquals('getTest', $response);
}
public function testPost()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'post');
$this->assertEquals('postTest', $response);
}
public function testPut()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'put');
$this->assertEquals('putTest', $response);
}
}
+19 -1
View File
@@ -1,4 +1,5 @@
<?php
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exceptions/ResponseException.php';
require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
@@ -43,7 +44,7 @@ class RouteRewriteTest extends PHPUnit_Framework_TestCase
try {
TestRouter::debug('/my-non-existing-path', 'get');
} catch(\ResponseException $e) {
} catch (\ResponseException $e) {
}
@@ -57,4 +58,21 @@ class RouteRewriteTest extends PHPUnit_Framework_TestCase
}
public function testRewriteExceptionMessage()
{
$this->setExpectedException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class);
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) {
if (strtolower($request->getUri()) == '/my/test') {
$request->setRewriteUrl('/another-non-existing');
return $request;
}
});
TestRouter::debug('/my/test', 'get');
}
}
+22 -3
View File
@@ -77,10 +77,10 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
public function testPathParamRegex()
{
TestRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
$response = TestRouter::debugOutput('/test/path/123123', 'get');
TestRouter::get('/{lang}/productscategories/{name}', 'DummyController@param', ['where' => ['lang' => '[a-z]+', 'name' => '[A-Za-z0-9\-]+']]);
$response = TestRouter::debugOutput('/it/productscategories/system', 'get');
$this->assertEquals('123123', $response);
$this->assertEquals('it, system', $response);
}
public function testDomainAllowedRoute()
@@ -125,4 +125,23 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
TestRouter::debug('/my/custom-path', 'get');
}
public function testDefaultParameterRegex()
{
TestRouter::get('/my/{path}', 'DummyController@param', ['defaultParameterRegex' => '[\w\-]+']);
$output = TestRouter::debugOutput('/my/custom-regex', 'get');
$this->assertEquals('custom-regex', $output);
}
public function testDefaultParameterRegexGroup()
{
TestRouter::group(['defaultParameterRegex' => '[\w\-]+'], function() {
TestRouter::get('/my/{path}', 'DummyController@param');
});
$output = TestRouter::debugOutput('/my/custom-regex', 'get');
$this->assertEquals('custom-regex', $output);
}
}
+13
View File
@@ -8,6 +8,19 @@ require_once 'Helpers/TestRouter.php';
class RouterUrlTest extends PHPUnit_Framework_TestCase
{
public function testSimilarUrls()
{
// Match normal route on alias
TestRouter::resource('/url11', 'DummyController@method1');
TestRouter::resource('/url1', 'DummyController@method1', ['as' => 'match']);
TestRouter::debugNoReset('/url1', 'get');
$this->assertEquals(TestRouter::getUrl('match'), TestRouter::getUrl());
TestRouter::router()->reset();
}
public function testUrls()
{
// Match normal route on alias