Compare commits

...

45 Commits

Author SHA1 Message Date
Simon Sessingø a40f81d5fc Merge pull request #251 from skipperbent/v3-development
V3 development
2017-08-01 21:30:42 +02:00
Simon Sessingø a5527a0e8c Merge pull request #250 from skipperbent/bugfix-urlmatch
Bugfix urlmatch
2017-08-01 21:30:13 +02:00
Simon Sessingø 3c73de866e Added code-comments. 2017-08-01 21:25:44 +02:00
Simon Sessingø 883d8a6b0e Fixes for issue #248 2017-08-01 21:09:18 +02:00
Simon Sessingø 3a7b27796a Url matching fixes 2017-08-01 18:29:47 +02:00
Simon Sessingø 4cca5186f3 Merge pull request #246 from skipperbent/v3-development
V3 development
2017-07-18 11:28:19 +02:00
Simon Sessingø 5437420175 Merge pull request #245 from skipperbent/php5-5-compatibility
Fixed PHP 5.5 compatibility (issue: #244 - thanks @thexeos)
2017-07-18 11:27:59 +02:00
Simon Sessingø 410fa3c9f5 Fixed PHP 5.5 compatibility (issue: #244 - thanks @thexeos) 2017-07-18 11:26:45 +02:00
Simon Sessingø be20fe4dd1 Updated composer.json tags. 2017-07-08 12:20:49 +02:00
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
Simon Sessingø 94d0764631 Merge pull request #228 from skipperbent/v3-development
Development
2017-03-05 17:10:27 +01:00
Simon Sessingø ff3f1bdcdd Development
- Fixed issue #227 causing custom resource-routes not to be loaded after latest update.
- Optimized `RouteResource` class.
- Renamed `IRestController` to `IResourceController`.
- Added unit-tests for RouterResource.
- Simplified unit-tests with the `TestRouter` custom router class.
2017-03-05 17:09:46 +01:00
Simon Sessingø 3e75d6ec0b Merge pull request #225 from skipperbent/v3-development
Development
2017-03-01 03:39:33 +01:00
Simon Sessingø 8c5d8c2dc9 Development
- Fixed: namespace being prepended on `RouterController` routes with absolute namespaces.
- Fixed: match domain on `RouteController` and `RouteResource`.
- Fixed: strict url-matching on `RouteController`. Fix should provide better url-matching.
- Fixed: `RouterResource` always matching first url when having simular urls (ex: `/funny-cat` and `/funny-dog`).
- Added `loadRoutes` method to `Router` class so routes now can be loaded without routing the request (useful when running in Cli etc).
- Removed `getInstance` from `Router` class. Please use `SimpleRouter::router()` for singleton usage instead.
- Added `getRemoteAddr` alias-method for `getIp` in `Request` class.
- Added `getValue` to `IInputItem` interface.
- Other minor optimizations.
- Updated documentation with note on absolute/relative namespaces.
2017-02-26 09:18:35 +01:00
Simon Sessingø 9d2bffcd02 Merge pull request #222 from skipperbent/v3-development
Development
2017-02-15 03:29:16 +01:00
Simon Sessingø 2a448fccd2 Development
- Fixed: only set default namespace on relative callbacks.
- Fixed: default-namespace not being set when calling `SimpleRouter::resource`.
- Minor optimisations.
2017-02-15 03:28:34 +01:00
Simon Sessingø e682cf0b7c Merge pull request #219 from skipperbent/v3-development
V3 development
2017-02-13 06:47:26 +01:00
Simon Sessingø c87298ee24 Optimisations 2017-02-13 06:45:07 +01:00
Simon Sessingø 79414255e0 Development
- Using `$request->setRewriteRoute($route);` rewrite-callback now renders any added middlewares on the route.
- Rewrite callbacks now sets default-namespace on route, if defined.
2017-02-13 06:29:22 +01:00
Simon Sessingø e913dd98da Merge pull request #217 from skipperbent/v3-development
Fixed: replace empty parameter-values with null.
2017-02-12 20:48:02 +01:00
Simon Sessingø ccd3cf450e Fixed: replace empty parameter-values with null. 2017-02-12 20:43:49 +01:00
Simon Sessingø 2654ca67db Merge pull request #214 from skipperbent/v3-development
Optimisations
2017-02-09 13:34:32 +01:00
Simon Sessingø 86c3e513c3 Optimisations 2017-02-09 13:33:36 +01:00
Simon Sessingø ce276bd5b7 Version 3
- Easier rewrite management.
- Optimisations.
- Updated documentation.
2017-02-09 13:18:05 +01:00
Simon Sessingø bf069c2d42 Fixed getExtension method in InputFile retuning empty value. 2017-02-01 07:51:35 +01:00
Simon Sessingø 523d49359b Development
- Fixed where method calling itself instead of setWhere.
- Added unit-tests for regex.
2017-01-19 18:40:47 +01:00
Simon Sessingø 7cc2652bcd getRoute optimisations. 2016-11-28 14:13:58 +01:00
Simon Sessingø e26d55a810 getUrl will now use default-parameter value if specificially set to null when using url(). 2016-11-28 09:37:05 +01:00
Simon Sessingø 8f3ce68a5e Optimised route-match behavior 2016-11-28 06:43:26 +01:00
Simon Sessingø d25351f4f9 Fixed parameters not merged with default values 2016-11-28 05:53:02 +01:00
45 changed files with 1538 additions and 752 deletions
+1 -2
View File
@@ -1,4 +1,3 @@
.idea
composer.lock
vendor/
demo-project/vendor
vendor/
+327 -202
View File
@@ -15,71 +15,75 @@ If you want a great new feature or experience any issues what-so-ever, please fe
## Table of Contents
- Gettings started
- Requirements
- Notes
- Features
- Installation
- Setting up Apache
- Setting up Nginx
- Setting up simple-php-router
- Helper methods
- [Getting started](#getting-started)
- [Requirements](#requirements)
- [Notes](#notes-1)
- [Features](#features)
- [Installation](#installation)
- [Setting up Apache](#setting-up-apache)
- [Setting up Nginx](#setting-up-nginx)
- [Configuration](#configuration)
- [Helper functions](#helper-functions)
- Routes
- Basic routing
- Available methods
- Multiple HTTP-verbs
- Route parameters
- Required parameters
- Optional parameters
- Regular expression constraints
- Regular expression route-match
- Named routes
- Generating URLs To Named Routes
- Router groups
- Middleware
- Namespaces
- Subdomain-routing
- Route prefixes
- Form Method Spoofing
- Accessing The Current Route
- Other examples
- [Routes](#routes)
- [Basic routing](#basic-routing)
- [Available methods](#available-methods)
- [Multiple HTTP-verbs](#multiple-http-verbs)
- [Route parameters](#route-parameters)
- [Required parameters](#required-parameters)
- [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)
- [Middleware](#middleware)
- [Namespaces](#namespaces)
- [Subdomain-routing](#subdomain-routing)
- [Route prefixes](#route-prefixes)
- [Form Method Spoofing](#form-method-spoofing)
- [Accessing The Current Route](#accessing-the-current-route)
- [Other examples](#other-examples)
- CSRF-protection
- Adding CSRF-verifier
- Getting CSRF-token
- [CSRF-protection](#csrf-protection)
- [Adding CSRF-verifier](#adding-csrf-verifier)
- [Getting CSRF-token](#getting-csrf-token)
- Middleware
- Example
- ExceptionHandler
- Example
- [Middlewares](#middlewares)
- [Example](#example)
- [ExceptionHandlers](#exceptionhandlers)
- [Handling 404, 403 and other errors](#handling-404-403-and-other-errors)
- [Using custom exception handlers](#using-custom-exception-handlers)
- Urls
- Get by name (single route)
- Get by name (controller route)
- Get by class
- Get by custom names for methods on a controller/resource route
- Getting REST/resource controller urls
- [Urls](#urls)
- [Get by name (single route)](#get-by-name-single-route)
- [Get by name (controller route)](#get-by-name-controller-route)
- [Get by class](#get-by-class)
- [Get by custom names for methods on a controller/resource route](#using-custom-names-for-methods-on-a-controllerresource-route)
- [Getting REST/resource controller urls](#getting-restresource-controller-urls)
- [Get the current url](#get-the-current-url)
- Input & parameters
- Return single parameter value
- Return single parameter object
- Managing files
- Return all parameters
- [Input & parameters](#input--parameters)
- [Using the Input class to manage parameters](#using-the-input-class-to-manage-parameters)
- [Get single parameter value](#get-single-parameter-value)
- [Get parameter object](#get-parameter-object)
- [Managing files](#managing-files)
- [Get all parameters](#get-all-parameters)
- Advanced
- Bootmanager: loading routes dynamically
- Ovewrite route about to be loaded
- Examples
- Rewriting to new route
- Changing callback
- [Advanced](#advanced)
- [Url rewriting](#url-rewriting)
- [Rewrite using callback](#rewrite-using-callback)
- [Rewrite using url](#rewrite-using-url)
- [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)
- Adding routes manually
- Extending
- Credits
- Sites
- License
- [Credits](#credits)
- [Sites](#sites)
- [License](#license)
___
@@ -154,7 +158,21 @@ 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.
### Setting up simple-php-router
#### .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.
@@ -192,8 +210,6 @@ To implement the functions below, simply copy the code to a new file and require
```php
<?php
use Pecee\SimpleRouter\SimpleRouter;
/**
* Get url for a route by using either name/alias, class or method name.
*
@@ -211,41 +227,43 @@ use Pecee\SimpleRouter\SimpleRouter;
* @param array|null $getParams
* @return string
*/
function url($name = null, $parameters = null, $getParams = null) {
SimpleRouter::getUrl($name, $parameters, $getParams);
function url($name = null, $parameters = null, $getParams = null)
{
return SimpleRouter::getUrl($name, $parameters, $getParams);
}
/**
* Get current csrf-token
* @return null|string
*/
function csrf_token() {
$token = new \Pecee\CsrfToken();
return $token->getToken();
}
/**
* Get request object
* @return \Pecee\Http\Request
*/
function request() {
return SimpleRouter::request();
}
/**
* Get response object
* @return \Pecee\Http\Response
*/
function response() {
function response()
{
return SimpleRouter::response();
}
/**
* @return \Pecee\Http\Request
*/
function request()
{
return SimpleRouter::request();
}
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input() {
return SimpleRouter::request()->getInput();
function input()
{
return request()->getInput();
}
function redirect($url, $code = null)
{
if ($code !== null) {
response()->httpCode($code);
}
response()->redirect($url);
}
```
@@ -364,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:
@@ -375,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');
```
@@ -421,7 +468,7 @@ Route groups allow you to share route attributes, such as middleware or namespac
To assign middleware to all routes within a group, you may use the middleware key in the group attribute array. Middleware are executed in the order they are listed in the array:
```php
SimpleRouter::group(['middleware' => '\Demo\Middleware\Auth'], function () {
SimpleRouter::group(['middleware' => \Demo\Middleware\Auth::class], function () {
SimpleRouter::get('/', function () {
// Uses Auth Middleware
});
@@ -436,6 +483,11 @@ SimpleRouter::group(['middleware' => '\Demo\Middleware\Auth'], function () {
Another common use-case for route groups is assigning the same PHP namespace to a group of controllers using the `namespace` parameter in the group array:
#### Note
Group namespaces will only be added to routes with relative callbacks.
For example if your route has an absolute callback like `\Demo\Controller\DefaultController@home`, the namespace from the route will not be prepended.
To fix this you can make the callback relative by removing the `\` in the beginning of the callback.
```php
SimpleRouter::group(['namespace' => 'Admin'], function () {
// Controllers Within The "App\Http\Controllers\Admin" Namespace
@@ -479,7 +531,8 @@ HTML forms do not support `PUT`, `PATCH` or `DELETE` actions. So, when defining
You can access information about the current route loaded by using the following method:
```php
SimpleRouter::router()->getLoadedRoute();
SimpleRouter::request()->getLoadedRoute();
request()->getLoadedRoute();
```
## Other examples
@@ -493,7 +546,7 @@ use Pecee\SimpleRouter\SimpleRouter;
/* Adding custom csrfVerifier here */
SimpleRouter::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
SimpleRouter::group(['middleware' => '\Demo\Middlewares\Site', 'exceptionHandler' => 'Handlers\CustomExceptionHandler'], function() {
SimpleRouter::group(['middleware' => \Demo\Middlewares\Site::class, 'exceptionHandler' => \Demo\Handlers\CustomExceptionHandler::class], function() {
SimpleRouter::get('/answers/{id}', 'ControllerAnswers@show', ['where' => ['id' => '[0-9]+']]);
@@ -503,7 +556,7 @@ SimpleRouter::group(['middleware' => '\Demo\Middlewares\Site', 'exceptionHandler
* Restful resource (see IRestController interface for available methods)
*/
SimpleRouter::resource('/rest', 'ControllerRessource');
SimpleRouter::resource('/rest', ControllerRessource::class);
/**
@@ -519,7 +572,7 @@ SimpleRouter::group(['middleware' => '\Demo\Middlewares\Site', 'exceptionHandler
* etc.
*/
SimpleRouter::controller('/animals', 'ControllerAnimals');
SimpleRouter::controller('/animals', ControllerAnimals::class);
});
@@ -584,13 +637,19 @@ namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomMiddleware implements Middleware {
public function handle(Request $request, ILoadableRoute &$route) {
public function handle(Request $request) {
$request->setUri(url('home'));
// Authenticate user, will be available using request()->user
$request->user = User::authenticate();
// If authentication failed, redirect request to user-login page.
if($request->user === null) {
$request->setRewriteUrl(url('user.login'));
return $request;
}
}
}
@@ -598,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).
@@ -614,11 +690,10 @@ namespace Demo\Handlers;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error)
public function handleError(Request $request, \Exception $error)
{
/* You can use the exception handler to format errors depending on the request and type. */
@@ -635,14 +710,9 @@ class CustomExceptionHandler implements IExceptionHandler
/* The router will throw the NotFoundHttpException on 404 */
if($error instanceof NotFoundHttpException) {
/*
* Render your own custom 404-view, rewrite the request to another route,
* or simply return the $request object to ignore the error and continue on rendering the route.
*
* The code below will make the router render our page.notfound route.
*/
// Render custom 404-page
$request->setUri(url('page.notfound'));
$request->setRewriteCallback('Demo\Controllers\PageController@notFound');
return $request;
}
@@ -660,7 +730,7 @@ class CustomExceptionHandler implements IExceptionHandler
By default all controller and resource routes will use a simplified version of their url as name.
### Get routes using custom name (single route)
### Get by name (single route)
```php
SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);
@@ -673,10 +743,10 @@ url('product', null, ['category' => 'shoes']);
# /product-view/?category=shoes
```
### Getting the url using the name (controller route)
### Get by name (controller route)
```php
SimpleRouter::controller('/images', 'ImagesController', ['as' => 'picture']);
SimpleRouter::controller('/images', ImagesController::class, ['as' => 'picture']);
url('picture@getView', null, ['category' => 'shoes']);
url('picture', 'getView', ['category' => 'shoes']);
@@ -688,7 +758,7 @@ url('picture', 'view');
# /images/view/
```
### Getting the url using class
### Get by class
```php
SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);
@@ -705,7 +775,7 @@ url('ImagesController@getImage', null, ['id' => 22]);
### Using custom names for methods on a controller/resource route
```php
SimpleRouter::controller('gadgets', 'GadgetsController', ['names' => ['getIphoneInfo' => 'iphone']]);
SimpleRouter::controller('gadgets', GadgetsController::class, ['names' => ['getIphoneInfo' => 'iphone']]);
url('gadgets.iphone');
@@ -716,7 +786,7 @@ url('gadgets.iphone');
### Getting REST/resource controller urls
```php
SimpleRouter::resource('/phones', 'PhonesController');
SimpleRouter::resource('/phones', PhonesController::class);
url('phones');
url('phones.index');
@@ -731,7 +801,7 @@ url('phones.edit');
# /phones/edit/
```
### Return the current url
### Get the current url
```php
url();
@@ -748,7 +818,7 @@ url(null, null, ['q' => 'cars']);
We've added the `Input` class to easy access and manage parameters from your Controller-classes.
**Return single parameter value (matches both GET, POST, FILE):**
### Get single parameter value:
If items is grouped in the html, it will return an array of items.
@@ -758,7 +828,7 @@ If items is grouped in the html, it will return an array of items.
$value = input()->get($index, $defaultValue, $methods);
```
**Return parameter object (matches both GET, POST, FILE):**
### Get parameter object
Will return an instance of `InputItem` or `InputFile` depending on the type.
@@ -774,7 +844,7 @@ If items is grouped in the html, it will return an array of items.
$object = input()->getObject($index, $defaultValue = null, $methods = null);
```
**Return specific GET parameter (where name is the name of your parameter):**
### Return specific GET parameter (where name is the name of your parameter):
```php
# -- match any (default) --
@@ -798,9 +868,11 @@ $object = input()->get($index, $defaultValue, 'file');
$object = input()->findGet($index, $defaultValue);
$object = input()->findPost($index, $defaultValue);
$object = input()->findFile($index, $defaultValue);
```
# -- examples --
### Managing files
```php
/**
* In this small example we loop through a collection of files
* added on the page like this
@@ -818,9 +890,10 @@ foreach(input()->get('images', []) as $image)
}
}
```
**Return all parameters:**
### Get all parameters
```php
// Get all
@@ -860,7 +933,130 @@ $siteId = input()->get('site_id', 2, ['post', 'get']);
# Advanced
## Load routes dynamicially using the router bootmanager
## Url rewriting
Sometimes it can be useful to manipulate the route about to be loaded.
simple-php-router allows you to easily change the route about to be executed.
All information about the current route is stored in the ```\Pecee\SimpleRouter\Router``` instance's `loadedRoute` property.
For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::router()`.
```php
use Pecee\SimpleRouter;
$request = SimpleRouter::request();
$request->setRewriteCallback('Example\MyCustomClass@hello');
// -- or you can rewrite by url --
$request->setRewriteUrl('/my-rewrite-url');
```
**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.
### Rewrite using callback
This method is most efficient, as it will render the route immediately.
This method is useful for rendering 404-pages etc.
You can also change the callback by modifying the `$route` parameter. This is perfect if you just want to display a view quickly - or change the callback depending
on some criteria's for the request.
The callback below will fire immediately after the `Middleware` or `ExceptionHandler` has been loaded, as they are loaded before the route is rendered.
If you wish to change the callback from outside, please have this in mind.
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
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class CustomMiddleware implements IMiddleware {
public function handle(Request $request) {
$request->setRewriteCallback('Demo\Controllers\DefaultController@notFound');
return $request;
}
}
```
##### Exception handler example
```php
namespace Demo\Handlers;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, \Exception $error)
{
/* The router will throw the NotFoundHttpException on 404 */
if($error instanceof NotFoundHttpException) {
/*
* Render your own custom 404-view, rewrite the request to another route,
* or simply return the $request object to ignore the error and continue on rendering the route.
*
* The code below will make the router render our page.notfound route.
*/
$request->setRewriteCallback('Demo\Controllers\DefaultController@notFound');
return $request;
}
throw $error;
}
}
```
### 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.
This method is useful if you want to redirect a url to another-url which is dependent on a middleware. You can also add a custom rule by calling `$request->setRewriteRoute($route)` if
you want to customize request-methods or use another route-type like `RouteController` etc.
We are using the `url()` helper function to get the uri to another route added in the `routes.php` file.
**NOTE: Use this method if you want to fully load another route using it's settings (request method, middlewares etc).**
##### Middleware example
The example below will redirect the request to the `home`-route.
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class CustomMiddleware implements IMiddleware {
public function handle(Request $request) {
$request->setRewriteUrl(url('home'));
return $request;
}
}
```
### 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.
@@ -876,7 +1072,7 @@ class CustomRouterRules implement IRouterBootManager {
$rewriteRules = [
'/my-cat-is-beatiful' => '/article/view/1',
'/horses-are-great' => '/article/view/2'
'/horses-are-great' => '/article/view/2',
];
foreach($rewriteRules as $url => $rule) {
@@ -884,11 +1080,11 @@ class CustomRouterRules implement IRouterBootManager {
// If the current uri matches the url, we use our custom route
if($request->getUri() === $url) {
$request->setUri($rule);
$request->setRewriteUrl($rule);
return $request;
}
}
return $request;
}
}
@@ -906,83 +1102,7 @@ The last thing we need to do, is to add our custom boot-manager to the ```routes
SimpleRouter::addBootManager(new CustomRouterRules());
```
## Easily overwrite route about to be loaded
Sometimes it can be useful to manipulate the route about to be loaded.
simple-php-router allows you to easily change the route about to be executed.
All information about the current route is stored in the ```\Pecee\SimpleRouter\Router``` instance's `loadedRoute` property.
For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::router()`.
```php
use Pecee\SimpleRouter;
$route = SimpleRouter::router()->getLoadedRoute();
$route->setCallback('Example\MyCustomClass@hello');
// -- or you can rewrite by doing --
$route->setClass('Example\MyCustomClass');
$route->setMethod('hello');
```
### Examples
It's only possible to change the route BEFORE the route has initially been loaded. If you want to redirect to another route, we highly recommend that you
modify the `IRoute` object from a `Middleware` or `ExceptionHandler`, like the examples below.
#### Rewriting to new route
The example below will cause the router to re-route the request with another url. We are using the `url()` helper function to get the uri to another route added in the `routes.php` file.
**NOTE: Use this method if you want to fully load another route using it's settings (request method etc).**
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomMiddleware implements Middleware {
public function handle(Request $request, ILoadableRoute &$route) {
$request->setUri(url('home'));
}
}
```
#### Changing callback
You can also change the callback by modifying the `$route` parameter. This is perfect if you just want to display a view quickly - or change the callback depending
on some criteria's for the request.
The callback below will fire immediately after the `Middleware` or `ExceptionHandler` has been loaded, as they are loaded before the route is rendered.
If you wish to change the callback from outside, please have this in mind.
**NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.**
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomMiddleware implements Middleware {
public function handle(Request $request, ILoadableRoute &$route) {
$route->callback('DefaultController@home');
}
}
```
## 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.
@@ -991,8 +1111,8 @@ If you are up for a challenge, want the full control or simply just want to crea
use \Pecee\SimpleRouter\Router;
use \Pecee\SimpleRouter\Route\RouteUrl;
/* Grap the router instance */
$router = Router::getInstance();
/* Create new Router instance */
$router = new Router();
$route = new RouteUrl('/answer/1', function() {
@@ -1000,7 +1120,7 @@ $route = new RouteUrl('/answer/1', function() {
});
$route->setMiddleware('\Demo\Middlewares\AuthMiddleware');
$route->addMiddleware(\Demo\Middlewares\AuthMiddleware::class);
$route->setNamespace('\Demo\Controllers');
$route->setPrefix('v1');
@@ -1008,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.
@@ -1042,6 +1166,7 @@ class Router extends SimpleRouter {
# Credits
## Sites
This is some sites that uses the simple-router project in production.
- [holla.dk](http://www.holla.dk)
+32 -23
View File
@@ -1,26 +1,35 @@
{
"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", "routing", "laravel", "pecee" ],
"license": "MIT",
"support": {
"source": "https://github.com/skipperbent/simple-php-router/issues"
},
"authors": [
{
"name": "Simon Sessingø",
"email": "simon.sessingoe@gmail.com"
}
],
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "4.7.7"
},
"autoload": {
"psr-4": {
"Pecee\\": "src/Pecee/"
}
"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",
"php"
],
"license": "MIT",
"support": {
"source": "https://github.com/skipperbent/simple-php-router/issues"
},
"authors": [
{
"name": "Simon Sessingø",
"email": "simon.sessingoe@gmail.com"
}
],
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "4.7.7"
},
"autoload": {
"psr-4": {
"Pecee\\": "src/Pecee/"
}
}
}
@@ -1,7 +1,7 @@
<?php
namespace Pecee\Controllers;
interface IRestController
interface IResourceController
{
/**
+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
);
}
}
+1 -3
View File
@@ -2,16 +2,14 @@
namespace Pecee\Handlers;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
interface IExceptionHandler
{
/**
* @param Request $request
* @param ILoadableRoute $route
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error);
public function handleError(Request $request, \Exception $error);
}
+2
View File
@@ -12,6 +12,8 @@ interface IInputItem
public function setName($name);
public function getValue();
public function __toString();
}
+6 -12
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) {
@@ -93,7 +93,9 @@ class Input
$path = $original[$property];
foreach (array_values($index) as $i) {
$fileValues = array_values($index);
foreach ($fileValues as $i) {
$path = $path[$i];
}
@@ -106,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])) {
@@ -281,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 -3
View File
@@ -43,8 +43,8 @@ class InputFile implements IInputItem
], $values);
return (new static($values['index']))
->setError($values['error'])
->setSize($values['size'])
->setError($values['error'])
->setType($values['type'])
->setTmpName($values['tmp_name'])
->setFilename($values['name']);
@@ -127,7 +127,7 @@ class InputFile implements IInputItem
*/
public function getExtension()
{
return pathinfo($this->getName(), PATHINFO_EXTENSION);
return pathinfo($this->getFilename(), PATHINFO_EXTENSION);
}
/**
@@ -199,7 +199,7 @@ class InputFile implements IInputItem
}
/**
* Return true if an upload error occured.
* Return true if an upload error occurred.
*
* @return bool
*/
@@ -256,6 +256,11 @@ class InputFile implements IInputItem
return $this->getTmpName();
}
public function getValue()
{
return $this->getFilename();
}
public function toArray()
{
return [
@@ -4,7 +4,6 @@ namespace Pecee\Http\Middleware;
use Pecee\CsrfToken;
use Pecee\Http\Middleware\Exceptions\TokenMismatchException;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class BaseCsrfVerifier implements IMiddleware
{
@@ -55,7 +54,7 @@ class BaseCsrfVerifier implements IMiddleware
return false;
}
public function handle(Request $request, ILoadableRoute &$route = null)
public function handle(Request $request)
{
if ($this->skip($request) === false && in_array($request->getMethod(), ['post', 'put', 'delete'], false) === true) {
+1 -3
View File
@@ -2,15 +2,13 @@
namespace Pecee\Http\Middleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
interface IMiddleware
{
/**
* @param Request $request
* @param ILoadableRoute $route
* @return Request|null
*/
public function handle(Request $request, ILoadableRoute &$route);
public function handle(Request $request);
}
+123 -1
View File
@@ -2,16 +2,30 @@
namespace Pecee\Http;
use Pecee\Http\Input\Input;
use Pecee\SimpleRouter\Route\ILoadableRoute;
use Pecee\SimpleRouter\Route\RouteUrl;
use Pecee\SimpleRouter\SimpleRouter;
class Request
{
protected $data = [];
private $data = [];
protected $headers;
protected $host;
protected $uri;
protected $method;
protected $input;
/**
* @var ILoadableRoute|null
*/
protected $rewriteRoute;
protected $rewriteUrl;
/**
* @var ILoadableRoute|null
*/
protected $loadedRoute;
public function __construct()
{
$this->parseHeaders();
@@ -111,6 +125,17 @@ class Request
return $this->getHeader('remote-addr');
}
/**
* Get remote address/ip
*
* @alias static::getIp
* @return string
*/
public function getRemoteAddr()
{
return $this->getIp();
}
/**
* Get referer
* @return string
@@ -213,6 +238,103 @@ class Request
$this->method = $method;
}
/**
* Set rewrite route
*
* @param ILoadableRoute $route
* @return static
*/
public function setRewriteRoute(ILoadableRoute $route)
{
$this->rewriteRoute = $route;
$callback = $route->getCallback();
/* Only add default namespace on relative callbacks */
if ($callback === null || $callback[0] !== '\\') {
$namespace = SimpleRouter::getDefaultNamespace();
if ($namespace !== null) {
if ($this->rewriteRoute->getNamespace() !== null) {
$namespace .= '\\' . $this->rewriteRoute->getNamespace();
}
$this->rewriteRoute->setDefaultNamespace($namespace);
}
}
return $this;
}
/**
* Get rewrite route
*
* @return ILoadableRoute|null
*/
public function getRewriteRoute()
{
return $this->rewriteRoute;
}
/**
* Get rewrite url
*
* @return string
*/
public function getRewriteUrl()
{
return $this->rewriteUrl;
}
/**
* Set rewrite url
*
* @param string $rewriteUrl
* @return static
*/
public function setRewriteUrl($rewriteUrl)
{
$this->rewriteUrl = $rewriteUrl;
return $this;
}
/**
* Set rewrite callback
* @param string $callback
* @return static
*/
public function setRewriteCallback($callback)
{
return $this->setRewriteRoute(new RouteUrl($this->uri, $callback));
}
/**
* Get loaded route
* @return ILoadableRoute|null
*/
public function getLoadedRoute()
{
return $this->loadedRoute;
}
/**
* Set loaded route
*
* @param ILoadableRoute $route
* @return static
*/
public function setLoadedRoute(ILoadableRoute $route)
{
$this->loadedRoute = $route;
return $this;
}
public function __isset($name)
{
return array_key_exists($name, $this->data);
@@ -3,4 +3,5 @@ namespace Pecee\SimpleRouter\Exceptions;
class NotFoundHttpException extends HttpException
{
}
@@ -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
*
@@ -20,9 +20,8 @@ interface ILoadableRoute extends IRoute
* Loads and renders middlewares-classes
*
* @param Request $request
* @param ILoadableRoute $route
*/
public function loadMiddleware(Request $request, ILoadableRoute $route);
public function loadMiddleware(Request $request);
public function getUrl();
+4 -2
View File
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
@@ -8,10 +9,11 @@ interface IRoute
/**
* Method called to check if a domain matches
*
* @param string $route
* @param Request $request
* @return bool
*/
public function matchRoute(Request $request);
public function matchRoute($route, Request $request);
/**
* Called when route is matched.
@@ -172,7 +174,7 @@ interface IRoute
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware);
public function addMiddleware($middleware);
/**
* Set middlewares array
+29 -38
View File
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Middleware\IMiddleware;
@@ -23,50 +24,40 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
* Loads and renders middlewares-classes
*
* @param Request $request
* @param ILoadableRoute $route
* @throws HttpException
*/
public function loadMiddleware(Request $request, ILoadableRoute $route)
public function loadMiddleware(Request $request)
{
if (count($this->getMiddlewares()) > 0) {
$max = count($this->getMiddlewares());
$max = count($this->getMiddlewares());
if ($max > 0) {
for ($i = 0; $i < $max; $i++) {
$middleware = $this->getMiddlewares()[$i];
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new HttpException($middleware . ' must be instance of Middleware');
if (is_object($middleware) === false) {
$middleware = $this->loadClass($middleware);
}
$middleware->handle($request, $route);
if (($middleware instanceof IMiddleware) === false) {
throw new HttpException($middleware . ' must be inherit the IMiddleware interface');
}
$middleware->handle($request);
}
}
}
public function matchRegex(Request $request, $url)
{
/* Match on custom defined regular expression */
if ($this->regex === null) {
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);
}
/**
@@ -81,9 +72,9 @@ 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 . '/is', $this->url, $matches)) {
if (preg_match_all('/' . $regex . '/', $this->url, $matches)) {
$this->parameters = array_fill_keys($matches[1], null);
}
}
@@ -107,46 +98,46 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
*/
public function findUrl($method = null, $parameters = null, $name = null)
{
$url = '';
$url = $this->getUrl();
$parameters = (array)$parameters;
$group = $this->getGroup();
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
$url .= '//' . $this->getGroup()->getDomains()[0];
if ($group !== null && count($group->getDomains()) > 0) {
$url = '//' . $group->getDomains()[0] . $url;
}
$url .= $this->getUrl();
$params = array_merge($this->getParameters(), $parameters);
/* Url that contains parameters that aren't recognized */
/* Contains parameters that aren't recognized and will be appended at the end of the url */
$unknownParams = [];
/* Create the param string - {} */
/* Create the param string - {parameter} */
$param1 = $this->paramModifiers[0] . '%s' . $this->paramModifiers[1];
/* Create the param string with the optional symbol - {?} */
/* Create the param string with the optional symbol - {parameter?} */
$param2 = $this->paramModifiers[0] . '%s' . $this->paramOptionalSymbol . $this->paramModifiers[1];
/* Let's parse the values of any {} parameter in the url */
/* Replace any {parameter} in the url with the correct value */
$params = $this->getParameters();
$max = count($params) - 1;
$keys = array_keys($params);
for ($i = $max; $i >= 0; $i--) {
$param = $keys[$i];
$value = $params[$param];
$value = $value = ($parameters !== null && array_key_exists($param, $parameters)) ? $parameters[$param] : $params[$param];
$value = isset($parameters[$param]) ? $parameters[$param] : $value;
/* If parameter is specifically set to null - use the original-defined value */
if ($value === null && isset($this->originalParameters[$param])) {
$value = $this->originalParameters[$param];
}
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
/* Add parameter to the correct position */
$url = str_ireplace([sprintf($param1, $param), sprintf($param2, $param)], $value, $url);
} else {
$unknownParams[$param] = $value;
}
}
/** @noinspection AliasFunctionsUsageInspection */
$url .= join('/', $unknownParams);
return rtrim($url, '/') . '/';
+126 -48
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();
@@ -57,59 +61,84 @@ abstract class Route implements IRoute
public function renderRoute(Request $request)
{
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
$callback = $this->getCallback();
if ($callback === null) {
return;
}
/* Render callback function */
if (is_callable($callback) === true) {
/* When the callback is a function */
call_user_func_array($this->getCallback(), $this->getParameters());
call_user_func_array($callback, $this->getParameters());
} else {
return;
/* When the callback is a method */
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (method_exists($class, $method) === 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]);
if (preg_match_all('/' . $regex . '/is', $route, $parameters)) {
$parameters = [];
$parameterNamesRegex = [];
$parameterNames = $parameters[1];
$parameterRequired = [];
// Ensures that hostnames/domains will work with parameters
$url = '/' . ltrim($url, '/');
$urlParts = preg_split('/\{[^}]+\}/is', rtrim($route, '/'));
if (preg_match_all('/' . $regex . '/', $route, $parameters)) {
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', rtrim($route, '/'));
foreach ($urlParts as $key => $t) {
$regex = '';
if ($key < (count($parameters[1]))) {
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];
}
@@ -122,13 +151,16 @@ abstract class Route implements IRoute
$urlRegex = preg_quote($route, '/');
}
if (preg_match('/^' . $urlRegex . '(\/?)$/is', $url, $matches) > 0) {
if (preg_match('/^' . $urlRegex . '\/?/', $url, $matches) > 0) {
$values = [];
/* Only take matched parameters with name */
foreach ($parameters[1] as $name) {
$values[$name] = $matches[$name];
if (isset($parameters[1]) === true) {
/* Only take matched parameters with name */
foreach ($parameters[1] as $name) {
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
}
}
return $values;
@@ -146,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;
}
@@ -243,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];
@@ -254,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];
@@ -337,6 +369,10 @@ abstract class Route implements IRoute
$values['middleware'] = $this->middlewares;
}
if ($this->defaultParameterRegex !== null) {
$values['defaultParameterRegex'] = $this->defaultParameterRegex;
}
return $values;
}
@@ -370,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;
}
@@ -406,7 +446,7 @@ abstract class Route implements IRoute
*/
public function where(array $options)
{
return $this->where($options);
return $this->setWhere($options);
}
/**
@@ -442,15 +482,16 @@ abstract class Route implements IRoute
$this->originalParameters = $parameters;
}
$this->parameters = $parameters;
$this->parameters = array_merge($this->parameters, $parameters);
return $this;
}
/**
* 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)
@@ -460,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
*
@@ -481,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;
}
}
@@ -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);
@@ -83,34 +85,32 @@ class RouteController extends LoadableRoute implements IControllerRoute
return '/' . trim($url, '/') . '/';
}
public function matchRoute(Request $request)
public function matchRoute($url, Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = parse_url(urldecode($url), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
/* Match global regular-expression for route */
if ($this->matchRegex($request, $url) === true) {
return true;
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false || (stripos($url, $this->url) !== 0 && strtolower($url) !== strtolower($this->url))) {
return false;
}
if (stripos($url, $this->url) === 0 && strtolower($url) === strtolower($this->url)) {
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
if (count($path) > 0) {
$path = explode('/', $strippedUrl);
$method = (isset($path[0]) === false || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
$this->method = $request->getMethod() . ucfirst($method);
if (count($path) > 0) {
$this->parameters = array_slice($path, 1);
$method = (!isset($path[0]) || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
$this->method = $method;
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
$this->parameters = array_slice($path, 1);
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return true;
}
return true;
}
return false;
+19 -3
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;
}
@@ -40,19 +42,33 @@ class RouteGroup extends Route implements IGroupRoute
/**
* Method called to check if route matches
*
* @param string $url
* @param Request $request
* @return bool
*/
public function matchRoute(Request $request)
public function matchRoute($url, Request $request)
{
/* Skip if prefix doesn't match */
if ($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) {
if ($this->prefix !== null && stripos($url, $this->prefix) === false) {
return false;
}
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
*
+24 -18
View File
@@ -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));
@@ -61,9 +62,9 @@ class RouteResource extends LoadableRoute implements IControllerRoute
public function findUrl($method = null, $parameters = null, $name = null)
{
$method = array_search($name, $this->names, false);
if ($method !== false) {
return rtrim($this->url . $this->urls[$method], '/') . '/';
$url = array_search($name, $this->names, false);
if ($url !== false) {
return rtrim($this->url . $this->urls[$url], '/') . '/';
}
return $this->url;
@@ -76,48 +77,53 @@ class RouteResource extends LoadableRoute implements IControllerRoute
return true;
}
public function matchRoute(Request $request)
public function matchRoute($url, Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = parse_url(urldecode($url), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
/* Match global regular-expression for route */
$domainMatch = $this->matchRegex($request, $url);
if ($domainMatch !== null) {
return $domainMatch;
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false || (stripos($url, $this->url) !== 0 && strtolower($url) !== strtolower($this->url))) {
return false;
}
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$parameters = $this->parseParameters($route, $url);
if ($parameters === null) {
/* Parse parameters from current route */
$this->parameters = $this->parseParameters($route, $url);
/* If no custom regular expression or parameters was found on this route, we stop */
if ($regexMatch === null && $this->parameters === null) {
return false;
}
$this->parameters = (array)$parameters;
$action = strtolower(trim($this->parameters['action']));
$id = $this->parameters['id'];
$action = isset($this->parameters['action']) ? $this->parameters['action'] : null;
// Remove action parameter
unset($this->parameters['action']);
$method = $request->getMethod();
// Delete
if ($method === static::REQUEST_TYPE_DELETE && isset($this->parameters['id'])) {
if ($method === static::REQUEST_TYPE_DELETE && $id !== null) {
return $this->call($this->methodNames['destroy']);
}
// Update
if (isset($this->parameters['id']) && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], false)) {
if ($id !== null && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], false) === true) {
return $this->call($this->methodNames['update']);
}
// Edit
if ($method === static::REQUEST_TYPE_GET && isset($this->parameters['id']) && strtolower($action) === 'edit') {
if ($method === static::REQUEST_TYPE_GET && $id !== null && $action === 'edit') {
return $this->call($this->methodNames['edit']);
}
// Create
if ($method === static::REQUEST_TYPE_GET && strtolower($action) === 'create') {
if ($method === static::REQUEST_TYPE_GET && $id === 'create') {
return $this->call($this->methodNames['create']);
}
@@ -127,7 +133,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute
}
// Show
if ($method === static::REQUEST_TYPE_GET && isset($this->parameters['id'])) {
if ($method === static::REQUEST_TYPE_GET && $id !== null) {
return $this->call($this->methodNames['show']);
}
+15 -11
View File
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
@@ -11,27 +12,30 @@ class RouteUrl extends LoadableRoute
$this->setCallback($callback);
}
public function matchRoute(Request $request)
public function matchRoute($url, Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = parse_url(urldecode($url), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
/* Match global regular-expression for route */
$domainMatch = $this->matchRegex($request, $url);
if ($domainMatch !== null) {
return $domainMatch;
}
$regexMatch = $this->matchRegex($request, $url);
/* Make regular expression based on route */
$parameters = $this->parseParameters($this->url, $url);
if ($parameters === null) {
if ($regexMatch === false) {
return false;
}
$this->setParameters($parameters);
/* Parse parameters from current route */
$parameters = $this->parseParameters($this->url, $url);
/* If no custom regular expression or parameters was found on this route, we stop */
if ($regexMatch === null && $parameters === null) {
return false;
}
/* Set the parameters */
$this->setParameters((array)$parameters);
return true;
}
}
+119 -103
View File
@@ -1,4 +1,5 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Handlers\IExceptionHandler;
@@ -69,38 +70,7 @@ class Router
*/
protected $exceptionHandlers;
/**
* The current loaded route
* @var ILoadableRoute|null
*/
protected $loadedRoute;
/**
* List over route changes (to avoid endless-looping)
* @var array
*/
protected $routeRewrites = [];
/**
* If the route has been rewritten/changed this property will contain the original url.
* @var string
*/
protected $originalUrl;
/**
* Get current router instance
* @return static
*/
public static function getInstance()
{
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
protected function __construct()
public function __construct()
{
$this->reset();
}
@@ -143,6 +113,7 @@ class Router
* @param array $routes
* @param IGroupRoute|null $group
* @param IRoute|null $parent
* @throws NotFoundHttpException
*/
protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null)
{
@@ -151,6 +122,8 @@ class Router
$exceptionHandlers = [];
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
/* @var $route IRoute */
for ($i = $max; $i >= 0; $i--) {
@@ -161,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($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();
}
}
}
@@ -202,7 +173,7 @@ class Router
if (count($this->routeStack) > 0) {
/* Pop and grap the routes added when executing group callback earlier */
/* Pop and grab the routes added when executing group callback earlier */
$stack = $this->routeStack;
$this->routeStack = [];
@@ -211,56 +182,59 @@ class Router
}
}
$this->exceptionHandlers = array_unique(array_merge($exceptionHandlers, $this->exceptionHandlers));
$this->exceptionHandlers = array_merge($exceptionHandlers, $this->exceptionHandlers);
}
/**
* Load routes
* @throws NotFoundHttpException
* @return void
*/
public function loadRoutes()
{
/* Initialize boot-managers */
if (count($this->bootManagers) > 0) {
$max = count($this->bootManagers) - 1;
/* @var $manager IRouterBootManager */
for ($i = $max; $i >= 0; $i--) {
$manager = $this->bootManagers[$i];
$manager->boot($this->request);
}
}
/* Loop through each route-request */
$this->processRoutes($this->routes);
}
public function routeRequest($rewrite = false)
{
$this->loadedRoute = null;
$routeNotAllowed = false;
try {
/* Initialize boot-managers */
if (count($this->bootManagers) > 0) {
$max = count($this->bootManagers) - 1;
/* @var $manager IRouterBootManager */
for ($i = $max; $i >= 0; $i--) {
$manager = $this->bootManagers[$i];
$this->request = $manager->boot($this->request);
if (!($this->request instanceof Request)) {
throw new HttpException('Bootmanager "' . get_class($manager) . '" must return instance of ' . Request::class, 500);
}
}
}
if ($rewrite === false) {
/* Loop through each route-request */
$this->processRoutes($this->routes);
$this->loadRoutes();
if ($this->csrfVerifier !== null) {
/* Verify csrf token for request */
$this->csrfVerifier->handle($this->request);
}
$this->originalUrl = $this->request->getUri();
}
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
$max = count($this->processedRoutes) - 1;
/* @var $route IRoute */
/* @var $route ILoadableRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* If the route matches */
if ($route->matchRoute($this->request) === true) {
if ($route->matchRoute($url, $this->request) === true) {
/* Check if request method matches */
if (count($route->getRequestMethods()) > 0 && in_array($this->request->getMethod(), $route->getRequestMethods(), false) === false) {
@@ -268,12 +242,23 @@ class Router
continue;
}
$this->loadedRoute = $route;
$this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute);
$route->loadMiddleware($this->request);
/* If the request has changed, we reinitialize the router */
if ($this->request->getUri() !== $this->originalUrl && in_array($this->request->getUri(), $this->routeRewrites) === false) {
$this->routeRewrites[] = $this->request->getUri();
$rewriteRoute = $this->request->getRewriteRoute();
if ($rewriteRoute !== null) {
$rewriteRoute->loadMiddleware($this->request);
$rewriteRoute->renderRoute($this->request);
return;
}
/* If the request has changed */
$rewriteUrl = $this->request->getRewriteUrl();
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->processedRoutes[$i]);
$this->processedRoutes = array_values($this->processedRoutes);
$this->routeRequest(true);
return;
@@ -281,8 +266,8 @@ class Router
/* Render route */
$routeNotAllowed = false;
$this->request->setUri($this->originalUrl);
$this->loadedRoute->renderRoute($this->request);
$this->request->setLoadedRoute($route);
$route->renderRoute($this->request);
break;
}
@@ -296,13 +281,24 @@ class Router
$this->handleException(new HttpException('Route or method not allowed', 403));
}
if ($this->loadedRoute === null) {
$this->handleException(new NotFoundHttpException('Route not found: ' . $this->request->getUri(), 404));
if ($this->request->getLoadedRoute() === null) {
$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));
}
}
protected function handleException(\Exception $e)
{
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
$max = count($this->exceptionHandlers);
/* @var $handler IExceptionHandler */
@@ -310,21 +306,35 @@ class Router
$handler = $this->exceptionHandlers[$i];
$handler = new $handler();
if (is_object($handler) === false) {
$handler = new $handler();
}
if (!($handler instanceof IExceptionHandler)) {
if (($handler instanceof IExceptionHandler) === false) {
throw new HttpException('Exception handler must implement the IExceptionHandler interface.', 500);
}
$request = $handler->handleError($this->request, $this->loadedRoute, $e);
if ($handler->handleError($this->request, $e) !== null) {
/* If the request has changed */
if ($request !== null && $this->request->getUri() !== $this->originalUrl && in_array($request->getUri(), $this->routeRewrites) === false) {
$this->request = $request;
$this->routeRewrites[] = $request->getUri();
$this->routeRequest(true);
$rewriteRoute = $this->request->getRewriteRoute();
return;
if ($rewriteRoute !== null) {
$rewriteRoute->loadMiddleware($this->request);
$rewriteRoute->renderRoute($this->request);
return;
}
$rewriteUrl = $this->request->getRewriteUrl();
/* If the request has changed */
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->exceptionHandlers[$i]);
$this->exceptionHandlers = array_values($this->exceptionHandlers);
$this->routeRequest(true);
return;
}
}
}
@@ -337,7 +347,7 @@ class Router
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
return (trim($item) !== '');
});
}
@@ -373,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())) {
@@ -382,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)) {
@@ -437,9 +447,11 @@ class Router
return (($url === '') ? '/' : $url . '/') . $this->arrayToParams($getParams);
}
$loadedRoute = $this->request->getLoadedRoute();
/* If nothing is defined and a route is loaded we use that */
if ($name === null && $this->loadedRoute !== null) {
return $this->loadedRoute->findUrl($this->loadedRoute->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
if ($name === null && $loadedRoute !== null) {
return $loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
}
/* We try to find a match on the given name */
@@ -450,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 */
@@ -516,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
*
@@ -548,13 +573,4 @@ class Router
return $this;
}
/**
* Get loaded route
* @return ILoadableRoute|null
*/
public function getLoadedRoute()
{
return $this->loadedRoute;
}
}
+64 -10
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,12 @@ class SimpleRouter
*/
protected static $response;
/**
* Router instance
* @var Router
*/
protected static $router;
/**
* Start/route request
*
@@ -212,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)
{
@@ -235,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)
{
@@ -257,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)
{
@@ -279,11 +287,12 @@ 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)
{
$route = new RouteResource($url, $controller);
$route = static::addDefaultNamespace($route);
if ($settings !== null) {
$route->setSettings($settings);
@@ -294,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.
*
@@ -348,7 +379,11 @@ class SimpleRouter
*/
public static function router()
{
return Router::getInstance();
if (static::$router === null) {
static::$router = new Router();
}
return static::$router;
}
/**
@@ -360,16 +395,35 @@ class SimpleRouter
protected static function addDefaultNamespace(IRoute $route)
{
if (static::$defaultNamespace !== null) {
$namespace = static::$defaultNamespace;
if ($route->getNamespace() !== null) {
$namespace .= '\\' . $route->getNamespace();
$callback = $route->getCallback();
/* Only add default namespace on relative callbacks */
if ($callback === null || $callback[0] !== '\\') {
$namespace = static::$defaultNamespace;
$currentNamespace = $route->getNamespace();
if ($currentNamespace !== null) {
$namespace .= '\\' . $currentNamespace;
}
$route->setDefaultNamespace($namespace);
}
$route->setDefaultNamespace($namespace);
}
return $route;
}
/**
* Get default namespace
* @return string
*/
public static function getDefaultNamespace()
{
return static::$defaultNamespace;
}
}
+20 -10
View File
@@ -2,24 +2,34 @@
class DummyController
{
public function start()
public function method1()
{
echo static::class . '@' . 'start() OK';
}
public function method2()
{
}
public function param($params = null)
{
$params = func_get_args();
echo 'Params: ' . join(', ', $params);
echo join(', ', func_get_args());
}
public function notFound()
{
echo 'not found';
}
public function getTest()
{
echo 'getTest';
}
public function silent() {
public function postTest()
{
echo 'postTest';
}
}
public function putTest()
{
echo 'putTest';
}
}
+2 -3
View File
@@ -1,12 +1,11 @@
<?php
require_once 'Exceptions/MiddlewareLoadedException.php';
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class DummyMiddleware implements IMiddleware
class DummyMiddleware implements \Pecee\Http\Middleware\IMiddleware
{
public function handle(Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route)
public function handle(Request $request)
{
throw new MiddlewareLoadedException('Middleware loaded!');
}
@@ -0,0 +1,18 @@
<?php
class ResponseException extends \Exception
{
protected $response;
public function __construct($response)
{
$this->response = $response;
parent::__construct('', 0);
}
public function getResponse()
{
return $this->response;
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
class ExceptionHandler implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
echo $error->getMessage();
}
@@ -0,0 +1,14 @@
<?php
class ExceptionHandlerFirst implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
$request->setUri('/');
return $request;
}
}
@@ -0,0 +1,14 @@
<?php
class ExceptionHandlerSecond implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
$request->setUri('/');
return $request;
}
}
@@ -0,0 +1,13 @@
<?php
class ExceptionHandlerThird implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
throw new ResponseException('ExceptionHandler loaded');
}
}
@@ -1,13 +0,0 @@
<?php
class TestExceptionHandlerFirst implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
echo 'ExceptionHandler 1 loaded' . chr(10);
$request->setUri('/');
return $request;
}
}
@@ -1,13 +0,0 @@
<?php
class TestExceptionHandlerSecond implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
echo 'ExceptionHandler 2 loaded' . chr(10);
$request->setUri('/');
return $request;
}
}
@@ -1,12 +0,0 @@
<?php
class TestExceptionHandlerThird implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
echo 'ExceptionHandler 3 loaded' . chr(10);
throw new ExceptionHandlerException('All good!', 666);
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
class ResourceController implements \Pecee\Controllers\IResourceController
{
public function index()
{
echo 'index';
}
public function show($id)
{
echo 'show ' . $id;
}
public function store()
{
echo 'store';
}
public function create()
{
echo 'create';
}
public function edit($id)
{
echo 'edit ' . $id;
}
public function update($id)
{
echo 'update ' . $id;
}
public function destroy($id)
{
echo 'destroy ' . $id;
}
}
+26 -38
View File
@@ -2,8 +2,7 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
require_once 'Helpers/TestRouter.php';
class GroupTest extends PHPUnit_Framework_TestCase
{
@@ -13,82 +12,71 @@ class GroupTest extends PHPUnit_Framework_TestCase
{
$this->result = false;
SimpleRouter::group(['prefix' => '/group'], function () {
TestRouter::group(['prefix' => '/group'], function () {
$this->result = true;
});
try {
SimpleRouter::start();
} catch (Exception $e) {
// ignore RouteNotFound exception
}
TestRouter::debug('/', 'get');
} catch(\Exception $e) {
}
$this->assertTrue($this->result);
}
public function testNestedGroup()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/api/v1/test');
SimpleRouter::request()->setMethod('get');
TestRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/test', 'DummyController@method1');
});
});
SimpleRouter::start();
TestRouter::debug('/api/v1/test', 'get');
}
public function testManyRoutes()
public function testMultipleRoutes()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/match');
SimpleRouter::request()->setMethod('get');
TestRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/test', 'DummyController@method1');
});
});
SimpleRouter::get('/my/match', 'DummyController@start');
TestRouter::get('/my/match', 'DummyController@method1');
SimpleRouter::group(['prefix' => '/service'], function () {
TestRouter::group(['prefix' => '/service'], function () {
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/no-match', 'DummyController@start');
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/no-match', 'DummyController@method1');
});
});
SimpleRouter::start();
TestRouter::debug('/my/match', 'get');
}
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/fancy/url/1');
SimpleRouter::request()->setMethod('get');
// Test array name
SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']);
TestRouter::get('/my/fancy/url/1', 'DummyController@method1', ['as' => 'fancy1']);
// Test method name
SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setName('fancy2');
TestRouter::get('/my/fancy/url/2', 'DummyController@method1')->setName('fancy2');
SimpleRouter::start();
TestRouter::debugNoReset('/my/fancy/url/1');
$this->assertEquals('/my/fancy/url/1/', SimpleRouter::getUrl('fancy1'));
$this->assertEquals('/my/fancy/url/2/', SimpleRouter::getUrl('fancy2'));
$this->assertEquals('/my/fancy/url/1/', TestRouter::getUrl('fancy1'));
$this->assertEquals('/my/fancy/url/2/', TestRouter::getUrl('fancy2'));
TestRouter::router()->reset();
}
+41
View File
@@ -0,0 +1,41 @@
<?php
class TestRouter extends \Pecee\SimpleRouter\SimpleRouter
{
public static function debugNoReset($testUri, $testMethod = 'get')
{
static::request()->setUri($testUri);
static::request()->setMethod($testMethod);
static::start();
}
public static function debug($testUri, $testMethod = 'get')
{
try {
static::debugNoReset($testUri, $testMethod);
} catch(\Exception $e) {
static::router()->reset();
throw $e;
}
static::router()->reset();
}
public static function debugOutput($testUri, $testMethod = 'get')
{
$response = null;
// Route request
ob_start();
static::debug($testUri, $testMethod);
$response = ob_get_contents();
ob_end_clean();
// Return response
return $response;
}
}
+12 -19
View File
@@ -3,39 +3,32 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
require_once 'Helpers/TestRouter.php';
class MiddlewareTest extends PHPUnit_Framework_TestCase
{
public function testMiddlewareFound()
{
$this->setExpectedException('MiddlewareLoadedException');
$this->setExpectedException(MiddlewareLoadedException::class);
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
TestRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
TestRouter::get('/my/test/url', 'DummyController@method1', ['middleware' => 'DummyMiddleware']);
});
SimpleRouter::start();
TestRouter::debug('/my/test/url', 'get');
}
public function testNestedMiddlewareLoad()
public function testNestedMiddlewareDontLoad()
{
$this->setExpectedException('MiddlewareLoadedException');
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler', 'middleware' => 'DummyMiddleware'], function () {
SimpleRouter::get('/my/test/url', 'DummyController@start');
TestRouter::group(['exceptionHandler' => 'ExceptionHandler', 'middleware' => 'DummyMiddleware'], function () {
TestRouter::get('/middleware', 'DummyController@method1');
});
SimpleRouter::start();
TestRouter::get('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'get');
}
}
@@ -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);
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
require_once 'Dummy/ResourceController.php';
require_once 'Helpers/TestRouter.php';
class RouterResourceTest extends PHPUnit_Framework_TestCase
{
public function testResourceStore()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource', 'post');
$this->assertEquals('store', $response);
}
public function testResourceCreate()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/create', 'get');
$this->assertEquals('create', $response);
}
public function testResourceIndex()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource', 'get');
$this->assertEquals('index', $response);
}
public function testResourceDestroy()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'delete');
$this->assertEquals('destroy 38', $response);
}
public function testResourceEdit()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38/edit', 'get');
$this->assertEquals('edit 38', $response);
}
public function testResourceUpdate()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'put');
$this->assertEquals('update 38', $response);
}
public function testResourceGet()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'get');
$this->assertEquals('show 38', $response);
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exceptions/ResponseException.php';
require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
require_once 'Dummy/Handler/ExceptionHandlerThird.php';
require_once 'Helpers/TestRouter.php';
class RouteRewriteTest extends PHPUnit_Framework_TestCase
{
/**
* Redirects to another route through 3 exception handlers.
*
* You will see "ExceptionHandler 1 loaded" 2 times. This happen because
* the exceptionhandler is asking the router to reload.
*
* That means that the exceptionhandler is loaded again, but this time
* the router ignores the same rewrite-route to avoid loop - loads
* the second which have same behavior and is also ignored before
* throwing the final Exception in ExceptionHandler 3.
*
* So this tests:
* 1. If ExceptionHandlers loads
* 2. If ExceptionHandlers load in the correct order
* 3. If ExceptionHandlers can rewrite the page on error
* 4. If the router can avoid redirect-loop due to developer has started loop.
* 5. And finally if we reaches the last exception-handler and that the correct
* exception-type is being thrown.
*/
public function testExceptionHandlerRewrite()
{
global $stack;
$stack = [];
TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () {
TestRouter::group(['exceptionHandler' => ExceptionHandlerThird::class], function () {
TestRouter::get('/my-path', 'DummyController@method1');
});
});
try {
TestRouter::debug('/my-non-existing-path', 'get');
} catch (\ResponseException $e) {
}
$expectedStack = [
ExceptionHandlerFirst::class,
ExceptionHandlerSecond::class,
ExceptionHandlerThird::class,
];
$this->assertEquals($expectedStack, $stack);
}
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');
}
}
+72 -97
View File
@@ -3,12 +3,7 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exceptions/ExceptionHandlerException.php';
require_once 'Dummy/Handler/TestExceptionHandlerFirst.php';
require_once 'Dummy/Handler/TestExceptionHandlerSecond.php';
require_once 'Dummy/Handler/TestExceptionHandlerThird.php';
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException as NotFoundHttpException;
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
require_once 'Helpers/TestRouter.php';
class RouterRouteTest extends PHPUnit_Framework_TestCase
{
@@ -16,113 +11,57 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
public function testMultiParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
TestRouter::get('/test-{param1}-{param2}', function ($param1, $param2) {
SimpleRouter::get('/test-{param1}-{param2}', function($param1, $param2) {
if($param1 === 'param1' && $param2 === 'param2') {
if ($param1 === 'param1' && $param2 === 'param2') {
$this->result = true;
}
});
SimpleRouter::start();
TestRouter::debug('/test-param1-param2', 'get');
$this->assertTrue($this->result);
}
/**
* Redirects to another route through 3 exception handlers.
*
* You will see "ExceptionHandler 1 loaded" 2 times. This happen because
* the exceptionhandler is asking the router to reload.
*
* That means that the exceptionhandler is loaded again, but this time
* the router ignores the same rewrite-route to avoid loop - loads
* the second which have same behavior and is also ignored before
* throwing the final Exception in ExceptionHandler 3.
*
* So this tests:
* 1. If ExceptionHandlers loads
* 2. If ExceptionHandlers load in the correct order
* 3. If ExceptionHandlers can rewrite the page on error
* 4. If the router can avoid redirect-loop due to developer has started loop.
* 5. And finally if we reaches the last exception-handler and that the correct
* exception-type is being thrown.
*/
public function testNotFound()
{
$this->setExpectedException('ExceptionHandlerException');
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
SimpleRouter::group(['exceptionHandler' => ['TestExceptionHandlerFirst', 'TestExceptionHandlerSecond']], function () {
SimpleRouter::group(['exceptionHandler' => 'TestExceptionHandlerThird'], function () {
SimpleRouter::get('/non-existing-path', 'DummyController@start');
});
});
SimpleRouter::start();
$this->setExpectedException('\Pecee\SimpleRouter\Exceptions\NotFoundHttpException');
TestRouter::get('/non-existing-path', 'DummyController@method1');
TestRouter::debug('/test-param1-param2', 'post');
}
public function testGet()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('get');
SimpleRouter::get('/my/test/url', 'DummyController@start');
SimpleRouter::start();
TestRouter::get('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'get');
}
public function testPost()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
SimpleRouter::post('/my/test/url', 'DummyController@start');
SimpleRouter::start();
TestRouter::post('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'post');
}
public function testPut()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('put');
SimpleRouter::put('/my/test/url', 'DummyController@start');
SimpleRouter::start();
TestRouter::put('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'put');
}
public function testDelete()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('delete');
SimpleRouter::delete('/my/test/url', 'DummyController@start');
SimpleRouter::start();
TestRouter::delete('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'delete');
}
public function testMethodNotAllowed()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
SimpleRouter::get('/my/test/url', 'DummyController@start');
TestRouter::get('/my/test/url', 'DummyController@method1');
try {
SimpleRouter::start();
TestRouter::debug('/my/test/url', 'post');
} catch (\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
@@ -130,43 +69,79 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
public function testSimpleParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1');
TestRouter::get('/test-{param1}', 'DummyController@param');
$response = TestRouter::debugOutput('/test-param1', 'get');
SimpleRouter::get('/test-{param1}', 'DummyController@param');
SimpleRouter::start();
$this->assertEquals('param1', $response);
}
public function testPathParamRegex()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test/path/123123');
TestRouter::get('/{lang}/productscategories/{name}', 'DummyController@param', ['where' => ['lang' => '[a-z]+', 'name' => '[A-Za-z0-9\-]+']]);
$response = TestRouter::debugOutput('/it/productscategories/system', 'get');
SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
SimpleRouter::start();
$this->assertEquals('it, system', $response);
}
public function testDomainRoute()
public function testDomainAllowedRoute()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test');
SimpleRouter::request()->setHost('hello.world.com');
$this->result = false;
SimpleRouter::group(['domain' => '{subdomain}.world.com'], function () {
SimpleRouter::get('/test', function ($subdomain = null) {
TestRouter::group(['domain' => '{subdomain}.world.com'], function () {
TestRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
SimpleRouter::start();
TestRouter::request()->setHost('hello.world.com');
TestRouter::debug('/test', 'get');
$this->assertTrue($this->result);
}
public function testDomainNotAllowedRoute()
{
$this->result = false;
TestRouter::group(['domain' => '{subdomain}.world.com'], function () {
TestRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
TestRouter::request()->setHost('other.world.com');
TestRouter::debug('/test', 'get');
$this->assertFalse($this->result);
}
public function testRegEx()
{
TestRouter::get('/my/{path}', 'DummyController@method1')->where(['path' => '[a-zA-Z\-]+']);
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);
}
}
+64 -35
View File
@@ -3,99 +3,128 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
require_once 'Helpers/TestRouter.php';
class RouterUrlTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
protected function getUrl($name = null, $parameters = null, array $getParams = [])
public function testOptionalParameters()
{
return SimpleRouter::getUrl($name, $parameters, $getParams);
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');
TestRouter::resource('/url1', 'DummyController@method1', ['as' => 'match']);
TestRouter::debugNoReset('/url1', 'get');
$this->assertEquals(TestRouter::getUrl('match'), TestRouter::getUrl());
TestRouter::router()->reset();
}
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/');
// Match normal route on alias
SimpleRouter::get('/', 'DummyController@silent', ['as' => 'home']);
TestRouter::get('/', 'DummyController@method1', ['as' => 'home']);
SimpleRouter::get('/about', 'DummyController@about');
TestRouter::get('/about', 'DummyController@about');
SimpleRouter::group(['prefix' => '/admin', 'as' => 'admin'], function () {
TestRouter::group(['prefix' => '/admin', 'as' => 'admin'], function () {
// Match route with prefix on alias
SimpleRouter::get('/{id?}', 'DummyController@start', ['as' => 'home']);
TestRouter::get('/{id?}', 'DummyController@method2', ['as' => 'home']);
// Match controller with prefix and alias
SimpleRouter::controller('/users', 'DummyController', ['as' => 'users']);
TestRouter::controller('/users', 'DummyController', ['as' => 'users']);
// Match controller with prefix and NO alias
SimpleRouter::controller('/pages', 'DummyController');
TestRouter::controller('/pages', 'DummyController');
});
SimpleRouter::group(['prefix' => 'api', 'as' => 'api'], function () {
TestRouter::group(['prefix' => 'api', 'as' => 'api'], function () {
// Match resource controller
SimpleRouter::resource('phones', 'DummyController');
TestRouter::resource('phones', 'DummyController');
});
SimpleRouter::controller('gadgets', 'DummyController', ['names' => ['getIphoneInfo' => 'iphone']]);
TestRouter::controller('gadgets', 'DummyController', ['names' => ['getIphoneInfo' => 'iphone']]);
// Match controller with no prefix and no alias
SimpleRouter::controller('/cats', 'CatsController');
TestRouter::controller('/cats', 'CatsController');
// Pretend to load page
SimpleRouter::start();
TestRouter::debugNoReset('/', 'get');
$this->assertEquals('/gadgets/iphoneinfo/', $this->getUrl('gadgets.iphone'));
$this->assertEquals('/gadgets/iphoneinfo/', TestRouter::getUrl('gadgets.iphone'));
$this->assertEquals('/api/phones/create/', $this->getUrl('api.phones.create'));
$this->assertEquals('/api/phones/create/', TestRouter::getUrl('api.phones.create'));
// Should match /
$this->assertEquals('/', $this->getUrl('home'));
$this->assertEquals('/', TestRouter::getUrl('home'));
// Should match /about/
$this->assertEquals('/about/', $this->getUrl('DummyController@about'));
$this->assertEquals('/about/', TestRouter::getUrl('DummyController@about'));
// Should match /admin/
$this->assertEquals('/admin/', $this->getUrl('DummyController@start'));
$this->assertEquals('/admin/', TestRouter::getUrl('DummyController@method2'));
// Should match /admin/
$this->assertEquals('/admin/', $this->getUrl('admin.home'));
$this->assertEquals('/admin/', TestRouter::getUrl('admin.home'));
// Should match /admin/2/
$this->assertEquals('/admin/2/', $this->getUrl('admin.home', ['id' => 2]));
$this->assertEquals('/admin/2/', TestRouter::getUrl('admin.home', ['id' => 2]));
// Should match /admin/users/
$this->assertEquals('/admin/users/', $this->getUrl('admin.users'));
$this->assertEquals('/admin/users/', TestRouter::getUrl('admin.users'));
// Should match /admin/users/home/
$this->assertEquals('/admin/users/home/', $this->getUrl('admin.users@home'));
$this->assertEquals('/admin/users/home/', TestRouter::getUrl('admin.users@home'));
// Should match /cats/
$this->assertEquals('/cats/', $this->getUrl('CatsController'));
$this->assertEquals('/cats/', TestRouter::getUrl('CatsController'));
// Should match /cats/view/
$this->assertEquals('/cats/view/', $this->getUrl('CatsController', 'view'));
$this->assertEquals('/cats/view/', TestRouter::getUrl('CatsController', 'view'));
// Should match /cats/view/
//$this->assertEquals('/cats/view/', $this->getUrl('CatsController', ['view']));
//$this->assertEquals('/cats/view/', TestRouter::getUrl('CatsController', ['view']));
// Should match /cats/view/666
$this->assertEquals('/cats/view/666/', $this->getUrl('CatsController@getView', ['666']));
$this->assertEquals('/cats/view/666/', TestRouter::getUrl('CatsController@getView', ['666']));
// Should match /funny/man/
$this->assertEquals('/funny/man/', $this->getUrl('/funny/man'));
$this->assertEquals('/funny/man/', TestRouter::getUrl('/funny/man'));
// Should match /?jackdaniels=true&cola=yeah
$this->assertEquals('/?jackdaniels=true&cola=yeah', $this->getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
$this->assertEquals('/?jackdaniels=true&cola=yeah', TestRouter::getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
TestRouter::router()->reset();
}