mirror of
https://github.com/skipperbent/simple-php-router.git
synced 2026-06-17 16:57:53 +00:00
Compare commits
278 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6341960d7 | |||
| 72d33dd497 | |||
| e23dd37435 | |||
| aa5ec47051 | |||
| 85cf925793 | |||
| 155729074b | |||
| 7c0a20115e | |||
| 0be7bfcfd9 | |||
| 877e0aa937 | |||
| 7f8d90eef8 | |||
| 3e7767d978 | |||
| 4bb784bcec | |||
| c4ee1b9186 | |||
| 5ef9349b18 | |||
| efd5159604 | |||
| 264f0a7b0f | |||
| bdfc36ed5c | |||
| 5197ae2990 | |||
| d921ae8105 | |||
| 9c701aabee | |||
| af2be14ccb | |||
| 2b0821a557 | |||
| fae2e84c98 | |||
| afc81d7005 | |||
| c90c74b88f | |||
| 05f2493304 | |||
| 0856caa9de | |||
| 35dc26d741 | |||
| 496d3e7182 | |||
| f2819f866e | |||
| b9aa348b38 | |||
| c29c52ae16 | |||
| 6547c07113 | |||
| 1fd13ed2aa | |||
| fde77969c0 | |||
| c3072e8886 | |||
| 9d5c4a2ed1 | |||
| 97753f5370 | |||
| b8634bcf79 | |||
| 6ad22a3816 | |||
| ed41cd55af | |||
| ebeca952cf | |||
| 0672e85fd7 | |||
| 927f8d7b3c | |||
| 74177a2082 | |||
| 2221bced4f | |||
| 6559278511 | |||
| 8b9698229d | |||
| a565f66c4c | |||
| 832ef992a3 | |||
| cc5e417db9 | |||
| 2cc90e28d0 | |||
| eb63a5d6ba | |||
| a07b30a80d | |||
| c45cd6347a | |||
| 4a353efc97 | |||
| f7ce440c56 | |||
| 41705f030a | |||
| 18fa0f9610 | |||
| 66ecf0ee33 | |||
| 4ba15033d9 | |||
| 60393a3722 | |||
| cfa18e520a | |||
| 5e448f0835 | |||
| 98ad310404 | |||
| a5aac57ce9 | |||
| 7f924c7d0a | |||
| 3a90578351 | |||
| 52e0f5ef94 | |||
| 3df3ef36ef | |||
| 0090c167bb | |||
| ae68598024 | |||
| c723ca7e61 | |||
| 7362b748af | |||
| 4fe85d6568 | |||
| c82d91375c | |||
| e3b6899375 | |||
| e774122b1e | |||
| e8aceb291c | |||
| 957a382248 | |||
| a179450018 | |||
| 48e2e3f9bc | |||
| f95e12c49c | |||
| 9f509ac818 | |||
| ac3e9ed2ac | |||
| 3b4d23e9ae | |||
| b3e99a2283 | |||
| c1aa5782a1 | |||
| 387d9710d8 | |||
| fd098266f7 | |||
| c7473938c5 | |||
| 65c811356d | |||
| ea255baec3 | |||
| acf37c4023 | |||
| 6fb92d7cae | |||
| e84c8c2f02 | |||
| 7c970c442c | |||
| 4cd1e8e069 | |||
| 8720e732e1 | |||
| 7e8cb91f68 | |||
| bc67038e11 | |||
| e31d8af2f7 | |||
| 9f285fd0ce | |||
| b7f5d31544 | |||
| 5086347802 | |||
| 26f89213a2 | |||
| bb7991afdc | |||
| 796d01bc25 | |||
| 74187ee326 | |||
| f090d6c038 | |||
| 6e859e11ab | |||
| 6aa38cfa4c | |||
| 62f0075cf3 | |||
| 6890b60737 | |||
| a40f81d5fc | |||
| a5527a0e8c | |||
| 3c73de866e | |||
| 883d8a6b0e | |||
| 3a7b27796a | |||
| 1e07140865 | |||
| 4cca5186f3 | |||
| 5437420175 | |||
| 410fa3c9f5 | |||
| be20fe4dd1 | |||
| bc03490100 | |||
| 0947f6746e | |||
| 31c8710ce7 | |||
| 1ac7761d35 | |||
| 9eb7c5c13c | |||
| 5151461a02 | |||
| b07348a3df | |||
| d411b31cc2 | |||
| ca381d445f | |||
| 2c61cef7ad | |||
| 98ee60859f | |||
| 9dd80dd1d9 | |||
| 3f9ab73f7f | |||
| fef997422e | |||
| 8901e7c125 | |||
| 50e8926272 | |||
| e44d6bc30b | |||
| 3f61c90749 | |||
| e4e632dca9 | |||
| a5c4a1f721 | |||
| 0bc7fa7bd5 | |||
| eb036ded3b | |||
| debe080181 | |||
| dd99e1d488 | |||
| 1c07311c5d | |||
| 801c84eb05 | |||
| ed98519152 | |||
| 55d031e152 | |||
| 8509062e00 | |||
| 4b4ab906fb | |||
| 9617cacc31 | |||
| 54a824ce44 | |||
| 99088719ed | |||
| 2062ff4189 | |||
| 3e41ee28b6 | |||
| 13a5f40cd0 | |||
| 751b4444ae | |||
| 351f7a01e8 | |||
| 8cd42a2c4b | |||
| 1496878ae9 | |||
| e8e1471bab | |||
| b3f2e5f812 | |||
| 4de1498723 | |||
| a60fa9f919 | |||
| ea243f2c89 | |||
| ce6f34f4d4 | |||
| 56653568e4 | |||
| 2868ffe023 | |||
| 5d330643e7 | |||
| 8ab52364da | |||
| 57aa8eac1e | |||
| 110bc2afcf | |||
| 4efc72d013 | |||
| a92a4cdf0d | |||
| 49fc991f9a | |||
| 0a4c78cf64 | |||
| 20353c6e4d | |||
| 6706d86784 | |||
| 4c62f86a26 | |||
| 20c1e0ef69 | |||
| 6445746324 | |||
| 1a2921acb4 | |||
| fe560a9ba5 | |||
| 258d9d05c7 | |||
| c522801c28 | |||
| 7e7319de06 | |||
| 9d275d6d3b | |||
| 531b35532b | |||
| e6dd9f3f55 | |||
| c258f937e8 | |||
| e803b1ca5c | |||
| 88d58cd7b7 | |||
| 45faf9830e | |||
| d04c74ccad | |||
| c915c0386a | |||
| 02809a4daf | |||
| 30479b15ca | |||
| da6b5af19f | |||
| 4dde51e833 | |||
| 914ec9d1b7 | |||
| 961a4d0e94 | |||
| 5f72755a98 | |||
| 2216090a5f | |||
| 41d15d3acd | |||
| d467f60e55 | |||
| ded9c8ebe0 | |||
| c30ae6b098 | |||
| 394f7beb8b | |||
| c93308c8e1 | |||
| baab004482 | |||
| c3241012af | |||
| 00c0cad211 | |||
| 9e2ba2674b | |||
| c6e85676da | |||
| 4d18f33a81 | |||
| 5a74c9d27d | |||
| 9d98b2da4c | |||
| b5eef6f3ee | |||
| 6930864e7e | |||
| ad0eceb814 | |||
| f3f129ae0b | |||
| 4bec2bc5fb | |||
| 85b9fac21e | |||
| 4f47463497 | |||
| 0c93633d13 | |||
| e5c86c1822 | |||
| 973344e46e | |||
| 32c305bd2c | |||
| 4a03005c68 | |||
| 52034411cf | |||
| 4c8ed5bb3d | |||
| 9fed6ffb3f | |||
| 15da599e82 | |||
| 9274acb591 | |||
| 5c7759ab72 | |||
| c7b8593185 | |||
| fb478f475c | |||
| 1142d9d4ce | |||
| 5d5c96e802 | |||
| bfdaf8ac52 | |||
| 71dc6e172f | |||
| 6ee172927f | |||
| bb5e629199 | |||
| 0cc0a59fd5 | |||
| 498fd6b07d | |||
| 96ab22a4f8 | |||
| 7f528c133b | |||
| 5a50190293 | |||
| 355ef01d63 | |||
| d3162b5a2b | |||
| 810b80487d | |||
| 18a9df56ca | |||
| 6e14ded03f | |||
| 899081f8d8 | |||
| e7b9206bc9 | |||
| cd6e800984 | |||
| 11bd5a7d11 | |||
| be32796b01 | |||
| 8b3d71a328 | |||
| 1fae638aaf | |||
| 37c8bc9f32 | |||
| 75029b330a | |||
| fd5d893040 | |||
| c1512740af | |||
| 212ae133de | |||
| ae58231fa1 | |||
| 358b25d4f1 | |||
| 3d45851d9b | |||
| ee5c2207f8 | |||
| b1ca3fc9ef | |||
| 253c0c70d4 | |||
| 53ba2d7ac5 | |||
| 315fe05769 | |||
| a57113309a |
+1
-2
@@ -1,4 +1,3 @@
|
||||
.idea
|
||||
composer.lock
|
||||
vendor/
|
||||
demo-project/vendor
|
||||
vendor/
|
||||
@@ -7,9 +7,31 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a
|
||||
|
||||
The goal of this project is to create a router that is more or less 100% compatible with the Laravel documentation, while remaining as simple as possible, and as easy to integrate and change without compromising either speed or complexity. Being lightweight is the #1 priority.
|
||||
|
||||
### Ideas and issues
|
||||
### Feedback and development
|
||||
|
||||
If you want a great new feature or experience any issues what-so-ever, please feel free to leave an issue and i'll look into it whenever possible.
|
||||
If you are missing a feature, experience problems or have ideas or feedback that you want us to hear, please feel free to create an issue.
|
||||
|
||||
###### Issues guidelines
|
||||
|
||||
- Please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue.
|
||||
For example: if you are experiencing issues, you should provide the necessary steps to reproduce the error within your description.
|
||||
|
||||
- We love to hear out any ideas or feedback to the library.
|
||||
|
||||
[Create a new issue here](https://github.com/skipperbent/simple-php-router/issues/new)
|
||||
|
||||
###### Contribution development guidelines
|
||||
|
||||
- Please try to follow the PSR-2 codestyle guidelines.
|
||||
|
||||
- Please create your pull requests to the development base that matches the version number you want to change.
|
||||
For example when pushing changes to version 3, the pull request should use the `v3-development` base/branch.
|
||||
|
||||
- Create detailed descriptions for your commits, as these will be used in the changelog for new releases.
|
||||
|
||||
- When changing existing functionality, please ensure that the unit-tests working.
|
||||
|
||||
- When adding new stuff, please remember to add new unit-tests for the functionality.
|
||||
|
||||
---
|
||||
|
||||
@@ -22,6 +44,7 @@ If you want a great new feature or experience any issues what-so-ever, please fe
|
||||
- [Installation](#installation)
|
||||
- [Setting up Apache](#setting-up-apache)
|
||||
- [Setting up Nginx](#setting-up-nginx)
|
||||
- [Setting up IIS](#setting-up-iis)
|
||||
- [Configuration](#configuration)
|
||||
- [Helper functions](#helper-functions)
|
||||
|
||||
@@ -34,6 +57,7 @@ If you want a great new feature or experience any issues what-so-ever, please fe
|
||||
- [Optional parameters](#optional-parameters)
|
||||
- [Regular expression constraints](#regular-expression-constraints)
|
||||
- [Regular expression route-match](#regular-expression-route-match)
|
||||
- [Custom regex for matching parameters](#custom-regex-for-matching-parameters)
|
||||
- [Named routes](#named-routes)
|
||||
- [Generating URLs To Named Routes](#generating-urls-to-named-routes)
|
||||
- [Router groups](#router-groups)
|
||||
@@ -41,6 +65,7 @@ If you want a great new feature or experience any issues what-so-ever, please fe
|
||||
- [Namespaces](#namespaces)
|
||||
- [Subdomain-routing](#subdomain-routing)
|
||||
- [Route prefixes](#route-prefixes)
|
||||
- [Partial groups](#partial-groups)
|
||||
- [Form Method Spoofing](#form-method-spoofing)
|
||||
- [Accessing The Current Route](#accessing-the-current-route)
|
||||
- [Other examples](#other-examples)
|
||||
@@ -48,11 +73,14 @@ If you want a great new feature or experience any issues what-so-ever, please fe
|
||||
- [CSRF-protection](#csrf-protection)
|
||||
- [Adding CSRF-verifier](#adding-csrf-verifier)
|
||||
- [Getting CSRF-token](#getting-csrf-token)
|
||||
- [Custom CSRF-verifier](#custom-csrf-verifier)
|
||||
- [Custom Token-provider](#custom-token-provider)
|
||||
|
||||
- [Middlewares](#middlewares)
|
||||
- [Example](#example)
|
||||
- [ExceptionHandler](#exceptionhandler)
|
||||
- [Example](#example-1)
|
||||
- [ExceptionHandlers](#exceptionhandlers)
|
||||
- [Handling 404, 403 and other errors](#handling-404-403-and-other-errors)
|
||||
- [Using custom exception handlers](#using-custom-exception-handlers)
|
||||
|
||||
- [Urls](#urls)
|
||||
- [Get by name (single route)](#get-by-name-single-route)
|
||||
@@ -70,12 +98,12 @@ If you want a great new feature or experience any issues what-so-ever, please fe
|
||||
- [Get all parameters](#get-all-parameters)
|
||||
|
||||
- [Advanced](#advanced)
|
||||
- [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically)
|
||||
- [Url rewriting](#url-rewriting)
|
||||
- [Rewrite using callback](#rewrite-using-callback)
|
||||
- [Rewrite using url](#rewrite-using-url)
|
||||
|
||||
- [Adding routes manually](#adding-routes-manually)
|
||||
- [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically)
|
||||
- [Adding routes manually](#adding-routes-manually)
|
||||
- [Parameters](#parameters)
|
||||
- [Extending](#extending)
|
||||
|
||||
- [Credits](#credits)
|
||||
@@ -155,6 +183,77 @@ location / {
|
||||
|
||||
Nothing special is required for Apache to work. We've include the `.htaccess` file in the `public` folder. If rewriting is not working for you, please check that the `mod_rewrite` module (htaccess support) is enabled in the Apache configuration.
|
||||
|
||||
#### .htaccess example
|
||||
|
||||
Below is an example of an working `.htaccess` file used by simple-php-router.
|
||||
|
||||
Simply create a new `.htaccess` file in your projects `public` directory and paste the contents below in your newly created file. This will redirect all requests to your `index.php` file (see Configuration section below).
|
||||
|
||||
```
|
||||
RewriteEngine on
|
||||
RewriteCond %{SCRIPT_FILENAME} !-f
|
||||
RewriteCond %{SCRIPT_FILENAME} !-d
|
||||
RewriteCond %{SCRIPT_FILENAME} !-l
|
||||
RewriteRule ^(.*)$ index.php/$1
|
||||
```
|
||||
|
||||
### Setting up IIS
|
||||
|
||||
On IIS you have to add some lines your `web.config` file in the `public` folder or create a new one. If rewriting is not working for you, please check that your IIS version have included the `url rewrite` module or download and install them from Microsoft web site.
|
||||
|
||||
#### web.config example
|
||||
|
||||
Below is an example of an working `web.config` file used by simple-php-router.
|
||||
|
||||
Simply create a new `web.config` 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). If the `web.config` file already exists, add the `<rewrite>` section inside the `<system.webServer>` branch.
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<!-- Remove slash '/' from the en of the url -->
|
||||
<rule name="RewriteRequestsToPublic">
|
||||
<match url="^(.*)$" />
|
||||
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
|
||||
</conditions>
|
||||
<action type="Rewrite" url="/{R:0}" />
|
||||
</rule>
|
||||
|
||||
<!-- When requested file or folder don't exists, will request again through index.php -->
|
||||
<rule name="Imported Rule 1" stopProcessing="true">
|
||||
<match url="^(.*)$" ignoreCase="true" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="/index.php/{R:1}" appendQueryString="true" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
If you do not have a `favicon.ico` file in your project, you can get a `NotFoundHttpException` (404 - not found).
|
||||
To add `favicon.ico` to the IIS ignore-list, add the following line to the `<conditions>` group:
|
||||
```
|
||||
<add input="{REQUEST_FILENAME}" negate="true" pattern="favicon.ico" ignoreCase="true" />
|
||||
```
|
||||
|
||||
You can also make one exception for files with some extensions:
|
||||
```
|
||||
<add input="{REQUEST_FILENAME}" pattern="\.ico|\.png|\.css|\.jpg" negate="true" ignoreCase="true" />
|
||||
```
|
||||
|
||||
If you are using `$_SERVER['ORIG_PATH_INFO']`, you will get `\index.php\` as part of the returned value. For example:
|
||||
```
|
||||
/index.php/test/mypage.php
|
||||
```
|
||||
|
||||
### 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.
|
||||
@@ -187,12 +286,13 @@ SimpleRouter::start();
|
||||
|
||||
### Helper functions
|
||||
|
||||
We recommend that you add these helper functions to your project. Theese will allow you to access functionality of the router more easily.
|
||||
We recommend that you add these helper functions to your project. These will allow you to access functionality of the router more easily.
|
||||
|
||||
To implement the functions below, simply copy the code to a new file and require the file before initializing the router.
|
||||
To implement the functions below, simply copy the code to a new file and require the file before initializing the router or copy the `helpers.php` we've included in this library.
|
||||
|
||||
```php
|
||||
<?php
|
||||
use Pecee\SimpleRouter\SimpleRouter as Router;
|
||||
|
||||
/**
|
||||
* Get url for a route by using either name/alias, class or method name.
|
||||
*
|
||||
@@ -212,7 +312,7 @@ To implement the functions below, simply copy the code to a new file and require
|
||||
*/
|
||||
function url($name = null, $parameters = null, $getParams = null)
|
||||
{
|
||||
return SimpleRouter::getUrl($name, $parameters, $getParams);
|
||||
return Router::getUrl($name, $parameters, $getParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,7 +320,7 @@ function url($name = null, $parameters = null, $getParams = null)
|
||||
*/
|
||||
function response()
|
||||
{
|
||||
return SimpleRouter::response();
|
||||
return Router::response();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,15 +328,22 @@ function response()
|
||||
*/
|
||||
function request()
|
||||
{
|
||||
return SimpleRouter::request();
|
||||
return Router::request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input class
|
||||
* @return \Pecee\Http\Input\Input
|
||||
* @param string|null $index Parameter index name
|
||||
* @param string|null $defaultValue Default return value
|
||||
* @param string|array|null $methods Default method
|
||||
* @return \Pecee\Http\Input\Input|string
|
||||
*/
|
||||
function input()
|
||||
function input($index = null, $defaultValue = null, $methods = null)
|
||||
{
|
||||
if ($index !== null) {
|
||||
return request()->getInput()->get($index, $defaultValue, $methods);
|
||||
}
|
||||
|
||||
return request()->getInput();
|
||||
}
|
||||
|
||||
@@ -248,6 +355,20 @@ function redirect($url, $code = null)
|
||||
|
||||
response()->redirect($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current csrf-token
|
||||
* @return string|null
|
||||
*/
|
||||
function csrf_token()
|
||||
{
|
||||
$baseVerifier = Router::router()->getCsrfVerifier();
|
||||
if ($baseVerifier !== null) {
|
||||
return $baseVerifier->getToken();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
@@ -293,7 +414,7 @@ SimpleRouter::any('foo', function() {
|
||||
});
|
||||
```
|
||||
|
||||
We've created a simple method which matches `GET` and `POST` which is most commenly used:
|
||||
We've created a simple method which matches `GET` and `POST` which is most commonly used:
|
||||
|
||||
```php
|
||||
SimpleRouter::form('foo', function() {
|
||||
@@ -365,7 +486,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:
|
||||
|
||||
@@ -376,13 +497,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');
|
||||
```
|
||||
|
||||
@@ -448,7 +598,7 @@ SimpleRouter::group(['namespace' => 'Admin'], function () {
|
||||
});
|
||||
```
|
||||
|
||||
### Subdomain-routing
|
||||
### Sub domain-routing
|
||||
|
||||
Route groups may also be used to handle sub-domain routing. Sub-domains may be assigned route parameters just like route URIs, allowing you to capture a portion of the sub-domain for usage in your route or controller. The sub-domain may be specified using the `domain` key on the group attribute array:
|
||||
|
||||
@@ -472,6 +622,29 @@ SimpleRouter::group(['prefix' => '/admin'], function () {
|
||||
});
|
||||
```
|
||||
|
||||
## Partial groups
|
||||
|
||||
Partial router groups has the same benefits as a normal group, but supports parameters and are only rendered once the url has matched.
|
||||
|
||||
This can be extremely useful in situations, where you only want special routes to be added, when a certain criteria or logic has been met.
|
||||
|
||||
**NOTE:** Use partial groups with caution as routes added within are only rendered and available once the url of the partial-group has matched. This can cause `url()` not to find urls for the routes added within.
|
||||
|
||||
**Example:**
|
||||
|
||||
```php
|
||||
SimpleRouter::partialGroup('/admin/{applicationId}', function ($applicationId) {
|
||||
|
||||
SimpleRouter::get('/', function($applicationId) {
|
||||
|
||||
// Matches The "/admin/applicationId" URL
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Form Method Spoofing
|
||||
|
||||
HTML forms do not support `PUT`, `PATCH` or `DELETE` actions. So, when defining `PUT`, `PATCH` or `DELETE` routes that are called from an HTML form, you will need to add a hidden `_method` field to the form. The value sent with the `_method` field will be used as the HTTP request method:
|
||||
@@ -538,11 +711,57 @@ SimpleRouter::get('/page/404', 'ControllerPage@notFound', ['as' => 'page.notfoun
|
||||
|
||||
# CSRF Protection
|
||||
|
||||
Any forms posting to `POST`, `PUT` or `DELETE` routes should include the CSRF-token. We strongly recommend that you create your enable CSRF-verification on your site.
|
||||
Any forms posting to `POST`, `PUT` or `DELETE` routes should include the CSRF-token. We strongly recommend that you enable CSRF-verification on your site to maximize security.
|
||||
|
||||
Create a new class and extend the ```BaseCsrfVerifier``` middleware class provided with simple-php-router.
|
||||
You can use the `BaseCsrfVerifier` to enable CSRF-validation on all request. If you need to disable verification for specific urls, please refer to the "Custom CSRF-verifier" section below.
|
||||
|
||||
Add the property ```except``` with an array of the urls to the routes you would like to exclude/whitelist from the CSRF validation. Using ```*``` at the end for the url will match the entire url.
|
||||
By default simple-php-router will use the `CookieTokenProvider` class. This provider will store the security-token in a cookie on the clients machine.
|
||||
If you want to store the token elsewhere, please refer to the "Creating custom Token Provider" section below.
|
||||
|
||||
## Adding CSRF-verifier
|
||||
|
||||
When you've created your CSRF-verifier you need to tell simple-php-router that it should use it. You can do this by adding the following line in your `routes.php` file:
|
||||
|
||||
```php
|
||||
Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
|
||||
```
|
||||
|
||||
## Getting CSRF-token
|
||||
|
||||
When posting to any of the urls that has CSRF-verification enabled, you need post your CSRF-token or else the request will get rejected.
|
||||
|
||||
You can get the CSRF-token by calling the helper method:
|
||||
|
||||
```php
|
||||
csrf_token();
|
||||
```
|
||||
|
||||
You can also get the token directly:
|
||||
|
||||
```php
|
||||
return Router::router()->getCsrfVerifier()->getTokenProvider()->getToken();
|
||||
```
|
||||
|
||||
The default name/key for the input-field is `csrf_token` and is defined in the `POST_KEY` constant in the `BaseCsrfVerifier` class.
|
||||
You can change the key by overwriting the constant in your own CSRF-verifier class.
|
||||
|
||||
**Example:**
|
||||
|
||||
The example below will post to the current url with a hidden field "`csrf_token`".
|
||||
|
||||
```html
|
||||
<form method="post" action="<?= url(); ?>">
|
||||
<input type="hidden" name="csrf_token" value="<?= csrf_token(); ?>">
|
||||
<!-- other input elements here -->
|
||||
</form>
|
||||
```
|
||||
|
||||
## Custom CSRF-verifier
|
||||
|
||||
Create a new class and extend the `BaseCsrfVerifier` middleware class provided by default with the simple-php-router library.
|
||||
|
||||
Add the property `except` with an array of the urls to the routes you want to exclude/whitelist from the CSRF validation.
|
||||
Using ```*``` at the end for the url will match the entire url.
|
||||
|
||||
**Here's a basic example on a CSRF-verifier class:**
|
||||
|
||||
@@ -560,22 +779,45 @@ class CsrfVerifier extends BaseCsrfVerifier
|
||||
}
|
||||
```
|
||||
|
||||
## Adding CSRF-verifier
|
||||
## Custom Token Provider
|
||||
|
||||
When you've created your CSRF verifier - you need to tell simple-php-router that it should use it. You can do this by adding the following line in your `routes.php` file:
|
||||
By default the `BaseCsrfVerifier` will use the `CookieTokenProvider` to store the token in a cookie on the clients machine.
|
||||
|
||||
If you need to store the token elsewhere, you can do that by creating your own class and implementing the `ITokenProvider` class.
|
||||
|
||||
```php
|
||||
Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
|
||||
class SessionTokenProvider implements ITokenProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Refresh existing token
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
// Implement your own functionality here...
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate valid CSRF token
|
||||
*
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function validate($token)
|
||||
{
|
||||
// Implement your own functionality here...
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Getting CSRF-token
|
||||
|
||||
When posting to any of the urls that has CSRF-verification enabled, you need post your CSRF-token or else the request will get rejected.
|
||||
|
||||
You can get the CSRF-token by calling the helper method:
|
||||
Next you need to set your custom `ITokenProvider` implementation on your `BaseCsrfVerifier` class in your routes file:
|
||||
|
||||
```php
|
||||
csrf_token();
|
||||
$verifier = new \dscuz\Middleware\CsrfVerifier();
|
||||
$verifier->setTokenProvider(new SessionTokenProvider());
|
||||
|
||||
Router::csrfVerifier($verifier);
|
||||
```
|
||||
|
||||
---
|
||||
@@ -611,13 +853,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).
|
||||
|
||||
@@ -635,7 +894,7 @@ class CustomExceptionHandler implements IExceptionHandler
|
||||
|
||||
/* You can use the exception handler to format errors depending on the request and type. */
|
||||
|
||||
if (stripos($request->getUri(), '/api') !== false) {
|
||||
if (stripos($request->getUri()->getPath(), '/api') !== false) {
|
||||
|
||||
response()->json([
|
||||
'error' => $error->getMessage(),
|
||||
@@ -762,7 +1021,7 @@ If items is grouped in the html, it will return an array of items.
|
||||
**Note:** `get` will automatically trim the value and ensure that it's not empty. If it's empty the `$defaultValue` will be returned.
|
||||
|
||||
```php
|
||||
$value = input()->get($index, $defaultValue, $methods);
|
||||
$value = input($index, $defaultValue, $methods);
|
||||
```
|
||||
|
||||
### Get parameter object
|
||||
@@ -771,11 +1030,11 @@ Will return an instance of `InputItem` or `InputFile` depending on the type.
|
||||
|
||||
You can use this in your html as it will render the value of the item.
|
||||
However if you want to compare value in your if statements, you have to use
|
||||
the `getValue` or use the `input()->get()` instead.
|
||||
the `getValue` or use the `input()` instead.
|
||||
|
||||
If items is grouped in the html, it will return an array of items.
|
||||
|
||||
**Note:** `getObject` will only return `$defaultValue` if the item doesn't exist. If you want `$defaultValue` to be returned if the item is empty, please use `input()->get()` instead.
|
||||
**Note:** `getObject` will only return `$defaultValue` if the item doesn't exist. If you want `$defaultValue` to be returned if the item is empty, please use `input()` instead.
|
||||
|
||||
```php
|
||||
$object = input()->getObject($index, $defaultValue = null, $methods = null);
|
||||
@@ -792,13 +1051,17 @@ $object = input()->getObject($index, $defaultValue = null, $methods = null);
|
||||
* $defaultValue is returned if the value is empty.
|
||||
*/
|
||||
|
||||
$id = input()->get($index, $defaultValue);
|
||||
$id = input()->get($index, $defaultValue, $method);
|
||||
|
||||
# -- shortcut to above --
|
||||
|
||||
$id = input($index, $defaultValue, $method);
|
||||
|
||||
# -- match specific --
|
||||
|
||||
$object = input()->get($index, $defaultValue, 'get');
|
||||
$object = input()->get($index, $defaultValue, 'post');
|
||||
$object = input()->get($index, $defaultValue, 'file');
|
||||
$object = input($index, $defaultValue, 'get');
|
||||
$object = input($index, $defaultValue, 'post');
|
||||
$object = input($index, $defaultValue, 'file');
|
||||
|
||||
# -- or --
|
||||
|
||||
@@ -817,7 +1080,7 @@ $object = input()->findFile($index, $defaultValue);
|
||||
*/
|
||||
|
||||
/* @var $image \Pecee\Http\Input\InputFile */
|
||||
foreach(input()->get('images', []) as $image)
|
||||
foreach(input('images', []) as $image)
|
||||
{
|
||||
if($image->getMime() === 'image/jpeg') {
|
||||
|
||||
@@ -850,6 +1113,7 @@ All object implements the `IInputItem` interface and will always contain these m
|
||||
- `getValue()` - returns the value of the input.
|
||||
|
||||
`InputFile` has the same methods as above along with some other file-specific methods like:
|
||||
- `getFilename` - get the filename.
|
||||
- `getTmpName()` - get file temporary name.
|
||||
- `getSize()` - get file size.
|
||||
- `move($destination)` - move file to destination.
|
||||
@@ -863,7 +1127,7 @@ Below example requires you to have the helper functions added. Please refer to t
|
||||
|
||||
```php
|
||||
/* Get parameter site_id or default-value 2 from either post-value or query-string */
|
||||
$siteId = input()->get('site_id', 2, ['post', 'get']);
|
||||
$siteId = input('site_id', 2, ['post', 'get']);
|
||||
```
|
||||
|
||||
---
|
||||
@@ -873,7 +1137,7 @@ $siteId = input()->get('site_id', 2, ['post', 'get']);
|
||||
## 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.
|
||||
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()`.
|
||||
|
||||
@@ -888,11 +1152,9 @@ $request->setRewriteCallback('Example\MyCustomClass@hello');
|
||||
$request->setRewriteUrl('/my-rewrite-url');
|
||||
```
|
||||
|
||||
### Examples
|
||||
**Note:** It's only possible to change the route BEFORE the route has initially been rendered. You can use the `Request` object to manipulate the route which are about to be loaded.
|
||||
|
||||
It's only possible to change the route BEFORE the route has initially been rendered. You can use the `Request` object to manipulate the route which are about to be loaded.
|
||||
|
||||
#### Rewrite using callback
|
||||
### Rewrite using callback
|
||||
|
||||
This method is most efficient, as it will render the route immediately.
|
||||
|
||||
@@ -908,7 +1170,7 @@ The example below will render `DefaultController@notFound` regardless of the url
|
||||
|
||||
**NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.**
|
||||
|
||||
#### Middleware example
|
||||
##### Middleware example
|
||||
|
||||
```php
|
||||
namespace Demo\Middlewares;
|
||||
@@ -928,7 +1190,7 @@ class CustomMiddleware implements IMiddleware {
|
||||
}
|
||||
```
|
||||
|
||||
#### Exception handler example
|
||||
##### Exception handler example
|
||||
|
||||
```php
|
||||
namespace Demo\Handlers;
|
||||
@@ -963,7 +1225,7 @@ class CustomExceptionHandler implements IExceptionHandler
|
||||
}
|
||||
```
|
||||
|
||||
#### Rewrite using url
|
||||
### Rewrite using url
|
||||
|
||||
The example below will cause the router to reload the request and reinitialize all the routes. This method is slower, but will ensure that all middlewares and rules for the route is loaded.
|
||||
|
||||
@@ -974,7 +1236,7 @@ We are using the `url()` helper function to get the uri to another route added i
|
||||
|
||||
**NOTE: Use this method if you want to fully load another route using it's settings (request method, middlewares etc).**
|
||||
|
||||
#### Middleware example
|
||||
##### Middleware example
|
||||
|
||||
The example below will redirect the request to the `home`-route.
|
||||
|
||||
@@ -995,7 +1257,7 @@ class CustomMiddleware implements IMiddleware {
|
||||
}
|
||||
```
|
||||
|
||||
# Bootmanager: loading routes dynamically
|
||||
### Bootmanager: loading routes dynamically
|
||||
|
||||
Sometimes it can be necessary to keep urls stored in the database, file or similar. In this example, we want the url ```/my-cat-is-beatiful``` to load the route ```/article/view/1``` which the router knows, because it's defined in the ```routes.php``` file.
|
||||
|
||||
@@ -1018,7 +1280,7 @@ class CustomRouterRules implement IRouterBootManager {
|
||||
|
||||
// If the current uri matches the url, we use our custom route
|
||||
|
||||
if($request->getUri() === $url) {
|
||||
if($request->getUri()->getPath() === $url) {
|
||||
$request->setRewriteUrl($rule);
|
||||
return $request;
|
||||
}
|
||||
@@ -1041,7 +1303,7 @@ The last thing we need to do, is to add our custom boot-manager to the ```routes
|
||||
SimpleRouter::addBootManager(new CustomRouterRules());
|
||||
```
|
||||
|
||||
## Adding routes manually
|
||||
### Adding routes manually
|
||||
|
||||
The ```SimpleRouter``` class referenced in the previous example, is just a simple helper class that knows how to communicate with the ```Router``` class.
|
||||
If you are up for a challenge, want the full control or simply just want to create your own ```Router``` helper class, this example is for you.
|
||||
@@ -1059,7 +1321,7 @@ $route = new RouteUrl('/answer/1', function() {
|
||||
|
||||
});
|
||||
|
||||
$route->setMiddleware(\Demo\Middlewares\AuthMiddleware::class);
|
||||
$route->addMiddleware(\Demo\Middlewares\AuthMiddleware::class);
|
||||
$route->setNamespace('\Demo\Controllers');
|
||||
$route->setPrefix('v1');
|
||||
|
||||
@@ -1067,6 +1329,10 @@ $route->setPrefix('v1');
|
||||
$router->addRoute($route);
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
This section contains advanced tips & tricks on extending the usage for parameters.
|
||||
|
||||
## Extending
|
||||
|
||||
This is a simple example of an integration into a framework.
|
||||
|
||||
+4
-1
@@ -2,11 +2,14 @@
|
||||
"name": "pecee/simple-router",
|
||||
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
|
||||
"keywords": [
|
||||
"router",
|
||||
"router",
|
||||
"routing",
|
||||
"route",
|
||||
"simple-php-router",
|
||||
"laravel",
|
||||
"pecee",
|
||||
"route"
|
||||
"php"
|
||||
],
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
use Pecee\SimpleRouter\SimpleRouter as Router;
|
||||
|
||||
/**
|
||||
* Get url for a route by using either name/alias, class or method name.
|
||||
*
|
||||
* The name parameter supports the following values:
|
||||
* - Route name
|
||||
* - Controller/resource name (with or without method)
|
||||
* - Controller class name
|
||||
*
|
||||
* When searching for controller/resource by name, you can use this syntax "route.name@method".
|
||||
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
|
||||
* If no arguments is specified, it will return the url for the current loaded route.
|
||||
*
|
||||
* @param string|null $name
|
||||
* @param string|array|null $parameters
|
||||
* @param array|null $getParams
|
||||
* @return string
|
||||
*/
|
||||
function url($name = null, $parameters = null, $getParams = null)
|
||||
{
|
||||
return Router::getUrl($name, $parameters, $getParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Pecee\Http\Response
|
||||
*/
|
||||
function response()
|
||||
{
|
||||
return Router::response();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Pecee\Http\Request
|
||||
*/
|
||||
function request()
|
||||
{
|
||||
return Router::request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input class
|
||||
* @param string|null $index Parameter index name
|
||||
* @param string|null $defaultValue Default return value
|
||||
* @param string|array|null $methods Default method
|
||||
* @return \Pecee\Http\Input\Input|string
|
||||
*/
|
||||
function input($index = null, $defaultValue = null, $methods = null)
|
||||
{
|
||||
if ($index !== null) {
|
||||
return request()->getInput()->get($index, $defaultValue, $methods);
|
||||
}
|
||||
|
||||
return request()->getInput();
|
||||
}
|
||||
|
||||
function redirect($url, $code = null)
|
||||
{
|
||||
if ($code !== null) {
|
||||
response()->httpCode($code);
|
||||
}
|
||||
|
||||
response()->redirect($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current csrf-token
|
||||
* @return string|null
|
||||
*/
|
||||
function csrf_token()
|
||||
{
|
||||
$baseVerifier = Router::router()->getCsrfVerifier();
|
||||
if ($baseVerifier !== null) {
|
||||
return $baseVerifier->getTokenProvider()->getToken();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
namespace Pecee;
|
||||
|
||||
class CsrfToken
|
||||
{
|
||||
const CSRF_KEY = 'XSRF-TOKEN';
|
||||
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* Generate random identifier for CSRF token
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generateToken()
|
||||
{
|
||||
if (function_exists('random_bytes')) {
|
||||
return bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
return bin2hex(openssl_random_pseudo_bytes(32));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate valid CSRF token
|
||||
*
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function validate($token)
|
||||
{
|
||||
if ($token !== null && $this->getToken() !== null) {
|
||||
return hash_equals($token, $this->getToken());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set csrf token cookie
|
||||
*
|
||||
* @param $token
|
||||
*/
|
||||
public function setToken($token)
|
||||
{
|
||||
setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get csrf token
|
||||
* @return string|null
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
if ($this->hasToken()) {
|
||||
return $_COOKIE[static::CSRF_KEY];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the csrf token has been defined
|
||||
* @return bool
|
||||
*/
|
||||
public function hasToken()
|
||||
{
|
||||
return isset($_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,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
@@ -35,7 +36,7 @@ class Input
|
||||
public function parseInputs()
|
||||
{
|
||||
/* Parse get requests */
|
||||
if (count($_GET) > 0) {
|
||||
if (count($_GET) !== 0) {
|
||||
$this->get = $this->handleGetPost($_GET);
|
||||
}
|
||||
|
||||
@@ -46,12 +47,12 @@ class Input
|
||||
parse_str(file_get_contents('php://input'), $postVars);
|
||||
}
|
||||
|
||||
if (count($postVars) > 0) {
|
||||
if (count($postVars) !== 0) {
|
||||
$this->post = $this->handleGetPost($postVars);
|
||||
}
|
||||
|
||||
/* Parse get requests */
|
||||
if (count($_FILES) > 0) {
|
||||
if (count($_FILES) !== 0) {
|
||||
$this->file = $this->parseFiles();
|
||||
}
|
||||
}
|
||||
@@ -60,20 +61,20 @@ class Input
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($_FILES as $key => $value) {
|
||||
foreach ((array)$_FILES as $key => $value) {
|
||||
|
||||
// Handle array input
|
||||
if (is_array($value['name']) === false) {
|
||||
$values['index'] = $key;
|
||||
$list[$key] = InputFile::createFromArray(array_merge($value, $values));
|
||||
$list[$key] = InputFile::createFromArray($values + $value);
|
||||
continue;
|
||||
}
|
||||
|
||||
$keys = [];
|
||||
$keys = [$key];
|
||||
|
||||
$files = $this->rearrangeFiles($value['name'], $keys, $value);
|
||||
|
||||
if (isset($list[$key])) {
|
||||
if (isset($list[$key]) === true) {
|
||||
$list[$key][] = $files;
|
||||
} else {
|
||||
$list[$key] = $files;
|
||||
@@ -87,40 +88,30 @@ class Input
|
||||
protected function rearrangeFiles(array $values, &$index, $original)
|
||||
{
|
||||
|
||||
$originalIndex = $index[0];
|
||||
array_shift($index);
|
||||
|
||||
$output = [];
|
||||
|
||||
$getItem = function ($key, $property = 'name') use ($original, $index) {
|
||||
|
||||
$path = $original[$property];
|
||||
|
||||
$fileValues = array_values($index);
|
||||
|
||||
foreach ($fileValues as $i) {
|
||||
$path = $path[$i];
|
||||
}
|
||||
|
||||
return $path[$key];
|
||||
};
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
|
||||
if (is_array($getItem($key)) === false) {
|
||||
if (is_array($original['name'][$key]) === false) {
|
||||
|
||||
$file = InputFile::createFromArray([
|
||||
'index' => $key,
|
||||
'error' => $getItem($key, 'error'),
|
||||
'tmp_name' => $getItem($key, 'tmp_name'),
|
||||
'type' => $getItem($key, 'type'),
|
||||
'size' => $getItem($key, 'size'),
|
||||
'filename' => $getItem($key, 'name'),
|
||||
'index' => (empty($key) === true && empty($originalIndex) === false) ? $originalIndex : $key,
|
||||
'name' => $original['name'][$key],
|
||||
'error' => $original['error'][$key],
|
||||
'tmp_name' => $original['tmp_name'][$key],
|
||||
'type' => $original['type'][$key],
|
||||
'size' => $original['size'][$key],
|
||||
]);
|
||||
|
||||
if (isset($output[$key])) {
|
||||
if (isset($output[$key]) === true) {
|
||||
$output[$key][] = $file;
|
||||
} else {
|
||||
$output[$key] = $file;
|
||||
continue;
|
||||
}
|
||||
|
||||
$output[$key] = $file;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -128,7 +119,7 @@ class Input
|
||||
|
||||
$files = $this->rearrangeFiles($value, $index, $original);
|
||||
|
||||
if (isset($output[$key])) {
|
||||
if (isset($output[$key]) === true) {
|
||||
$output[$key][] = $files;
|
||||
} else {
|
||||
$output[$key] = $files;
|
||||
@@ -143,13 +134,7 @@ class Input
|
||||
{
|
||||
$list = [];
|
||||
|
||||
$max = count($array) - 1;
|
||||
$keys = array_keys($array);
|
||||
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
|
||||
$key = $keys[$i];
|
||||
$value = $array[$key];
|
||||
foreach ($array as $key => $value) {
|
||||
|
||||
// Handle array input
|
||||
if (is_array($value) === false) {
|
||||
@@ -217,15 +202,15 @@ class Input
|
||||
|
||||
$element = null;
|
||||
|
||||
if ($methods === null || in_array('get', $methods)) {
|
||||
if ($methods === null || in_array('get', $methods, false) === true) {
|
||||
$element = $this->findGet($index);
|
||||
}
|
||||
|
||||
if (($element === null && $methods === null) || ($methods !== null && in_array('post', $methods))) {
|
||||
if (($element === null && $methods === null) || ($methods !== null && in_array('post', $methods, false) === true)) {
|
||||
$element = $this->findPost($index);
|
||||
}
|
||||
|
||||
if (($element === null && $methods === null) || ($methods !== null && in_array('file', $methods))) {
|
||||
if (($element === null && $methods === null) || ($methods !== null && in_array('file', $methods, false) === true)) {
|
||||
$element = $this->findFile($index);
|
||||
}
|
||||
|
||||
@@ -269,29 +254,21 @@ class Input
|
||||
*/
|
||||
public function all(array $filter = null)
|
||||
{
|
||||
$output = $_POST;
|
||||
$output = $_GET;
|
||||
|
||||
if ($this->request->getMethod() === 'post') {
|
||||
|
||||
$contents = file_get_contents('php://input');
|
||||
|
||||
if (strpos(trim($contents), '{') === 0) {
|
||||
$output = json_decode($contents, true);
|
||||
if ($output === false) {
|
||||
$output = [];
|
||||
$post = json_decode($contents, true);
|
||||
if ($post !== false) {
|
||||
$output = array_merge($output, $post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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)) : $output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
class InputFile implements IInputItem
|
||||
@@ -16,7 +17,7 @@ class InputFile implements IInputItem
|
||||
$this->index = $index;
|
||||
|
||||
// Make the name human friendly, by replace _ with space
|
||||
$this->name = ucfirst(str_replace('_', ' ', $this->index));
|
||||
$this->name = ucfirst(str_replace('_', ' ', strtolower($this->index)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,19 +29,19 @@ class InputFile implements IInputItem
|
||||
*/
|
||||
public static function createFromArray(array $values)
|
||||
{
|
||||
if (!isset($values['index'])) {
|
||||
if (isset($values['index']) === false) {
|
||||
throw new \InvalidArgumentException('Index key is required');
|
||||
}
|
||||
|
||||
/* Easy way of ensuring that all indexes-are set and not filling the screen with isset() */
|
||||
|
||||
$values = array_merge([
|
||||
$values += [
|
||||
'tmp_name' => null,
|
||||
'type' => null,
|
||||
'size' => null,
|
||||
'name' => null,
|
||||
'error' => null,
|
||||
], $values);
|
||||
];
|
||||
|
||||
return (new static($values['index']))
|
||||
->setSize($values['size'])
|
||||
@@ -267,8 +268,9 @@ class InputFile implements IInputItem
|
||||
'tmp_name' => $this->tmpName,
|
||||
'type' => $this->type,
|
||||
'size' => $this->size,
|
||||
'name' => $this->filename,
|
||||
'name' => $this->name,
|
||||
'error' => $this->error,
|
||||
'filename' => $this->filename,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class InputItem implements IInputItem
|
||||
$this->value = $value;
|
||||
|
||||
// Make the name human friendly, by replace _ with space
|
||||
$this->name = ucfirst(str_replace('_', ' ', $this->index));
|
||||
$this->name = ucfirst(str_replace('_', ' ', strtolower($this->index)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Middleware;
|
||||
|
||||
use Pecee\CsrfToken;
|
||||
use Pecee\Http\Middleware\Exceptions\TokenMismatchException;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\Http\Security\CookieTokenProvider;
|
||||
use Pecee\Http\Security\ITokenProvider;
|
||||
|
||||
class BaseCsrfVerifier implements IMiddleware
|
||||
{
|
||||
@@ -11,15 +13,11 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
const HEADER_KEY = 'X-CSRF-TOKEN';
|
||||
|
||||
protected $except;
|
||||
protected $csrfToken;
|
||||
protected $token;
|
||||
protected $tokenProvider;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->csrfToken = new CsrfToken();
|
||||
|
||||
// Generate or get the CSRF-Token from Cookie.
|
||||
$this->token = ($this->hasToken() === false) ? $this->generateToken() : $this->csrfToken->getToken();
|
||||
$this->tokenProvider = new CookieTokenProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,7 +27,7 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
*/
|
||||
protected function skip(Request $request)
|
||||
{
|
||||
if ($this->except === null || is_array($this->except) === false) {
|
||||
if ($this->except === null || count($this->except) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -41,9 +39,9 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
$url = rtrim($url, '/');
|
||||
if ($url[strlen($url) - 1] === '*') {
|
||||
$url = rtrim($url, '*');
|
||||
$skip = (stripos($request->getUri(), $url) === 0);
|
||||
$skip = (stripos($request->getUri()->getPath(), $url) === 0);
|
||||
} else {
|
||||
$skip = ($url === rtrim($request->getUri(), '/'));
|
||||
$skip = ($url === $request->getUri()->getPath());
|
||||
}
|
||||
|
||||
if ($skip === true) {
|
||||
@@ -54,6 +52,12 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle request
|
||||
*
|
||||
* @param Request $request
|
||||
* @throws TokenMismatchException
|
||||
*/
|
||||
public function handle(Request $request)
|
||||
{
|
||||
|
||||
@@ -66,34 +70,29 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
$token = $request->getHeader(static::HEADER_KEY);
|
||||
}
|
||||
|
||||
if ($this->csrfToken->validate($token) === false) {
|
||||
throw new TokenMismatchException('Invalid csrf-token.');
|
||||
if ($this->tokenProvider->validate($token) === false) {
|
||||
throw new TokenMismatchException('Invalid CSRF-token.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Refresh existing token
|
||||
$this->tokenProvider->refresh();
|
||||
|
||||
}
|
||||
|
||||
public function generateToken()
|
||||
public function getTokenProvider()
|
||||
{
|
||||
$token = CsrfToken::generateToken();
|
||||
$this->csrfToken->setToken($token);
|
||||
|
||||
return $token;
|
||||
return $this->tokenProvider;
|
||||
}
|
||||
|
||||
public function hasToken()
|
||||
/**
|
||||
* Set token provider
|
||||
* @param ITokenProvider $provider
|
||||
*/
|
||||
public function setTokenProvider(ITokenProvider $provider)
|
||||
{
|
||||
if ($this->token !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->csrfToken->hasToken();
|
||||
}
|
||||
|
||||
public function getToken()
|
||||
{
|
||||
return $this->token;
|
||||
$this->tokenProvider = $provider;
|
||||
}
|
||||
|
||||
}
|
||||
+16
-50
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
use Pecee\Http\Input\Input;
|
||||
@@ -8,7 +9,7 @@ use Pecee\SimpleRouter\SimpleRouter;
|
||||
|
||||
class Request
|
||||
{
|
||||
protected $data = [];
|
||||
private $data = [];
|
||||
protected $headers;
|
||||
protected $host;
|
||||
protected $uri;
|
||||
@@ -29,8 +30,11 @@ class Request
|
||||
public function __construct()
|
||||
{
|
||||
$this->parseHeaders();
|
||||
$this->host = $this->getHeader('http-host');
|
||||
$this->uri = $this->getHeader('request-uri');
|
||||
$this->setHost($this->getHeader('http-host'));
|
||||
|
||||
// Check if special IIS header exist, otherwise use default.
|
||||
$this->setUri(new Uri($this->getHeader('unencoded-url', $this->getHeader('request-uri'))));
|
||||
|
||||
$this->input = new Input($this);
|
||||
$this->method = strtolower($this->input->get('_method', $this->getHeader('request-method'), 'post'));
|
||||
}
|
||||
@@ -39,13 +43,7 @@ class Request
|
||||
{
|
||||
$this->headers = [];
|
||||
|
||||
$max = count($_SERVER) - 1;
|
||||
$keys = array_keys($_SERVER);
|
||||
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
$key = $keys[$i];
|
||||
$value = $_SERVER[$key];
|
||||
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
$this->headers[strtolower($key)] = $value;
|
||||
$this->headers[strtolower(str_replace('_', '-', $key))] = $value;
|
||||
}
|
||||
@@ -58,7 +56,7 @@ class Request
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return Uri
|
||||
*/
|
||||
public function getUri()
|
||||
{
|
||||
@@ -164,24 +162,7 @@ class Request
|
||||
*/
|
||||
public function getHeader($name, $defaultValue = null)
|
||||
{
|
||||
if (isset($this->headers[strtolower($name)])) {
|
||||
return $this->headers[strtolower($name)];
|
||||
}
|
||||
|
||||
$max = count($_SERVER) - 1;
|
||||
$keys = array_keys($_SERVER);
|
||||
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
|
||||
$key = $keys[$i];
|
||||
$name = $_SERVER[$key];
|
||||
|
||||
if ($key === $name) {
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $defaultValue;
|
||||
return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,10 +196,14 @@ class Request
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @param Uri|string $uri
|
||||
*/
|
||||
public function setUri($uri)
|
||||
{
|
||||
if (is_string($uri) === true) {
|
||||
$uri = new Uri($uri);
|
||||
}
|
||||
|
||||
$this->uri = $uri;
|
||||
}
|
||||
|
||||
@@ -246,26 +231,7 @@ class Request
|
||||
*/
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
$this->rewriteRoute = SimpleRouter::addDefaultNamespace($route);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
class Response
|
||||
@@ -41,7 +42,7 @@ class Response
|
||||
|
||||
public function refresh()
|
||||
{
|
||||
$this->redirect($this->request->getUri());
|
||||
$this->redirect($this->request->getUri()->getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,14 +83,21 @@ class Response
|
||||
}
|
||||
|
||||
/**
|
||||
* Json encode array
|
||||
* @param array $value
|
||||
* Json encode
|
||||
* @param array|\JsonSerializable $value
|
||||
* @param int $options JSON options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT, JSON_PRESERVE_ZERO_FRACTION, JSON_UNESCAPED_UNICODE, JSON_PARTIAL_OUTPUT_ON_ERROR.
|
||||
* @param int $dept JSON debt.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function json(array $value)
|
||||
public function json($value, $options = null, $dept = 512)
|
||||
{
|
||||
$this->header('Content-Type: application/json');
|
||||
echo json_encode($value);
|
||||
die();
|
||||
if (($value instanceof \JsonSerializable) === false && is_array($value) === false) {
|
||||
throw new \InvalidArgumentException('Invalid type for parameter "value". Must be of type array or object implementing the \JsonSerializable interface.');
|
||||
}
|
||||
|
||||
$this->header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode($value, $options, $dept);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Security;
|
||||
|
||||
class CookieTokenProvider implements ITokenProvider
|
||||
{
|
||||
const CSRF_KEY = 'CSRF-TOKEN';
|
||||
|
||||
protected $token;
|
||||
protected $cookieTimeoutMinutes = 120;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->token = $this->getToken();
|
||||
|
||||
if ($this->token === null) {
|
||||
$this->token = $this->generateToken();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random identifier for CSRF token
|
||||
*
|
||||
* @throws \RuntimeException|\Exception
|
||||
* @return string
|
||||
*/
|
||||
public function generateToken()
|
||||
{
|
||||
if (function_exists('random_bytes') === true) {
|
||||
return bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
$isSourceStrong = false;
|
||||
|
||||
$random = openssl_random_pseudo_bytes(32, $isSourceStrong);
|
||||
if ($isSourceStrong === false || $random === false) {
|
||||
throw new \RuntimeException('IV generation failed');
|
||||
}
|
||||
|
||||
return $random;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate valid CSRF token
|
||||
*
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function validate($token)
|
||||
{
|
||||
if ($token !== null && $this->getToken() !== null) {
|
||||
return hash_equals($token, $this->getToken());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set csrf token cookie
|
||||
* Overwrite this method to save the token to another storage like session etc.
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
public function setToken($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
setcookie(static::CSRF_KEY, $token, time() + 60 * $this->cookieTimeoutMinutes, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get csrf token
|
||||
* @param string|null $defaultValue
|
||||
* @return string|null
|
||||
*/
|
||||
public function getToken($defaultValue = null)
|
||||
{
|
||||
$this->token = ($this->hasToken() === true) ? $_COOKIE[static::CSRF_KEY] : null;
|
||||
|
||||
return ($this->token !== null) ? $this->token : $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh existing token
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
if ($this->token !== null) {
|
||||
$this->setToken($this->token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the csrf token has been defined
|
||||
* @return bool
|
||||
*/
|
||||
public function hasToken()
|
||||
{
|
||||
return isset($_COOKIE[static::CSRF_KEY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timeout for cookie in minutes
|
||||
* @return int
|
||||
*/
|
||||
public function getCookieTimeoutMinutes()
|
||||
{
|
||||
return $this->cookieTimeoutMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cookie timeout in minutes
|
||||
* @param $minutes
|
||||
*/
|
||||
public function setCookieTimeoutMinutes($minutes)
|
||||
{
|
||||
$this->cookieTimeoutMinutes = $minutes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Security;
|
||||
|
||||
interface ITokenProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Refresh existing token
|
||||
*/
|
||||
public function refresh();
|
||||
|
||||
/**
|
||||
* Validate valid CSRF token
|
||||
*
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function validate($token);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
class Uri
|
||||
{
|
||||
private $originalUrl;
|
||||
private $data = [
|
||||
'scheme' => null,
|
||||
'host' => null,
|
||||
'port' => null,
|
||||
'user' => null,
|
||||
'pass' => null,
|
||||
'path' => null,
|
||||
'query' => null,
|
||||
'fragment' => null,
|
||||
];
|
||||
|
||||
public function __construct($url)
|
||||
{
|
||||
$this->originalUrl = $url;
|
||||
$this->data = $this->parseUrl($url) + $this->data;
|
||||
|
||||
if (isset($this->data['path']) === true && $this->data['path'] !== '/') {
|
||||
$this->data['path'] = rtrim($this->data['path'], '/') . '/';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if url is using a secure protocol like https
|
||||
* @return bool
|
||||
*/
|
||||
public function isSecure()
|
||||
{
|
||||
return (strtolower($this->getScheme()) === 'https');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if url is relative
|
||||
* @return bool
|
||||
*/
|
||||
public function isRelative()
|
||||
{
|
||||
return ($this->getHost() === null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url scheme
|
||||
* @return string|null
|
||||
*/
|
||||
public function getScheme()
|
||||
{
|
||||
return $this->data['scheme'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url host
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->data['host'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url port
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPort()
|
||||
{
|
||||
return ($this->data['port'] !== null) ? (int)$this->data['port'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse username from url
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUserName()
|
||||
{
|
||||
return $this->data['user'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse password from url
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->data['pass'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path from url
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->data['path'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get querystring from url
|
||||
* @return string|null
|
||||
*/
|
||||
public function getQueryString()
|
||||
{
|
||||
return $this->data['query'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fragment from url (everything after #)
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFragment()
|
||||
{
|
||||
return $this->data['fragment'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalUrl()
|
||||
{
|
||||
return $this->originalUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8 aware parse_url() replacement.
|
||||
* @param string $url
|
||||
* @param int $component
|
||||
* @throws \InvalidArgumentException
|
||||
* @return array
|
||||
*/
|
||||
public function parseUrl($url, $component = -1)
|
||||
{
|
||||
$encodedUrl = preg_replace_callback(
|
||||
'/[^:\/@?&=#]+/u',
|
||||
function ($matches) {
|
||||
return urlencode($matches[0]);
|
||||
},
|
||||
$url
|
||||
);
|
||||
|
||||
$parts = parse_url($encodedUrl, $component);
|
||||
|
||||
if ($parts === false) {
|
||||
throw new \InvalidArgumentException('Malformed URL: ' . $url);
|
||||
}
|
||||
|
||||
return array_map('urldecode', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data array with information about the url
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getOriginalUrl();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
interface IPartialGroupRoute
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
@@ -20,7 +21,7 @@ interface IRoute
|
||||
*
|
||||
* @param Request $request
|
||||
* @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException
|
||||
* @return void
|
||||
* @return string
|
||||
*/
|
||||
public function renderRoute(Request $request);
|
||||
|
||||
@@ -173,7 +174,7 @@ interface IRoute
|
||||
* @param string $middleware
|
||||
* @return static
|
||||
*/
|
||||
public function setMiddleware($middleware);
|
||||
public function addMiddleware($middleware);
|
||||
|
||||
/**
|
||||
* Set middlewares array
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
@@ -29,20 +30,23 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
{
|
||||
$max = count($this->getMiddlewares());
|
||||
|
||||
if ($max > 0) {
|
||||
if ($max === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
|
||||
$middleware = $this->getMiddlewares()[$i];
|
||||
$middleware = $this->getMiddlewares()[$i];
|
||||
|
||||
if (is_object($middleware) === false) {
|
||||
$middleware = $this->loadClass($middleware);
|
||||
|
||||
if (($middleware instanceof IMiddleware) === false) {
|
||||
throw new HttpException($middleware . ' must be instance of Middleware');
|
||||
}
|
||||
|
||||
$middleware->handle($request);
|
||||
}
|
||||
|
||||
if (($middleware instanceof IMiddleware) === false) {
|
||||
throw new HttpException($middleware . ' must be inherit the IMiddleware interface');
|
||||
}
|
||||
|
||||
$middleware->handle($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,17 +58,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
return null;
|
||||
}
|
||||
|
||||
$parameters = [];
|
||||
|
||||
if (preg_match($this->regex, $request->getHost() . $url, $parameters) > 0) {
|
||||
|
||||
/* Remove global match */
|
||||
$this->parameters = array_slice($parameters, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return (preg_match($this->regex, $request->getHost() . $url) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,9 +73,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 . '/', $this->url, $matches)) {
|
||||
if (preg_match_all('/' . $regex . '/u', $this->url, $matches) > 0) {
|
||||
$this->parameters = array_fill_keys($matches[1], null);
|
||||
}
|
||||
}
|
||||
@@ -99,7 +93,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* Used when calling the url() helper.
|
||||
*
|
||||
* @param string|null $method
|
||||
* @param array|null $parameters
|
||||
* @param string|array|null $parameters
|
||||
* @param string|null $name
|
||||
* @return string
|
||||
*/
|
||||
@@ -107,8 +101,10 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
{
|
||||
$url = $this->getUrl();
|
||||
|
||||
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
|
||||
$url = '//' . $this->getGroup()->getDomains()[0] . $url;
|
||||
$group = $this->getGroup();
|
||||
|
||||
if ($group !== null && count($group->getDomains()) !== 0) {
|
||||
$url = '//' . $group->getDomains()[0] . $url;
|
||||
}
|
||||
|
||||
/* Contains parameters that aren't recognized and will be appended at the end of the url */
|
||||
@@ -128,11 +124,17 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
$param = $keys[$i];
|
||||
$value = $value = ($parameters !== null && array_key_exists($param, $parameters)) ? $parameters[$param] : $params[$param];
|
||||
|
||||
/* If parameter is specifically set to null - use the original-defined value */
|
||||
if ($value === null && isset($this->originalParameters[$param])) {
|
||||
$value = $this->originalParameters[$param];
|
||||
if ($parameters === '' || (is_array($parameters) === true && count($parameters) === 0)) {
|
||||
$value = '';
|
||||
} else {
|
||||
$p = (array)$parameters;
|
||||
$value = array_key_exists($param, $p) ? $p[$param] : $params[$param];
|
||||
|
||||
/* 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) {
|
||||
@@ -143,7 +145,8 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
}
|
||||
}
|
||||
|
||||
$url .= join('/', $unknownParams);
|
||||
$url = '/' . ltrim($url, '/');
|
||||
$url .= implode('/', $unknownParams);
|
||||
|
||||
return rtrim($url, '/') . '/';
|
||||
}
|
||||
@@ -227,15 +230,18 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
{
|
||||
if (isset($values['as'])) {
|
||||
if (isset($values['as']) === true) {
|
||||
|
||||
$name = $values['as'];
|
||||
|
||||
if ($this->name !== null && $merge !== false) {
|
||||
$this->setName($values['as'] . '.' . $this->name);
|
||||
} else {
|
||||
$this->setName($values['as']);
|
||||
$name .= '.' . $this->name;
|
||||
}
|
||||
|
||||
$this->setName($name);
|
||||
}
|
||||
|
||||
if (isset($values['prefix'])) {
|
||||
if (isset($values['prefix']) === true) {
|
||||
$this->setUrl($values['prefix'] . $this->getUrl());
|
||||
}
|
||||
|
||||
|
||||
@@ -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,8 +34,15 @@ abstract class Route implements IRoute
|
||||
* @var bool
|
||||
*/
|
||||
protected $filterEmptyParams = false;
|
||||
|
||||
/**
|
||||
* Default regular expression used for parsing parameters.
|
||||
* @var string|null
|
||||
*/
|
||||
protected $defaultParameterRegex;
|
||||
protected $paramModifiers = '{}';
|
||||
protected $paramOptionalSymbol = '?';
|
||||
protected $urlRegex = '/^%s\/?$/u';
|
||||
protected $group;
|
||||
protected $parent;
|
||||
protected $callback;
|
||||
@@ -46,63 +56,83 @@ abstract class Route implements IRoute
|
||||
protected $originalParameters = [];
|
||||
protected $middlewares = [];
|
||||
|
||||
/**
|
||||
* Load class by name
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render route
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string|mixed
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function renderRoute(Request $request)
|
||||
{
|
||||
$callback = $this->getCallback();
|
||||
|
||||
if ($callback !== null && is_callable($callback)) {
|
||||
if ($callback === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Render callback function */
|
||||
if (is_callable($callback) === true) {
|
||||
|
||||
/* When the callback is a function */
|
||||
call_user_func_array($callback, $this->getParameters());
|
||||
return call_user_func_array($callback, $this->getParameters());
|
||||
|
||||
} else {
|
||||
|
||||
/* When the callback is a method */
|
||||
$controller = explode('@', $callback);
|
||||
|
||||
$namespace = $this->getNamespace();
|
||||
|
||||
$className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0];
|
||||
|
||||
$class = $this->loadClass($className);
|
||||
$method = $controller[1];
|
||||
|
||||
if (method_exists($class, $method) === false) {
|
||||
throw new NotFoundHttpException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
|
||||
}
|
||||
|
||||
$parameters = $this->getParameters();
|
||||
|
||||
/* Filter parameters with null-value */
|
||||
|
||||
if ($this->filterEmptyParams === true) {
|
||||
$parameters = array_filter($parameters, function ($var) {
|
||||
return ($var !== null);
|
||||
});
|
||||
}
|
||||
|
||||
call_user_func_array([$class, $method], $parameters);
|
||||
}
|
||||
|
||||
/* When the callback is a class + method */
|
||||
$controller = explode('@', $callback);
|
||||
|
||||
$namespace = $this->getNamespace();
|
||||
|
||||
$className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0];
|
||||
|
||||
$class = $this->loadClass($className);
|
||||
$method = $controller[1];
|
||||
|
||||
if (method_exists($class, $method) === false) {
|
||||
throw new NotFoundHttpException(sprintf('Method "%s" does not exist in class "%s"', $method, $className), 404);
|
||||
}
|
||||
|
||||
$parameters = $this->getParameters();
|
||||
|
||||
/* Filter parameters with null-value */
|
||||
|
||||
if ($this->filterEmptyParams === true) {
|
||||
$parameters = array_filter($parameters, function ($var) {
|
||||
return ($var !== null);
|
||||
});
|
||||
}
|
||||
|
||||
return call_user_func_array([$class, $method], $parameters);
|
||||
}
|
||||
|
||||
protected function parseParameters($route, $url, $parameterRegex = '[\w]+')
|
||||
protected function parseParameters($route, $url, $parameterRegex = null)
|
||||
{
|
||||
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
|
||||
$regex = sprintf(static::PARAMETERS_REGEX_FORMAT, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
|
||||
|
||||
$parameters = [];
|
||||
|
||||
if (preg_match_all('/' . $regex . '/', $route, $parameters)) {
|
||||
// Ensures that hostnames/domains will work with parameters
|
||||
$url = '/' . ltrim($url, '/');
|
||||
|
||||
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', rtrim($route, '/'));
|
||||
if (preg_match_all('/' . $regex . '/u', $route, $parameters) > 0) {
|
||||
|
||||
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', $route);
|
||||
|
||||
foreach ($urlParts as $key => $t) {
|
||||
|
||||
@@ -111,28 +141,41 @@ abstract class Route implements IRoute
|
||||
if ($key < count($parameters[1])) {
|
||||
|
||||
$name = $parameters[1][$key];
|
||||
$regex = isset($this->where[$name]) ? $this->where[$name] : $parameterRegex;
|
||||
$regex = sprintf('\-?\/?(?P<%s>%s)', $name, $regex) . $parameters[2][$key];
|
||||
|
||||
/* If custom regex is defined, use that */
|
||||
if (isset($this->where[$name]) === true) {
|
||||
$regex = $this->where[$name];
|
||||
} else {
|
||||
|
||||
/* If method specific regex is defined use that, otherwise use the default parameter regex */
|
||||
if ($parameterRegex !== null) {
|
||||
$regex = $parameterRegex;
|
||||
} else {
|
||||
$regex = ($this->defaultParameterRegex === null) ? static::PARAMETERS_DEFAULT_REGEX : $this->defaultParameterRegex;
|
||||
}
|
||||
}
|
||||
|
||||
$regex = sprintf('(?:\/|\-)%1$s(?P<%2$s>%3$s)%1$s', $parameters[2][$key], $name, $regex);
|
||||
|
||||
}
|
||||
|
||||
$urlParts[$key] = preg_quote($t, '/') . $regex;
|
||||
}
|
||||
|
||||
$urlRegex = join('', $urlParts);
|
||||
$urlRegex = implode('', $urlParts);
|
||||
|
||||
} else {
|
||||
$urlRegex = preg_quote($route, '/');
|
||||
}
|
||||
|
||||
if (preg_match('/^' . $urlRegex . '(\/?)$/', $url, $matches) > 0) {
|
||||
if (preg_match(sprintf($this->urlRegex, $urlRegex), $url, $matches) > 0) {
|
||||
|
||||
$values = [];
|
||||
|
||||
if (isset($parameters[1])) {
|
||||
if (isset($parameters[1]) === true) {
|
||||
|
||||
/* Only take matched parameters with name */
|
||||
foreach ($parameters[1] as $name) {
|
||||
foreach ((array)$parameters[1] as $name) {
|
||||
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
|
||||
}
|
||||
}
|
||||
@@ -152,7 +195,7 @@ abstract class Route implements IRoute
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
if (strpos($this->callback, '@') !== false) {
|
||||
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
@@ -249,7 +292,7 @@ abstract class Route implements IRoute
|
||||
|
||||
public function getMethod()
|
||||
{
|
||||
if (strpos($this->callback, '@') !== false) {
|
||||
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
|
||||
$tmp = explode('@', $this->callback);
|
||||
|
||||
return $tmp[1];
|
||||
@@ -260,7 +303,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];
|
||||
@@ -331,18 +374,22 @@ abstract class Route implements IRoute
|
||||
$values['namespace'] = $this->namespace;
|
||||
}
|
||||
|
||||
if (count($this->requestMethods) > 0) {
|
||||
if (count($this->requestMethods) !== 0) {
|
||||
$values['method'] = $this->requestMethods;
|
||||
}
|
||||
|
||||
if (count($this->where) > 0) {
|
||||
if (count($this->where) !== 0) {
|
||||
$values['where'] = $this->where;
|
||||
}
|
||||
|
||||
if (count($this->middlewares) > 0) {
|
||||
if (count($this->middlewares) !== 0) {
|
||||
$values['middleware'] = $this->middlewares;
|
||||
}
|
||||
|
||||
if ($this->defaultParameterRegex !== null) {
|
||||
$values['defaultParameterRegex'] = $this->defaultParameterRegex;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
@@ -355,27 +402,31 @@ abstract class Route implements IRoute
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
{
|
||||
if ($this->namespace === null && isset($values['namespace'])) {
|
||||
if ($this->namespace === null && isset($values['namespace']) === true) {
|
||||
$this->setNamespace($values['namespace']);
|
||||
}
|
||||
|
||||
if (isset($values['method'])) {
|
||||
if (isset($values['method']) === true) {
|
||||
$this->setRequestMethods(array_merge($this->requestMethods, (array)$values['method']));
|
||||
}
|
||||
|
||||
if (isset($values['where'])) {
|
||||
if (isset($values['where']) === true) {
|
||||
$this->setWhere(array_merge($this->where, (array)$values['where']));
|
||||
}
|
||||
|
||||
if (isset($values['parameters'])) {
|
||||
if (isset($values['parameters']) === true) {
|
||||
$this->setParameters(array_merge($this->parameters, (array)$values['parameters']));
|
||||
}
|
||||
|
||||
// Push middleware if multiple
|
||||
if (isset($values['middleware'])) {
|
||||
if (isset($values['middleware']) === true) {
|
||||
$this->setMiddlewares(array_merge((array)$values['middleware'], $this->middlewares));
|
||||
}
|
||||
|
||||
if (isset($values['defaultParameterRegex']) === true) {
|
||||
$this->setDefaultParameterRegex($values['defaultParameterRegex']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -425,7 +476,7 @@ abstract class Route implements IRoute
|
||||
/* Sort the parameters after the user-defined param order, if any */
|
||||
$parameters = [];
|
||||
|
||||
if (count($this->originalParameters) > 0) {
|
||||
if (count($this->originalParameters) !== 0) {
|
||||
$parameters = $this->originalParameters;
|
||||
}
|
||||
|
||||
@@ -444,7 +495,7 @@ abstract class Route implements IRoute
|
||||
* If this is the first time setting parameters we store them so we
|
||||
* later can organize the array, in case somebody tried to sort the array.
|
||||
*/
|
||||
if (count($parameters) > 0 && count($this->originalParameters) === 0) {
|
||||
if (count($parameters) !== 0 && count($this->originalParameters) === 0) {
|
||||
$this->originalParameters = $parameters;
|
||||
}
|
||||
|
||||
@@ -454,9 +505,10 @@ abstract class Route implements IRoute
|
||||
}
|
||||
|
||||
/**
|
||||
* Set middleware class-name
|
||||
* Add middleware class-name
|
||||
*
|
||||
* @param string $middleware
|
||||
* @deprecated This method is deprecated and will be removed in the near future.
|
||||
* @param IMiddleware|string $middleware
|
||||
* @return static
|
||||
*/
|
||||
public function setMiddleware($middleware)
|
||||
@@ -466,6 +518,19 @@ abstract class Route implements IRoute
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add middleware class-name
|
||||
*
|
||||
* @param IMiddleware|string $middleware
|
||||
* @return static
|
||||
*/
|
||||
public function addMiddleware($middleware)
|
||||
{
|
||||
$this->middlewares[] = $middleware;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set middlewares array
|
||||
*
|
||||
@@ -487,4 +552,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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
@@ -53,7 +54,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 +67,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,20 +75,19 @@ 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);
|
||||
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower($method) . implode('/', $parameters);
|
||||
|
||||
return '/' . trim($url, '/') . '/';
|
||||
}
|
||||
|
||||
public function matchRoute($url, Request $request)
|
||||
{
|
||||
$url = parse_url(urldecode($url), PHP_URL_PATH);
|
||||
$url = rtrim($url, '/') . '/';
|
||||
|
||||
/* Match global regular-expression for route */
|
||||
$regexMatch = $this->matchRegex($request, $url);
|
||||
|
||||
@@ -98,7 +98,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
|
||||
$path = explode('/', $strippedUrl);
|
||||
|
||||
if (count($path) > 0) {
|
||||
if (count($path) !== 0) {
|
||||
|
||||
$method = (isset($path[0]) === false || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
|
||||
$this->method = $request->getMethod() . ucfirst($method);
|
||||
@@ -169,7 +169,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
{
|
||||
if (isset($values['names'])) {
|
||||
if (isset($values['names']) === true) {
|
||||
$this->names = $values['names'];
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -26,7 +28,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
|
||||
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
|
||||
|
||||
if ($parameters !== null && count($parameters) > 0) {
|
||||
if ($parameters !== null && count($parameters) !== 0) {
|
||||
|
||||
$this->parameters = $parameters;
|
||||
|
||||
@@ -54,6 +56,19 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
return $this->matchDomain($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add exception handler
|
||||
*
|
||||
* @param IExceptionHandler|string $handler
|
||||
* @return static $this
|
||||
*/
|
||||
public function addExceptionHandler($handler)
|
||||
{
|
||||
$this->exceptionHandlers[] = $handler;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set exception-handlers for group
|
||||
*
|
||||
@@ -131,24 +146,27 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
public function setSettings(array $values, $merge = false)
|
||||
{
|
||||
|
||||
if (isset($values['prefix'])) {
|
||||
if (isset($values['prefix']) === true) {
|
||||
$this->setPrefix($values['prefix'] . $this->prefix);
|
||||
}
|
||||
|
||||
if (isset($values['exceptionHandler'])) {
|
||||
if ($merge === false && isset($values['exceptionHandler']) === true) {
|
||||
$this->setExceptionHandlers((array)$values['exceptionHandler']);
|
||||
}
|
||||
|
||||
if (isset($values['domain'])) {
|
||||
if ($merge === false && isset($values['domain']) === true) {
|
||||
$this->setDomains((array)$values['domain']);
|
||||
}
|
||||
|
||||
if (isset($values['as'])) {
|
||||
if (isset($values['as']) === true) {
|
||||
|
||||
$name = $values['as'];
|
||||
|
||||
if ($this->name !== null && $merge !== false) {
|
||||
$this->name = $values['as'] . '.' . $this->name;
|
||||
} else {
|
||||
$this->name = $values['as'];
|
||||
$name .= '.' . $this->name;
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
parent::setSettings($values, $merge);
|
||||
@@ -173,7 +191,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
$values['as'] = $this->name;
|
||||
}
|
||||
|
||||
if (count($this->parameters) > 0) {
|
||||
if (count($this->parameters) !== 0) {
|
||||
$values['parameters'] = $this->parameters;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class RoutePartialGroup extends RouteGroup implements IPartialGroupRoute
|
||||
{
|
||||
protected $urlRegex = '/^%s\/?/u';
|
||||
|
||||
/**
|
||||
* Method called to check if route matches
|
||||
*
|
||||
* @param string $url
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function matchRoute($url, Request $request)
|
||||
{
|
||||
if ($this->prefix !== null) {
|
||||
/* Parse parameters from current route */
|
||||
$parameters = $this->parseParameters($this->prefix, $url);
|
||||
|
||||
/* If no custom regular expression or parameters was found on this route, we stop */
|
||||
if ($parameters === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the parameters */
|
||||
$this->setParameters((array)$parameters);
|
||||
}
|
||||
|
||||
return $this->matchDomain($request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
@@ -78,9 +79,6 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
|
||||
public function matchRoute($url, Request $request)
|
||||
{
|
||||
$url = parse_url(urldecode($url), PHP_URL_PATH);
|
||||
$url = rtrim($url, '/') . '/';
|
||||
|
||||
/* Match global regular-expression for route */
|
||||
$regexMatch = $this->matchRegex($request, $url);
|
||||
|
||||
@@ -90,8 +88,11 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
|
||||
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
|
||||
|
||||
/* Parse parameters from current route */
|
||||
$this->parameters = $this->parseParameters($route, $url);
|
||||
if ($this->parameters === null) {
|
||||
|
||||
/* If no custom regular expression or parameters was found on this route, we stop */
|
||||
if ($regexMatch === null && $this->parameters === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -205,11 +206,11 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
{
|
||||
if (isset($values['names'])) {
|
||||
if (isset($values['names']) === true) {
|
||||
$this->names = $values['names'];
|
||||
}
|
||||
|
||||
if (isset($values['methods'])) {
|
||||
if (isset($values['methods']) === true) {
|
||||
$this->methodNames = $values['methods'];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
@@ -13,22 +14,23 @@ class RouteUrl extends LoadableRoute
|
||||
|
||||
public function matchRoute($url, Request $request)
|
||||
{
|
||||
$url = parse_url(urldecode($url), PHP_URL_PATH);
|
||||
$url = rtrim($url, '/') . '/';
|
||||
|
||||
/* Match global regular-expression for route */
|
||||
$regexMatch = $this->matchRegex($request, $url);
|
||||
|
||||
if ($regexMatch === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Make regular expression based on route */
|
||||
/* Parse parameters from current route */
|
||||
$parameters = $this->parseParameters($this->url, $url);
|
||||
if ($parameters === null) {
|
||||
|
||||
/* If no custom regular expression or parameters was found on this route, we stop */
|
||||
if ($regexMatch === null && $parameters === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->setParameters($parameters);
|
||||
/* Set the parameters */
|
||||
$this->setParameters((array)$parameters);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Handlers\IExceptionHandler;
|
||||
@@ -9,6 +10,7 @@ use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
|
||||
use Pecee\SimpleRouter\Route\IControllerRoute;
|
||||
use Pecee\SimpleRouter\Route\IGroupRoute;
|
||||
use Pecee\SimpleRouter\Route\ILoadableRoute;
|
||||
use Pecee\SimpleRouter\Route\IPartialGroupRoute;
|
||||
use Pecee\SimpleRouter\Route\IRoute;
|
||||
|
||||
class Router
|
||||
@@ -121,41 +123,12 @@ class Router
|
||||
|
||||
$exceptionHandlers = [];
|
||||
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
|
||||
|
||||
/* @var $route IRoute */
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
|
||||
$route = $routes[$i];
|
||||
|
||||
/* @var $route IGroupRoute */
|
||||
if ($route instanceof IGroupRoute) {
|
||||
|
||||
$group = $route;
|
||||
|
||||
if ($route->getCallback() !== null && is_callable($route->getCallback())) {
|
||||
|
||||
$this->processingRoute = true;
|
||||
$route->renderRoute($this->request);
|
||||
$this->processingRoute = false;
|
||||
|
||||
if ($route->matchRoute($url, $this->request) === true) {
|
||||
|
||||
/* Add exception handlers */
|
||||
if (count($route->getExceptionHandlers()) > 0) {
|
||||
$exceptionHandlers += $route->getExceptionHandlers();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($group !== null) {
|
||||
|
||||
/* Add the parent group */
|
||||
$route->setGroup($group);
|
||||
}
|
||||
|
||||
if ($parent !== null) {
|
||||
|
||||
/* Add the parent route */
|
||||
@@ -166,13 +139,48 @@ class Router
|
||||
|
||||
}
|
||||
|
||||
if ($group !== null) {
|
||||
|
||||
/* Add the parent group */
|
||||
$route->setGroup($group);
|
||||
}
|
||||
|
||||
/* @var $route IGroupRoute */
|
||||
if ($route instanceof IGroupRoute) {
|
||||
|
||||
$group = $route;
|
||||
|
||||
if ($route->matchRoute($url, $this->request) === true) {
|
||||
|
||||
/* Add exception handlers */
|
||||
if (count($route->getExceptionHandlers()) !== 0) {
|
||||
/** @noinspection AdditionOperationOnArraysInspection */
|
||||
$exceptionHandlers += $route->getExceptionHandlers();
|
||||
}
|
||||
|
||||
/* Only render partial group if it matches */
|
||||
if ($route instanceof IPartialGroupRoute) {
|
||||
$this->processingRoute = true;
|
||||
$route->renderRoute($this->request);
|
||||
$this->processingRoute = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (($route instanceof IPartialGroupRoute) === false) {
|
||||
$this->processingRoute = true;
|
||||
$route->renderRoute($this->request);
|
||||
$this->processingRoute = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($route instanceof ILoadableRoute) {
|
||||
|
||||
/* Add the route to the map, so we can find the active one when all routes has been loaded */
|
||||
$this->processedRoutes[] = $route;
|
||||
}
|
||||
|
||||
if (count($this->routeStack) > 0) {
|
||||
if (count($this->routeStack) !== 0) {
|
||||
|
||||
/* Pop and grab the routes added when executing group callback earlier */
|
||||
$stack = $this->routeStack;
|
||||
@@ -183,7 +191,7 @@ class Router
|
||||
}
|
||||
}
|
||||
|
||||
$this->exceptionHandlers = array_unique(array_merge($exceptionHandlers, $this->exceptionHandlers));
|
||||
$this->exceptionHandlers = array_merge($exceptionHandlers, $this->exceptionHandlers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +202,7 @@ class Router
|
||||
public function loadRoutes()
|
||||
{
|
||||
/* Initialize boot-managers */
|
||||
if (count($this->bootManagers) > 0) {
|
||||
if (count($this->bootManagers) !== 0) {
|
||||
|
||||
$max = count($this->bootManagers) - 1;
|
||||
|
||||
@@ -209,6 +217,14 @@ class Router
|
||||
$this->processRoutes($this->routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes the request
|
||||
*
|
||||
* @param bool $rewrite
|
||||
* @return string|mixed
|
||||
* @throws HttpException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function routeRequest($rewrite = false)
|
||||
{
|
||||
$routeNotAllowed = false;
|
||||
@@ -225,7 +241,7 @@ class Router
|
||||
}
|
||||
}
|
||||
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
|
||||
|
||||
$max = count($this->processedRoutes) - 1;
|
||||
|
||||
@@ -238,7 +254,7 @@ class Router
|
||||
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) {
|
||||
if (count($route->getRequestMethods()) !== 0 && in_array($this->request->getMethod(), $route->getRequestMethods(), false) === false) {
|
||||
$routeNotAllowed = true;
|
||||
continue;
|
||||
}
|
||||
@@ -249,9 +265,8 @@ class Router
|
||||
|
||||
if ($rewriteRoute !== null) {
|
||||
$rewriteRoute->loadMiddleware($this->request);
|
||||
$rewriteRoute->renderRoute($this->request);
|
||||
|
||||
return;
|
||||
return $rewriteRoute->renderRoute($this->request);
|
||||
}
|
||||
|
||||
/* If the request has changed */
|
||||
@@ -260,17 +275,15 @@ class Router
|
||||
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
|
||||
unset($this->processedRoutes[$i]);
|
||||
$this->processedRoutes = array_values($this->processedRoutes);
|
||||
$this->routeRequest(true);
|
||||
|
||||
return;
|
||||
return $this->routeRequest(true);
|
||||
}
|
||||
|
||||
/* Render route */
|
||||
$routeNotAllowed = false;
|
||||
$this->request->setLoadedRoute($route);
|
||||
$route->renderRoute($this->request);
|
||||
|
||||
break;
|
||||
return $route->renderRoute($this->request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,51 +292,72 @@ class Router
|
||||
}
|
||||
|
||||
if ($routeNotAllowed === true) {
|
||||
$this->handleException(new HttpException('Route or method not allowed', 403));
|
||||
$message = sprintf('Route "%s" or method "%s" not allowed.', $this->request->getUri()->getPath(), $this->request->getMethod());
|
||||
$this->handleException(new HttpException($message, 403));
|
||||
}
|
||||
|
||||
if ($this->request->getLoadedRoute() === null) {
|
||||
$this->handleException(new NotFoundHttpException('Route not found: ' . $this->request->getUri(), 404));
|
||||
|
||||
$rewriteUrl = $this->request->getRewriteUrl();
|
||||
|
||||
if ($rewriteUrl !== null) {
|
||||
$message = sprintf('Route not found: "%s" (rewrite from: "%s")', $rewriteUrl, $this->request->getUri()->getPath());
|
||||
} else {
|
||||
$message = sprintf('Route not found: "%s"', $this->request->getUri()->getPath());
|
||||
}
|
||||
|
||||
$this->handleException(new NotFoundHttpException($message, 404));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Exception $e
|
||||
* @throws HttpException
|
||||
* @throws \Exception
|
||||
* @return string
|
||||
*/
|
||||
protected function handleException(\Exception $e)
|
||||
{
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
|
||||
|
||||
$max = count($this->exceptionHandlers);
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
|
||||
|
||||
/* @var $handler IExceptionHandler */
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
foreach ($this->exceptionHandlers as $key => $handler) {
|
||||
|
||||
$handler = $this->exceptionHandlers[$i];
|
||||
$handler = new $handler();
|
||||
if (is_object($handler) === false) {
|
||||
$handler = new $handler();
|
||||
}
|
||||
|
||||
if (($handler instanceof IExceptionHandler) === false) {
|
||||
throw new HttpException('Exception handler must implement the IExceptionHandler interface.', 500);
|
||||
}
|
||||
|
||||
if ($handler->handleError($this->request, $e) !== null) {
|
||||
try {
|
||||
|
||||
$rewriteRoute = $this->request->getRewriteRoute();
|
||||
if ($handler->handleError($this->request, $e) !== null) {
|
||||
|
||||
if ($rewriteRoute !== null) {
|
||||
$rewriteRoute->loadMiddleware($this->request);
|
||||
$rewriteRoute->renderRoute($this->request);
|
||||
$rewriteRoute = $this->request->getRewriteRoute();
|
||||
|
||||
return;
|
||||
if ($rewriteRoute !== null) {
|
||||
$rewriteRoute->loadMiddleware($this->request);
|
||||
|
||||
return $rewriteRoute->renderRoute($this->request);
|
||||
}
|
||||
|
||||
$rewriteUrl = $this->request->getRewriteUrl();
|
||||
|
||||
/* If the request has changed */
|
||||
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
|
||||
unset($this->exceptionHandlers[$key]);
|
||||
$this->exceptionHandlers = array_values($this->exceptionHandlers);
|
||||
|
||||
return $this->routeRequest(true);
|
||||
}
|
||||
}
|
||||
|
||||
$rewriteUrl = $this->request->getRewriteUrl();
|
||||
} catch (\Exception $e) {
|
||||
|
||||
/* If the request has changed */
|
||||
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
|
||||
unset($this->exceptionHandlers[$i]);
|
||||
$this->exceptionHandlers = array_values($this->exceptionHandlers);
|
||||
$this->routeRequest(true);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +366,7 @@ class Router
|
||||
|
||||
public function arrayToParams(array $getParams = [], $includeEmpty = true)
|
||||
{
|
||||
if (count($getParams) > 0) {
|
||||
if (count($getParams) !== 0) {
|
||||
|
||||
if ($includeEmpty === false) {
|
||||
$getParams = array_filter($getParams, function ($item) {
|
||||
@@ -372,7 +406,7 @@ class Router
|
||||
}
|
||||
|
||||
/* Using @ is most definitely a controller@method or alias@method */
|
||||
if (strpos($name, '@') !== false) {
|
||||
if (is_string($name) === true && strpos($name, '@') !== false) {
|
||||
list($controller, $method) = array_map('strtolower', explode('@', $name));
|
||||
|
||||
if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) {
|
||||
@@ -381,7 +415,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)) {
|
||||
@@ -422,6 +456,10 @@ class Router
|
||||
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
|
||||
}
|
||||
|
||||
if ($name === '' && $parameters === '') {
|
||||
return '/';
|
||||
}
|
||||
|
||||
/* Only merge $_GET when all parameters are null */
|
||||
if ($name === null && $parameters === null && $getParams === null) {
|
||||
$getParams = $_GET;
|
||||
@@ -431,9 +469,7 @@ class Router
|
||||
|
||||
/* Return current route if no options has been specified */
|
||||
if ($name === null && $parameters === null) {
|
||||
$url = rtrim(parse_url($this->request->getUri(), PHP_URL_PATH), '/');
|
||||
|
||||
return (($url === '') ? '/' : $url . '/') . $this->arrayToParams($getParams);
|
||||
return $this->request->getUri()->getPath() . $this->arrayToParams($getParams);
|
||||
}
|
||||
|
||||
$loadedRoute = $this->request->getLoadedRoute();
|
||||
@@ -451,7 +487,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 */
|
||||
@@ -464,7 +500,7 @@ class Router
|
||||
$route = $this->processedRoutes[$i];
|
||||
|
||||
/* Check if the route contains the name/alias */
|
||||
if ($route->hasName($controller)) {
|
||||
if ($route->hasName($controller) === true) {
|
||||
return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams);
|
||||
}
|
||||
|
||||
@@ -477,7 +513,7 @@ class Router
|
||||
}
|
||||
|
||||
/* No result so we assume that someone is using a hardcoded url and join everything together. */
|
||||
$url = trim(join('/', array_merge((array)$name, (array)$parameters)), '/');
|
||||
$url = trim(implode('/', array_merge((array)$name, (array)$parameters)), '/');
|
||||
|
||||
return (($url === '') ? '/' : '/' . $url . '/') . $this->arrayToParams($getParams);
|
||||
}
|
||||
@@ -517,6 +553,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
|
||||
*
|
||||
@@ -549,4 +598,4 @@ class Router
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
* 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;
|
||||
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
|
||||
use Pecee\SimpleRouter\Route\IRoute;
|
||||
use Pecee\SimpleRouter\Route\RoutePartialGroup;
|
||||
use Pecee\SimpleRouter\Route\RouteController;
|
||||
use Pecee\SimpleRouter\Route\RouteGroup;
|
||||
use Pecee\SimpleRouter\Route\RouteResource;
|
||||
@@ -33,17 +36,20 @@ class SimpleRouter
|
||||
*/
|
||||
protected static $response;
|
||||
|
||||
/**
|
||||
* Router instance
|
||||
* @var Router
|
||||
*/
|
||||
protected static $router;
|
||||
|
||||
/**
|
||||
* Start/route request
|
||||
*
|
||||
* @throws HttpException
|
||||
* @throws NotFoundHttpException
|
||||
* @throws HttpException|NotFoundHttpException|\Exception
|
||||
*/
|
||||
public static function start()
|
||||
{
|
||||
static::router()->routeRequest();
|
||||
echo static::router()->routeRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,14 +171,41 @@ class SimpleRouter
|
||||
*/
|
||||
public static function group(array $settings = [], \Closure $callback)
|
||||
{
|
||||
if (is_callable($callback) === false) {
|
||||
throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported');
|
||||
}
|
||||
|
||||
$group = new RouteGroup();
|
||||
$group->setCallback($callback);
|
||||
$group->setSettings($settings);
|
||||
|
||||
static::router()->addRoute($group);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special group that has the same benefits as group but supports
|
||||
* parameters and which are only rendered when the url matches.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $settings
|
||||
* @param \Closure $callback
|
||||
* @throws \InvalidArgumentException
|
||||
* @return RoutePartialGroup
|
||||
*/
|
||||
public static function partialGroup($url, \Closure $callback, array $settings = [])
|
||||
{
|
||||
if (is_callable($callback) === false) {
|
||||
throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported');
|
||||
}
|
||||
|
||||
$settings['prefix'] = $url;
|
||||
|
||||
$group = new RoutePartialGroup();
|
||||
$group->setSettings($settings);
|
||||
$group->setCallback($callback);
|
||||
|
||||
static::router()->addRoute($group);
|
||||
|
||||
return $group;
|
||||
@@ -214,7 +247,7 @@ class SimpleRouter
|
||||
* @param string $url
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl
|
||||
* @return RouteUrl|IRoute
|
||||
*/
|
||||
public static function match(array $requestMethods, $url, $callback, array $settings = null)
|
||||
{
|
||||
@@ -237,7 +270,7 @@ class SimpleRouter
|
||||
* @param string $url
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl
|
||||
* @return RouteUrl|IRoute
|
||||
*/
|
||||
public static function all($url, $callback, array $settings = null)
|
||||
{
|
||||
@@ -259,7 +292,7 @@ class SimpleRouter
|
||||
* @param string $url
|
||||
* @param string $controller
|
||||
* @param array|null $settings
|
||||
* @return RouteController
|
||||
* @return RouteController|IRoute
|
||||
*/
|
||||
public static function controller($url, $controller, array $settings = null)
|
||||
{
|
||||
@@ -281,7 +314,7 @@ class SimpleRouter
|
||||
* @param string $url
|
||||
* @param string $controller
|
||||
* @param array|null $settings
|
||||
* @return RouteResource
|
||||
* @return RouteResource|IRoute
|
||||
*/
|
||||
public static function resource($url, $controller, array $settings = null)
|
||||
{
|
||||
@@ -297,6 +330,28 @@ class SimpleRouter
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add exception callback handler.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return CallbackExceptionHandler $callbackHandler
|
||||
*/
|
||||
public static function error(\Closure $callback)
|
||||
{
|
||||
$routes = static::router()->getRoutes();
|
||||
|
||||
$callbackHandler = new CallbackExceptionHandler($callback);
|
||||
|
||||
$group = new RouteGroup();
|
||||
$group->addExceptionHandler($callbackHandler);
|
||||
|
||||
array_unshift($routes, $group);
|
||||
|
||||
static::router()->setRoutes($routes);
|
||||
|
||||
return $callbackHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url for a route by using either name/alias, class or method name.
|
||||
*
|
||||
@@ -351,7 +406,7 @@ class SimpleRouter
|
||||
*/
|
||||
public static function router()
|
||||
{
|
||||
if(static::$router === null) {
|
||||
if (static::$router === null) {
|
||||
static::$router = new Router();
|
||||
}
|
||||
|
||||
@@ -364,14 +419,14 @@ class SimpleRouter
|
||||
* @param IRoute $route
|
||||
* @return IRoute
|
||||
*/
|
||||
protected static function addDefaultNamespace(IRoute $route)
|
||||
public static function addDefaultNamespace(IRoute $route)
|
||||
{
|
||||
if (static::$defaultNamespace !== null) {
|
||||
|
||||
$callback = $route->getCallback();
|
||||
|
||||
/* Only add default namespace on relative callbacks */
|
||||
if ($callback === null || $callback[0] !== '\\') {
|
||||
if ($callback === null || (is_string($callback) === true && $callback[0] !== '\\')) {
|
||||
|
||||
$namespace = static::$defaultNamespace;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ class ExceptionHandlerFirst implements \Pecee\Handlers\IExceptionHandler
|
||||
global $stack;
|
||||
$stack[] = static::class;
|
||||
|
||||
$request->setUri('/');
|
||||
$request->setUri(new \Pecee\Http\Uri('/'));
|
||||
return $request;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ class ExceptionHandlerSecond implements \Pecee\Handlers\IExceptionHandler
|
||||
global $stack;
|
||||
$stack[] = static::class;
|
||||
|
||||
$request->setUri('/');
|
||||
$request->setUri(new \Pecee\Http\Uri('/'));
|
||||
return $request;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter
|
||||
|
||||
public static function debugNoReset($testUri, $testMethod = 'get')
|
||||
{
|
||||
static::request()->setUri($testUri);
|
||||
static::request()->setUri(new \Pecee\Http\Uri($testUri));
|
||||
static::request()->setMethod($testMethod);
|
||||
|
||||
static::start();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandler.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
|
||||
class RouterPartialGroupTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testParameters()
|
||||
{
|
||||
$result1 = null;
|
||||
$result2 = null;
|
||||
|
||||
TestRouter::partialGroup('{param1}/{param2}', function ($param1 = null, $param2 = null) use (&$result1, &$result2) {
|
||||
$result1 = $param1;
|
||||
$result2 = $param2;
|
||||
|
||||
TestRouter::get('/', 'DummyController@method1');
|
||||
});
|
||||
|
||||
TestRouter::debug('/param1/param2', 'get');
|
||||
|
||||
$this->assertEquals('param1', $result1);
|
||||
$this->assertEquals('param2', $result2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Exceptions/ResponseException.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
|
||||
@@ -43,7 +44,7 @@ class RouteRewriteTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
try {
|
||||
TestRouter::debug('/my-non-existing-path', 'get');
|
||||
} catch(\ResponseException $e) {
|
||||
} catch (\ResponseException $e) {
|
||||
|
||||
}
|
||||
|
||||
@@ -57,4 +58,21 @@ class RouteRewriteTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
}
|
||||
|
||||
public function testRewriteExceptionMessage()
|
||||
{
|
||||
$this->setExpectedException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class);
|
||||
|
||||
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) {
|
||||
|
||||
if (strtolower($request->getUri()->getPath()) == '/my/test') {
|
||||
$request->setRewriteUrl('/another-non-existing');
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
TestRouter::debug('/my/test', 'get');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -77,10 +77,10 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
public function testPathParamRegex()
|
||||
{
|
||||
TestRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
|
||||
$response = TestRouter::debugOutput('/test/path/123123', 'get');
|
||||
TestRouter::get('/{lang}/productscategories/{name}', 'DummyController@param', ['where' => ['lang' => '[a-z]+', 'name' => '[A-Za-z0-9\-]+']]);
|
||||
$response = TestRouter::debugOutput('/it/productscategories/system', 'get');
|
||||
|
||||
$this->assertEquals('123123', $response);
|
||||
$this->assertEquals('it, system', $response);
|
||||
}
|
||||
|
||||
public function testDomainAllowedRoute()
|
||||
@@ -125,4 +125,23 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
|
||||
TestRouter::debug('/my/custom-path', 'get');
|
||||
}
|
||||
|
||||
public function testDefaultParameterRegex()
|
||||
{
|
||||
TestRouter::get('/my/{path}', 'DummyController@param', ['defaultParameterRegex' => '[\w\-]+']);
|
||||
$output = TestRouter::debugOutput('/my/custom-regex', 'get');
|
||||
|
||||
$this->assertEquals('custom-regex', $output);
|
||||
}
|
||||
|
||||
public function testDefaultParameterRegexGroup()
|
||||
{
|
||||
TestRouter::group(['defaultParameterRegex' => '[\w\-]+'], function() {
|
||||
TestRouter::get('/my/{path}', 'DummyController@param');
|
||||
});
|
||||
|
||||
$output = TestRouter::debugOutput('/my/custom-regex', 'get');
|
||||
|
||||
$this->assertEquals('custom-regex', $output);
|
||||
}
|
||||
|
||||
}
|
||||
+68
-1
@@ -8,7 +8,74 @@ require_once 'Helpers/TestRouter.php';
|
||||
class RouterUrlTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testSimularUrls()
|
||||
public function testIssue253()
|
||||
{
|
||||
TestRouter::get('/', 'DummyController@method1');
|
||||
TestRouter::get('/page/{id?}', 'DummyController@method1');
|
||||
TestRouter::get('/test-output', function() {
|
||||
return 'return value';
|
||||
});
|
||||
|
||||
TestRouter::debugNoReset('/page/22', 'get');
|
||||
$this->assertEquals('/page/{id?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/', 'get');
|
||||
$this->assertEquals('/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
$output = TestRouter::debugOutput('/test-output', 'get');
|
||||
$this->assertEquals('return value', $output);
|
||||
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
|
||||
public function testUnicodeCharacters()
|
||||
{
|
||||
// Test spanish characters
|
||||
TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']);
|
||||
TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'get');
|
||||
$this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
// Test danish characters
|
||||
TestRouter::get('/kategori/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']);
|
||||
TestRouter::debugNoReset('/kategori/økse', 'get');
|
||||
$this->assertEquals('/kategori/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']);
|
||||
TestRouter::debugNoReset('/test/Dermatología');
|
||||
|
||||
$parameters = TestRouter::request()->getLoadedRoute()->getParameters();
|
||||
|
||||
$this->assertEquals('Dermatología', $parameters['param']);
|
||||
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
|
||||
public function testOptionalParameters()
|
||||
{
|
||||
TestRouter::get('/aviso/legal', 'DummyController@method1');
|
||||
TestRouter::get('/aviso/{aviso}', 'DummyController@method1');
|
||||
TestRouter::get('/pagina/{pagina}', 'DummyController@method1');
|
||||
TestRouter::get('/{pagina?}', 'DummyController@method1');
|
||||
|
||||
TestRouter::debugNoReset('/aviso/optional', 'get');
|
||||
$this->assertEquals('/aviso/{aviso}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/pagina/optional', 'get');
|
||||
$this->assertEquals('/pagina/{pagina}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/optional', 'get');
|
||||
$this->assertEquals('/{pagina?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/avisolegal', 'get');
|
||||
$this->assertNotEquals('/aviso/{aviso}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::debugNoReset('/avisolegal', 'get');
|
||||
$this->assertEquals('/{pagina?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
|
||||
public function testSimilarUrls()
|
||||
{
|
||||
// Match normal route on alias
|
||||
TestRouter::resource('/url11', 'DummyController@method1');
|
||||
|
||||
Reference in New Issue
Block a user