mirror of
https://github.com/skipperbent/simple-php-router.git
synced 2026-06-17 16:57:53 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a40f81d5fc | |||
| a5527a0e8c | |||
| 3c73de866e | |||
| 883d8a6b0e | |||
| 3a7b27796a | |||
| 4cca5186f3 | |||
| 5437420175 | |||
| 410fa3c9f5 | |||
| be20fe4dd1 | |||
| 0947f6746e | |||
| 31c8710ce7 | |||
| 1ac7761d35 | |||
| 9eb7c5c13c | |||
| 5151461a02 | |||
| b07348a3df | |||
| d411b31cc2 | |||
| ca381d445f | |||
| 98ee60859f | |||
| 9dd80dd1d9 |
+1
-2
@@ -1,4 +1,3 @@
|
||||
.idea
|
||||
composer.lock
|
||||
vendor/
|
||||
demo-project/vendor
|
||||
vendor/
|
||||
@@ -34,6 +34,7 @@ If you want a great new feature or experience any issues what-so-ever, please fe
|
||||
- [Optional parameters](#optional-parameters)
|
||||
- [Regular expression constraints](#regular-expression-constraints)
|
||||
- [Regular expression route-match](#regular-expression-route-match)
|
||||
- [Custom regex for matching parameters](#custom-regex-for-matching-parameters)
|
||||
- [Named routes](#named-routes)
|
||||
- [Generating URLs To Named Routes](#generating-urls-to-named-routes)
|
||||
- [Router groups](#router-groups)
|
||||
@@ -51,8 +52,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 +72,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)
|
||||
@@ -379,7 +382,7 @@ The example below is using the following regular expression: `/ajax/([\w]+)/?([0
|
||||
|
||||
**Matches:** `/ajax/abc/`, `/ajax/abc/123/`
|
||||
|
||||
**Doesn't match:** `/ajax/`
|
||||
**Won't match:** `/ajax/`
|
||||
|
||||
Match groups specified in the regex will be passed on as parameters:
|
||||
|
||||
@@ -390,13 +393,42 @@ SimpleRouter::all('/ajax/abc/123', function($param1, $param2) {
|
||||
})->setMatch('/\/ajax\/([\w]+)\/?([0-9]+)?\/?/is');
|
||||
```
|
||||
|
||||
### Custom 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 necessary 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.
|
||||
|
||||
#### Example
|
||||
|
||||
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');
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
## Named routes
|
||||
|
||||
Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the name method onto the route definition:
|
||||
|
||||
```php
|
||||
SimpleRouter::get('/user/profile', function () {
|
||||
//
|
||||
// Your code here
|
||||
})->name('profile');
|
||||
```
|
||||
|
||||
@@ -625,13 +657,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).
|
||||
|
||||
@@ -902,11 +951,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.
|
||||
|
||||
@@ -922,7 +969,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;
|
||||
@@ -942,7 +989,7 @@ class CustomMiddleware implements IMiddleware {
|
||||
}
|
||||
```
|
||||
|
||||
#### Exception handler example
|
||||
##### Exception handler example
|
||||
|
||||
```php
|
||||
namespace Demo\Handlers;
|
||||
@@ -977,7 +1024,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.
|
||||
|
||||
@@ -988,7 +1035,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.
|
||||
|
||||
@@ -1009,7 +1056,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.
|
||||
|
||||
@@ -1055,7 +1102,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.
|
||||
@@ -1073,7 +1120,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');
|
||||
|
||||
@@ -1081,6 +1128,10 @@ $route->setPrefix('v1');
|
||||
$router->addRoute($route);
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
This section contains advanced tips & tricks on extending the usage for parameters.
|
||||
|
||||
## Extending
|
||||
|
||||
This is a simple example of an integration into a framework.
|
||||
|
||||
+4
-1
@@ -2,11 +2,14 @@
|
||||
"name": "pecee/simple-router",
|
||||
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
|
||||
"keywords": [
|
||||
"router",
|
||||
"router",
|
||||
"routing",
|
||||
"route",
|
||||
"simple-php-router",
|
||||
"laravel",
|
||||
"pecee",
|
||||
"route"
|
||||
"php"
|
||||
],
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Pecee;
|
||||
|
||||
class CsrfToken
|
||||
{
|
||||
const CSRF_KEY = 'XSRF-TOKEN';
|
||||
const CSRF_KEY = 'CSRF-TOKEN';
|
||||
|
||||
protected $token;
|
||||
|
||||
@@ -60,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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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])) {
|
||||
@@ -283,15 +283,7 @@ class Input
|
||||
}
|
||||
}
|
||||
|
||||
$output = array_merge($_GET, $output);
|
||||
|
||||
if ($filter !== null) {
|
||||
$output = array_filter($output, function ($key) use ($filter) {
|
||||
return (in_array($key, $filter) === true);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
}
|
||||
|
||||
return $output;
|
||||
return ($filter !== null) ? array_intersect_key($output, array_flip($filter)) : array_merge($_GET, $output);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,47 +63,56 @@ 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 = [];
|
||||
|
||||
// Ensures that hostnames/domains will work with parameters
|
||||
$url = '/' . ltrim($url, '/');
|
||||
|
||||
if (preg_match_all('/' . $regex . '/', $route, $parameters)) {
|
||||
|
||||
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', rtrim($route, '/'));
|
||||
@@ -111,8 +124,21 @@ abstract class Route implements IRoute
|
||||
if ($key < count($parameters[1])) {
|
||||
|
||||
$name = $parameters[1][$key];
|
||||
$regex = isset($this->where[$name]) ? $this->where[$name] : $parameterRegex;
|
||||
$regex = sprintf('\-?\/?(?P<%s>%s)', $name, $regex) . $parameters[2][$key];
|
||||
|
||||
/* 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('(?:\/|\-)' . $parameters[2][$key] . '(?P<%s>%s)', $name, $regex) . $parameters[2][$key];
|
||||
|
||||
}
|
||||
|
||||
@@ -125,11 +151,11 @@ abstract class Route implements IRoute
|
||||
$urlRegex = preg_quote($route, '/');
|
||||
}
|
||||
|
||||
if (preg_match('/^' . $urlRegex . '(\/?)$/', $url, $matches) > 0) {
|
||||
if (preg_match('/^' . $urlRegex . '\/?/', $url, $matches) > 0) {
|
||||
|
||||
$values = [];
|
||||
|
||||
if (isset($parameters[1])) {
|
||||
if (isset($parameters[1]) === true) {
|
||||
|
||||
/* Only take matched parameters with name */
|
||||
foreach ($parameters[1] as $name) {
|
||||
@@ -152,7 +178,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 +275,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 +286,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 +369,10 @@ abstract class Route implements IRoute
|
||||
$values['middleware'] = $this->middlewares;
|
||||
}
|
||||
|
||||
if ($this->defaultParameterRegex !== null) {
|
||||
$values['defaultParameterRegex'] = $this->defaultParameterRegex;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
@@ -376,6 +406,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 +488,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 +501,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 +535,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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
if (strpos($name, '.') !== false) {
|
||||
$found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, false);
|
||||
if ($found !== false) {
|
||||
$method = $found;
|
||||
$method = (string)$found;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -90,8 +91,11 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
@@ -301,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.
|
||||
*
|
||||
@@ -355,7 +379,7 @@ class SimpleRouter
|
||||
*/
|
||||
public static function router()
|
||||
{
|
||||
if(static::$router === null) {
|
||||
if (static::$router === null) {
|
||||
static::$router = new Router();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+26
-1
@@ -8,7 +8,32 @@ require_once 'Helpers/TestRouter.php';
|
||||
class RouterUrlTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testSimularUrls()
|
||||
public function testOptionalParameters()
|
||||
{
|
||||
TestRouter::get('/aviso/legal', 'DummyController@method1');
|
||||
TestRouter::get('/aviso/{aviso}', 'DummyController@method1');
|
||||
TestRouter::get('/pagina/{pagina}', 'DummyController@method1');
|
||||
TestRouter::get('/{pagina?}', 'DummyController@method1');
|
||||
|
||||
TestRouter::debugNoReset('/aviso/optional', 'get');
|
||||
$this->assertEquals('/aviso/{aviso}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/pagina/optional', 'get');
|
||||
$this->assertEquals('/pagina/{pagina}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/optional', 'get');
|
||||
$this->assertEquals('/{pagina?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/avisolegal', 'get');
|
||||
$this->assertNotEquals('/aviso/{aviso}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/avisolegal', 'get');
|
||||
$this->assertEquals('/{pagina?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
|
||||
public function testSimilarUrls()
|
||||
{
|
||||
// Match normal route on alias
|
||||
TestRouter::resource('/url11', 'DummyController@method1');
|
||||
|
||||
Reference in New Issue
Block a user