mirror of
https://github.com/skipperbent/simple-php-router.git
synced 2026-06-19 09:41:25 +00:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54ae830ec8 | |||
| 085f98cf08 | |||
| f23d569757 | |||
| d279d5598d | |||
| be39010be3 | |||
| 79c82c90cc | |||
| 6d7d07669b | |||
| 98bf95bfc9 | |||
| b051bcf02b | |||
| b8d5106f4e | |||
| cfc9ac138a | |||
| a25be983b8 | |||
| a0c5bbdcc0 | |||
| 50c6499efb | |||
| 55a96a441e | |||
| 6b8c823427 | |||
| 932dfbf2b7 | |||
| 8d87aab35b | |||
| fc2e2e1e82 | |||
| 71518431a9 | |||
| cec240ab0c | |||
| 4d2b584936 | |||
| a102c70700 | |||
| f2d106c649 | |||
| 72d33dd497 | |||
| e23dd37435 | |||
| aa5ec47051 | |||
| 155729074b | |||
| 0be7bfcfd9 | |||
| 7f8d90eef8 | |||
| 4bb784bcec | |||
| c4ee1b9186 | |||
| efd5159604 | |||
| bdfc36ed5c | |||
| d921ae8105 | |||
| af2be14ccb | |||
| fae2e84c98 | |||
| c90c74b88f | |||
| 05f2493304 | |||
| 0856caa9de | |||
| 35dc26d741 | |||
| f2819f866e | |||
| b9aa348b38 | |||
| c29c52ae16 | |||
| 6547c07113 | |||
| fde77969c0 | |||
| c3072e8886 | |||
| 97753f5370 | |||
| 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 | |||
| e3b6899375 | |||
| a179450018 | |||
| ac3e9ed2ac |
@@ -0,0 +1,22 @@
|
||||
engines:
|
||||
phpmd:
|
||||
enabled: true
|
||||
checks:
|
||||
Design/TooManyPublicMethods:
|
||||
enabled: true
|
||||
Naming/ShortVariable:
|
||||
enabled: true
|
||||
CleanCode/StaticAccess:
|
||||
enabled: true
|
||||
Controversial/CamelCaseMethodName:
|
||||
enabled: true
|
||||
fixme:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- php:
|
||||
ratings:
|
||||
paths:
|
||||
- src/**
|
||||
@@ -0,0 +1,13 @@
|
||||
build:
|
||||
tests:
|
||||
override:
|
||||
-
|
||||
command: './vendor/bin/phpunit --coverage-clover=coverage.clover'
|
||||
coverage:
|
||||
file: 'coverage.clover'
|
||||
format: 'clover'
|
||||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
duplication: true
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
sudo: false
|
||||
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.1
|
||||
|
||||
before_script:
|
||||
- curl -sS http://getcomposer.org/installer | php
|
||||
- php composer.phar install --prefer-source --no-interaction
|
||||
|
||||
script:
|
||||
- ./vendor/bin/phpunit
|
||||
@@ -1,23 +1,18 @@
|
||||
# Simple PHP router
|
||||
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expandability in mind.
|
||||
|
||||
**Note: this documentation is currently work-in-progress. Feel free to contribute.**
|
||||
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind.
|
||||
|
||||
### Notes
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
**Please note that this documentation is currently work-in-progress. Feel free to contribute.**
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Getting started](#getting-started)
|
||||
- [Requirements](#requirements)
|
||||
- [Notes](#notes-1)
|
||||
- [Requirements](#requirements)
|
||||
- [Feedback and development](#feedback-and-development)
|
||||
- [Contribution development guidelines](#contribution-development-guidelines)
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Setting up Apache](#setting-up-apache)
|
||||
@@ -25,7 +20,14 @@ If you want a great new feature or experience any issues what-so-ever, please fe
|
||||
- [Setting up IIS](#setting-up-iis)
|
||||
- [Configuration](#configuration)
|
||||
- [Helper functions](#helper-functions)
|
||||
|
||||
- [Help and support](#help-and-support)
|
||||
- [How to debug](#how-to-debug)
|
||||
- [Creating unit-tests](#creating-unit-tests)
|
||||
- [Debug information](#debug-information)
|
||||
- [Benchmark and log-info](#benchmark-and-log-info)
|
||||
- [Reporting a new issue](#reporting-a-new-issue)
|
||||
- [Procedure for reporting a new issue](#procedure-for-reporting-a-new-issue)
|
||||
- [Issue template](#issue-template)
|
||||
- [Routes](#routes)
|
||||
- [Basic routing](#basic-routing)
|
||||
- [Available methods](#available-methods)
|
||||
@@ -43,6 +45,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)
|
||||
@@ -50,6 +53,8 @@ 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)
|
||||
@@ -74,12 +79,10 @@ If you want a great new feature or experience any issues what-so-ever, please fe
|
||||
|
||||
- [Advanced](#advanced)
|
||||
- [Url rewriting](#url-rewriting)
|
||||
- [Rewrite using callback](#rewrite-using-callback)
|
||||
- [Rewrite using url](#rewrite-using-url)
|
||||
- [Changing current route](#changing-current-route)
|
||||
- [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically)
|
||||
- [Adding routes manually](#adding-routes-manually)
|
||||
- [Parameters](#parameters)
|
||||
- [Custom default regex for matching parameters](#custom-default-regex-for-matching-parameters)
|
||||
- [Extending](#extending)
|
||||
|
||||
- [Credits](#credits)
|
||||
@@ -95,12 +98,10 @@ Add the latest version of Simple PHP Router running this command.
|
||||
composer require pecee/simple-router
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP 5.5 or greater
|
||||
|
||||
## Notes
|
||||
|
||||
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.
|
||||
|
||||
We've included a simple demo project for the router which can be found in the `demo-project` folder. This project should give you a basic understanding of how to setup and use simple-php-router project.
|
||||
|
||||
Please note that the demo-project only covers how to integrate the `simple-php-router` in a project without an existing framework. If you are using a framework in your project, the implementation might vary.
|
||||
@@ -119,6 +120,32 @@ You can find the demo-project here: [https://github.com/skipperbent/simple-route
|
||||
- How to get ExceptionHandlers, Middlewares and Controllers working.
|
||||
- How to setup your webservers.
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP 5.5 or greater
|
||||
|
||||
### Feedback and development
|
||||
|
||||
If the library is missing a feature that you need in your project or if you have feedback, we'd love to hear from you.
|
||||
Feel free to leave us feedback by [creating a new issue](https://github.com/skipperbent/simple-php-router/issues/new).
|
||||
|
||||
**Experiencing an issue?**
|
||||
|
||||
Please refer to our [Help and support](#help-and-support) section in the documentation before reporting a new issue.
|
||||
|
||||
##### 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.
|
||||
|
||||
## Features
|
||||
|
||||
- Basic routing (`GET`, `POST`, `PUT`, `PATCH`, `UPDATE`, `DELETE`) with support for custom multiple verbs.
|
||||
@@ -183,7 +210,7 @@ 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
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
@@ -212,6 +239,24 @@ Simply create a new `web.config` file in your projects `public` directory and pa
|
||||
</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.
|
||||
@@ -249,7 +294,8 @@ We recommend that you add these helper functions to your project. These will all
|
||||
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.
|
||||
*
|
||||
@@ -266,26 +312,30 @@ To implement the functions below, simply copy the code to a new file and require
|
||||
* @param string|array|null $parameters
|
||||
* @param array|null $getParams
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function url($name = null, $parameters = null, $getParams = null)
|
||||
{
|
||||
return SimpleRouter::getUrl($name, $parameters, $getParams);
|
||||
return Router::getUrl($name, $parameters, $getParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Pecee\Http\Response
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function response()
|
||||
{
|
||||
return SimpleRouter::response();
|
||||
return Router::response();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Pecee\Http\Request
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function request()
|
||||
{
|
||||
return SimpleRouter::request();
|
||||
return Router::request();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,16 +343,16 @@ function request()
|
||||
* @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
|
||||
* @return \Pecee\Http\Input\InputHandler|string
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function input($index = null, $defaultValue = null, $methods = null)
|
||||
{
|
||||
if($index !== null)
|
||||
{
|
||||
return request()->getInput()->get($index, $defaultValue, $methods);
|
||||
if ($index !== null) {
|
||||
return request()->getInputHandler()->get($index, $defaultValue, $methods);
|
||||
}
|
||||
|
||||
return request()->getInput();
|
||||
return request()->getInputHandler();
|
||||
}
|
||||
|
||||
function redirect($url, $code = null)
|
||||
@@ -313,10 +363,398 @@ function redirect($url, $code = null)
|
||||
|
||||
response()->redirect($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current csrf-token
|
||||
* @return string|null
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function csrf_token()
|
||||
{
|
||||
$baseVerifier = Router::router()->getCsrfVerifier();
|
||||
if ($baseVerifier !== null) {
|
||||
return $baseVerifier->getTokenProvider()->getToken();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Help and support
|
||||
|
||||
This section will go into details on how to debug the router and answer some of the commonly asked questions- and issues.
|
||||
|
||||
## How to debug
|
||||
|
||||
This section will show you how to write unit-tests for the router, view useful debugging information and answer some of the frequently asked questions.
|
||||
|
||||
It will also covers how to report any issue you might encounter.
|
||||
|
||||
### Creating unit-tests
|
||||
|
||||
The easiest and fastest way to debug any issues with the router, is to create a unit-test that represents the issue you are experiencing.
|
||||
|
||||
Unit-tests use a special `TestRouter` class, which simulates a request-method and requested url of a browser.
|
||||
|
||||
The `TestRouter` class can return the output directly or render a route silently.
|
||||
|
||||
```php
|
||||
public function testUnicodeCharacters()
|
||||
{
|
||||
// Add route containing two optional paramters with special spanish characters like "í".
|
||||
TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']);
|
||||
|
||||
// Start the routing and simulate the url "/cursos/listado/especialidad/cirugía local".
|
||||
TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'GET');
|
||||
|
||||
// Verify that the url for the loaded route matches the expected route.
|
||||
$this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
// Start the routing and simulate the url "/test/Dermatología" using "GET" as request-method.
|
||||
TestRouter::debugNoReset('/test/Dermatología', 'GET');
|
||||
|
||||
// Another route containing one parameter with special spanish characters like "í".
|
||||
TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']);
|
||||
|
||||
// Get all parameters parsed by the loaded route.
|
||||
$parameters = TestRouter::request()->getLoadedRoute()->getParameters();
|
||||
|
||||
// Check that the parameter named "param" matches the exspected value.
|
||||
$this->assertEquals('Dermatología', $parameters['param']);
|
||||
|
||||
// Add route testing danish special characters like "ø".
|
||||
TestRouter::get('/category/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']);
|
||||
|
||||
// Start the routing and simulate the url "/kategory/økse" using "GET" as request-method.
|
||||
TestRouter::debugNoReset('/category/økse', 'GET');
|
||||
|
||||
// Validate that the URL of the loaded-route matches the expected url.
|
||||
$this->assertEquals('/category/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
|
||||
|
||||
// Reset the router, so other tests wont inherit settings or the routes we've added.
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
```
|
||||
|
||||
#### Using the TestRouter helper
|
||||
|
||||
Depending on your test, you can use the methods below when rendering routes in your unit-tests.
|
||||
|
||||
|
||||
| Method | Description |
|
||||
| ------------- |-------------|
|
||||
| ```TestRouter::debug($url, $method)``` | Will render the route without returning anything. Exceptions will be thrown and the router will be reset automatically. |
|
||||
| ```TestRouter::debugOutput($url, $method)``` | Will render the route and return any value that the route might output. Manual reset required by calling `TestRouter::router()->reset()`. |
|
||||
| ```TestRouter::debugNoReset($url, $method);``` | Will render the route without resetting the router. Useful if you need to get loaded route, parameters etc. from the router. Manual reset required by calling `TestRouter::router()->reset()`. |
|
||||
|
||||
### Debug information
|
||||
|
||||
The library can output debug-information, which contains information like loaded routes, the parsed request-url etc. It also contains info which are important when reporting a new issue like PHP-version, library version, server-variables, router debug log etc.
|
||||
|
||||
You can activate the debug-information by calling the alternative start-method.
|
||||
|
||||
The example below will start the routing an return array with debugging-information
|
||||
|
||||
**Example:**
|
||||
|
||||
```php
|
||||
$debugInfo = SimpleRouter::startDebug();
|
||||
echo sprintf('<pre>%s</pre>', var_export($debugInfo));
|
||||
exit;
|
||||
```
|
||||
|
||||
**The example above will provide you with an output containing:**
|
||||
|
||||
| Key | Description |
|
||||
| ------------- |------------- |
|
||||
| `url` | The parsed request-uri. This url should match the url in the browser.|
|
||||
| `method` | The browsers request method (example: `GET`, `POST`, `PUT`, `PATCH`, `DELETE` etc).|
|
||||
| `host` | The website host (example: `domain.com`).|
|
||||
| `loaded_routes` | List of all the routes that matched the `url` and that has been rendered/loaded. |
|
||||
| `all_routes` | All available routes |
|
||||
| `boot_managers` | All available BootManagers |
|
||||
| `csrf_verifier` | CsrfVerifier class |
|
||||
| `log` | List of debug messages/log from the router. |
|
||||
| `router_output` | The rendered callback output from the router. |
|
||||
| `library_version` | The version of simple-php-router you are using. |
|
||||
| `php_version` | The version of PHP you are using. |
|
||||
| `server_params` | List of all `$_SERVER` variables/headers. |
|
||||
|
||||
#### Benchmark and logging
|
||||
|
||||
You can activate benchmark debugging/logging by calling `setDebugEnabled` method on the `Router` instance.
|
||||
|
||||
You have to enable debugging BEFORE starting the routing.
|
||||
|
||||
**Example:**
|
||||
|
||||
```php
|
||||
SimpleRouter::router()->setDebugEnabled(true);
|
||||
SimpleRouter::start();
|
||||
```
|
||||
|
||||
When the routing is complete, you can get the debug-log by calling the `getDebugLog()` on the `Router` instance. This will return an `array` of log-messages each containing execution time, trace info and debug-message.
|
||||
|
||||
**Example:**
|
||||
|
||||
```php
|
||||
$messages = SimpleRouter::router()->getDebugLog();
|
||||
```
|
||||
|
||||
## Reporting a new issue
|
||||
|
||||
**Before reporting your issue, make sure that the issue you are experiencing aren't already answered in the [Common errors](#common-errors) section or by searching the [closed issues](https://github.com/skipperbent/simple-php-router/issues?q=is%3Aissue+is%3Aclosed) page on GitHub.**
|
||||
|
||||
To avoid confusion and to help you resolve your issue as quickly as possible, you should provide a detailed explanation of the problem you are experiencing.
|
||||
|
||||
### Procedure for reporting a new issue
|
||||
|
||||
1. Go to [this page](https://github.com/skipperbent/simple-php-router/issues/new) to create a new issue.
|
||||
2. Add a title that describes your problems in as few words as possible.
|
||||
3. Copy and paste the template below in the description of your issue and replace each step with your own information. If the step is not relevant for your issue you can delete it.
|
||||
|
||||
### Issue template
|
||||
|
||||
Copy and paste the template below into the description of your new issue and replace it with your own information.
|
||||
|
||||
You can check the [Debug information](#debug-information) section to see how to generate the debug-info.
|
||||
|
||||
<pre>
|
||||
### Description
|
||||
|
||||
The library fails to render the route `/user/æsel` which contains one parameter using a custom regular expression for matching special foreign characters. Routes without special characters like `/user/tom` renders correctly.
|
||||
|
||||
### Steps to reproduce the error
|
||||
|
||||
1. Add the following route:
|
||||
|
||||
```php
|
||||
SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
|
||||
```
|
||||
|
||||
2. Navigate to `/user/æsel` in browser.
|
||||
|
||||
3. `NotFoundHttpException` is thrown by library.
|
||||
|
||||
### Route and/or callback for failing route
|
||||
|
||||
*Route:*
|
||||
|
||||
```php
|
||||
SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
|
||||
```
|
||||
|
||||
*Callback:*
|
||||
|
||||
```php
|
||||
public function show($username) {
|
||||
return sprintf('Username is: %s', $username);
|
||||
}
|
||||
```
|
||||
|
||||
### Debug info
|
||||
|
||||
```php
|
||||
array (
|
||||
'url' =>
|
||||
Pecee\Http\Url::__set_state(array(
|
||||
'originalUrl' => NULL,
|
||||
'data' =>
|
||||
array (
|
||||
'scheme' => NULL,
|
||||
'host' => NULL,
|
||||
'port' => NULL,
|
||||
'user' => NULL,
|
||||
'pass' => NULL,
|
||||
'path' => NULL,
|
||||
'query' => NULL,
|
||||
'fragment' => NULL,
|
||||
),
|
||||
)),
|
||||
'method' => '',
|
||||
'host' => NULL,
|
||||
'loaded_routes' =>
|
||||
array (
|
||||
),
|
||||
'all_routes' =>
|
||||
array (
|
||||
0 =>
|
||||
Pecee\SimpleRouter\Route\RouteUrl::__set_state(array(
|
||||
'url' => '/user/{name}/',
|
||||
'name' => NULL,
|
||||
'regex' => NULL,
|
||||
'filterEmptyParams' => true,
|
||||
'defaultParameterRegex' => NULL,
|
||||
'paramModifiers' => '{}',
|
||||
'paramOptionalSymbol' => '?',
|
||||
'urlRegex' => '/^%s\\/?$/u',
|
||||
'group' => NULL,
|
||||
'parent' => NULL,
|
||||
'callback' => 'UserController@show',
|
||||
'defaultNamespace' => NULL,
|
||||
'namespace' => NULL,
|
||||
'requestMethods' =>
|
||||
array (
|
||||
0 => 'get',
|
||||
),
|
||||
'where' =>
|
||||
array (
|
||||
'name' => '[\\w]+',
|
||||
),
|
||||
'parameters' =>
|
||||
array (
|
||||
'name' => NULL,
|
||||
),
|
||||
'originalParameters' =>
|
||||
array (
|
||||
),
|
||||
'middlewares' =>
|
||||
array (
|
||||
),
|
||||
)),
|
||||
),
|
||||
'boot_managers' =>
|
||||
array (
|
||||
),
|
||||
'csrf_verifier' => NULL,
|
||||
'log' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'message' => 'Started routing request (rewrite: no)',
|
||||
'time' => '0.0000069141',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
|
||||
'line' => 57,
|
||||
'function' => 'routeRequest',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'message' => 'Loading routes',
|
||||
'time' => '0.0036418438',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
|
||||
'line' => 273,
|
||||
'function' => 'loadRoutes',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'message' => 'Processing routes',
|
||||
'time' => '0.0069010258',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
|
||||
'line' => 251,
|
||||
'function' => 'processRoutes',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
3 =>
|
||||
array (
|
||||
'message' => 'Processing route "Pecee\\SimpleRouter\\Route\\RouteUrl"',
|
||||
'time' => '0.0099139214',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
|
||||
'line' => 251,
|
||||
'function' => 'processRoutes',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
4 =>
|
||||
array (
|
||||
'message' => 'Finished loading routes',
|
||||
'time' => '0.0130679607',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
|
||||
'line' => 273,
|
||||
'function' => 'loadRoutes',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
5 =>
|
||||
array (
|
||||
'message' => 'Matching route "Pecee\\SimpleRouter\\Route\\RouteUrl"',
|
||||
'time' => '0.0160858631',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
|
||||
'line' => 57,
|
||||
'function' => 'routeRequest',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
6 =>
|
||||
array (
|
||||
'message' => 'Route not found: "/"',
|
||||
'time' => '0.0193598270',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
|
||||
'line' => 57,
|
||||
'function' => 'routeRequest',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
7 =>
|
||||
array (
|
||||
'message' => 'Starting exception handling for "Pecee\\SimpleRouter\\Exceptions\\NotFoundHttpException"',
|
||||
'time' => '0.0229449272',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
|
||||
'line' => 345,
|
||||
'function' => 'handleException',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
8 =>
|
||||
array (
|
||||
'message' => 'Finished exception handling - exception not handled, throwing',
|
||||
'time' => '0.0258929729',
|
||||
'trace' =>
|
||||
array (
|
||||
'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
|
||||
'line' => 345,
|
||||
'function' => 'handleException',
|
||||
'class' => 'Pecee\\SimpleRouter\\Router',
|
||||
'type' => '->',
|
||||
),
|
||||
),
|
||||
),
|
||||
'router_output' => NULL,
|
||||
'library_version' => false,
|
||||
'php_version' => '7.2.0',
|
||||
'server_params' =>
|
||||
array (),
|
||||
)
|
||||
```
|
||||
|
||||
</pre>
|
||||
|
||||
Remember that a more detailed issue- description and debug-info might suck to write, but it will help others understand- and resolve your issue without asking for the information.
|
||||
|
||||
**Note:** 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. Providing the necessary steps to reproduce the error within your description, adding useful debugging info etc. will help others quickly resolve the issue you are reporting.
|
||||
|
||||
---
|
||||
|
||||
# Routes
|
||||
|
||||
Remember the ```routes.php``` file you required in your ```index.php```? This file be where you place all your custom rules for routing.
|
||||
@@ -358,7 +796,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() {
|
||||
@@ -544,7 +982,7 @@ SimpleRouter::group(['namespace' => 'Admin'], function () {
|
||||
|
||||
### Subdomain-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:
|
||||
Route groups may also be used to handle sub-domain routing. Sub-domains may be assigned route parameters just like route urls, 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:
|
||||
|
||||
```php
|
||||
SimpleRouter::group(['domain' => '{account}.myapp.com'], function () {
|
||||
@@ -556,7 +994,7 @@ SimpleRouter::group(['domain' => '{account}.myapp.com'], function () {
|
||||
|
||||
### Route prefixes
|
||||
|
||||
The `prefix` group attribute may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with `admin`:
|
||||
The `prefix` group attribute may be used to prefix each route in the group with a given url. For example, you may want to prefix all route urls within the group with `admin`:
|
||||
|
||||
```php
|
||||
SimpleRouter::group(['prefix' => '/admin'], function () {
|
||||
@@ -566,6 +1004,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:
|
||||
@@ -632,11 +1093,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:**
|
||||
|
||||
@@ -654,22 +1161,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);
|
||||
```
|
||||
|
||||
---
|
||||
@@ -746,7 +1276,7 @@ class CustomExceptionHandler implements IExceptionHandler
|
||||
|
||||
/* You can use the exception handler to format errors depending on the request and type. */
|
||||
|
||||
if (stripos($request->getUri()->getPath(), '/api') !== false) {
|
||||
if (stripos($request->getUrl()->getPath(), '/api') !== false) {
|
||||
|
||||
response()->json([
|
||||
'error' => $error->getMessage(),
|
||||
@@ -965,6 +1495,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.
|
||||
@@ -986,126 +1517,22 @@ $siteId = input('site_id', 2, ['post', 'get']);
|
||||
# Advanced
|
||||
|
||||
## Url rewriting
|
||||
Sometimes it can be useful to manipulate the route about to be loaded.
|
||||
simple-php-router allows you to easily change the route about to be executed.
|
||||
All information about the current route is stored in the ```\Pecee\SimpleRouter\Router``` instance's `loadedRoute` property.
|
||||
|
||||
For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::router()`.
|
||||
### Changing current route
|
||||
|
||||
Sometimes it can be useful to manipulate the route about to be loaded.
|
||||
simple-php-router allows you to easily manipulate and change the routes which are about to be rendered.
|
||||
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 helper function `request()` instead of calling the class directly `\Pecee\SimpleRouter\SimpleRouter::router()`.
|
||||
|
||||
|
||||
```php
|
||||
use Pecee\SimpleRouter;
|
||||
$request = SimpleRouter::request();
|
||||
$request->setRewriteCallback('Example\MyCustomClass@hello');
|
||||
request()->setRewriteCallback('Example\MyCustomClass@hello');
|
||||
|
||||
// -- or you can rewrite by url --
|
||||
|
||||
$request->setRewriteUrl('/my-rewrite-url');
|
||||
```
|
||||
|
||||
**Note:** It's only possible to change the route BEFORE the route has initially been rendered. You can use the `Request` object to manipulate the route which are about to be loaded.
|
||||
|
||||
### Rewrite using callback
|
||||
|
||||
This method is most efficient, as it will render the route immediately.
|
||||
|
||||
This method is useful for rendering 404-pages etc.
|
||||
|
||||
You can also change the callback by modifying the `$route` parameter. This is perfect if you just want to display a view quickly - or change the callback depending
|
||||
on some criteria's for the request.
|
||||
|
||||
The callback below will fire immediately after the `Middleware` or `ExceptionHandler` has been loaded, as they are loaded before the route is rendered.
|
||||
If you wish to change the callback from outside, please have this in mind.
|
||||
|
||||
The example below will render `DefaultController@notFound` regardless of the url.
|
||||
|
||||
**NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.**
|
||||
|
||||
##### Middleware example
|
||||
|
||||
```php
|
||||
namespace Demo\Middlewares;
|
||||
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class CustomMiddleware implements IMiddleware {
|
||||
|
||||
public function handle(Request $request) {
|
||||
|
||||
$request->setRewriteCallback('Demo\Controllers\DefaultController@notFound');
|
||||
return $request;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
##### Exception handler example
|
||||
|
||||
```php
|
||||
namespace Demo\Handlers;
|
||||
|
||||
use Pecee\Handlers\IExceptionHandler;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
|
||||
|
||||
class CustomExceptionHandler implements IExceptionHandler
|
||||
{
|
||||
public function handleError(Request $request, \Exception $error)
|
||||
{
|
||||
/* The router will throw the NotFoundHttpException on 404 */
|
||||
if($error instanceof NotFoundHttpException) {
|
||||
|
||||
/*
|
||||
* Render your own custom 404-view, rewrite the request to another route,
|
||||
* or simply return the $request object to ignore the error and continue on rendering the route.
|
||||
*
|
||||
* The code below will make the router render our page.notfound route.
|
||||
*/
|
||||
|
||||
$request->setRewriteCallback('Demo\Controllers\DefaultController@notFound');
|
||||
return $request;
|
||||
|
||||
}
|
||||
|
||||
throw $error;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Rewrite using url
|
||||
|
||||
The example below will cause the router to reload the request and reinitialize all the routes. This method is slower, but will ensure that all middlewares and rules for the route is loaded.
|
||||
|
||||
This method is useful if you want to redirect a url to another-url which is dependent on a middleware. You can also add a custom rule by calling `$request->setRewriteRoute($route)` if
|
||||
you want to customize request-methods or use another route-type like `RouteController` etc.
|
||||
|
||||
We are using the `url()` helper function to get the uri to another route added in the `routes.php` file.
|
||||
|
||||
**NOTE: Use this method if you want to fully load another route using it's settings (request method, middlewares etc).**
|
||||
|
||||
##### Middleware example
|
||||
|
||||
The example below will redirect the request to the `home`-route.
|
||||
|
||||
```php
|
||||
namespace Demo\Middlewares;
|
||||
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class CustomMiddleware implements IMiddleware {
|
||||
|
||||
public function handle(Request $request) {
|
||||
|
||||
$request->setRewriteUrl(url('home'));
|
||||
return $request;
|
||||
|
||||
}
|
||||
}
|
||||
request()->setRewriteUrl('/my-rewrite-url');
|
||||
```
|
||||
|
||||
### Bootmanager: loading routes dynamically
|
||||
@@ -1129,11 +1556,10 @@ class CustomRouterRules implement IRouterBootManager {
|
||||
|
||||
foreach($rewriteRules as $url => $rule) {
|
||||
|
||||
// If the current uri matches the url, we use our custom route
|
||||
// If the current url matches the rewrite url, we use our custom route
|
||||
|
||||
if($request->getUri()->getPath() === $url) {
|
||||
if($request->getUrl()->getPath() === $url) {
|
||||
$request->setRewriteUrl($rule);
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -9,7 +9,12 @@
|
||||
"simple-php-router",
|
||||
"laravel",
|
||||
"pecee",
|
||||
"php"
|
||||
"php",
|
||||
"framework",
|
||||
"url-handling",
|
||||
"input-handler",
|
||||
"routing-engine",
|
||||
"request-handler"
|
||||
],
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
@@ -22,10 +27,11 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.7.7"
|
||||
"phpunit/phpunit": "^6.0",
|
||||
"mockery/mockery": "^1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
+23
-3
@@ -18,6 +18,8 @@ use Pecee\SimpleRouter\SimpleRouter as Router;
|
||||
* @param string|array|null $parameters
|
||||
* @param array|null $getParams
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function url($name = null, $parameters = null, $getParams = null)
|
||||
{
|
||||
@@ -26,6 +28,7 @@ function url($name = null, $parameters = null, $getParams = null)
|
||||
|
||||
/**
|
||||
* @return \Pecee\Http\Response
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function response()
|
||||
{
|
||||
@@ -34,6 +37,7 @@ function response()
|
||||
|
||||
/**
|
||||
* @return \Pecee\Http\Request
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function request()
|
||||
{
|
||||
@@ -45,15 +49,16 @@ function request()
|
||||
* @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
|
||||
* @return \Pecee\Http\Input\InputHandler|string
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function input($index = null, $defaultValue = null, $methods = null)
|
||||
{
|
||||
if ($index !== null) {
|
||||
return request()->getInput()->get($index, $defaultValue, $methods);
|
||||
return request()->getInputHandler()->get($index, $defaultValue, $methods);
|
||||
}
|
||||
|
||||
return request()->getInput();
|
||||
return request()->getInputHandler();
|
||||
}
|
||||
|
||||
function redirect($url, $code = null)
|
||||
@@ -63,4 +68,19 @@ function redirect($url, $code = null)
|
||||
}
|
||||
|
||||
response()->redirect($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current csrf-token
|
||||
* @return string|null
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
function csrf_token()
|
||||
{
|
||||
$baseVerifier = Router::router()->getCsrfVerifier();
|
||||
if ($baseVerifier !== null) {
|
||||
return $baseVerifier->getTokenProvider()->getToken();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false">
|
||||
<testsuites>
|
||||
<testsuite name="SimpleRouter Test Suite">
|
||||
<directory>tests/Pecee/SimpleRouter/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
@@ -1,47 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Controllers;
|
||||
|
||||
interface IResourceController
|
||||
{
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @return string|null
|
||||
*/
|
||||
public function index();
|
||||
public function index(): ?string;
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
* @return void
|
||||
* @return string|null
|
||||
*/
|
||||
public function show($id);
|
||||
public function show($id): ?string;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @return string|null
|
||||
*/
|
||||
public function store();
|
||||
public function store(): ?string;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @return string|null
|
||||
*/
|
||||
public function create();
|
||||
public function create(): ?string;
|
||||
|
||||
/**
|
||||
* View
|
||||
* @param mixed $id
|
||||
* @return void
|
||||
* @return string|null
|
||||
*/
|
||||
public function edit($id);
|
||||
public function edit($id): ?string;
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
* @return void
|
||||
* @return string|null
|
||||
*/
|
||||
public function update($id);
|
||||
public function update($id): ?string;
|
||||
|
||||
/**
|
||||
* @param mixed $id
|
||||
* @return void
|
||||
* @return string|null
|
||||
*/
|
||||
public function destroy($id);
|
||||
public function destroy($id): ?string;
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
namespace Pecee;
|
||||
|
||||
class CsrfToken
|
||||
{
|
||||
const CSRF_KEY = 'CSRF-TOKEN';
|
||||
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* Generate random identifier for CSRF token
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @return string
|
||||
*/
|
||||
public static function generateToken()
|
||||
{
|
||||
if (function_exists('random_bytes')) {
|
||||
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
|
||||
*
|
||||
* @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() === true) {
|
||||
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,8 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Exceptions;
|
||||
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -25,12 +25,11 @@ class CallbackExceptionHandler implements IExceptionHandler
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param \Exception $error
|
||||
* @return Request|null
|
||||
*/
|
||||
public function handleError(Request $request, \Exception $error)
|
||||
public function handleError(Request $request, \Exception $error): void
|
||||
{
|
||||
/* Fire exceptions */
|
||||
return call_user_func($this->callback,
|
||||
\call_user_func($this->callback,
|
||||
$request,
|
||||
$error
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Handlers;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
@@ -8,8 +9,7 @@ interface IExceptionHandler
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param \Exception $error
|
||||
* @return Request|null
|
||||
*/
|
||||
public function handleError(Request $request, \Exception $error);
|
||||
public function handleError(Request $request, \Exception $error): void;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Exceptions;
|
||||
|
||||
class MalformedUrlException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
interface IInputItem
|
||||
{
|
||||
|
||||
public function getIndex();
|
||||
public function getIndex(): string;
|
||||
|
||||
public function setIndex($index);
|
||||
public function setIndex(string $index): self;
|
||||
|
||||
public function getName();
|
||||
public function getName(): string;
|
||||
|
||||
public function setName($name);
|
||||
public function setName(string $name): self;
|
||||
|
||||
public function getValue();
|
||||
public function getValue(): string;
|
||||
|
||||
public function setValue(string $value): self;
|
||||
|
||||
public function __toString();
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
use Pecee\Exceptions\InvalidArgumentException;
|
||||
|
||||
class InputFile implements IInputItem
|
||||
{
|
||||
public $index;
|
||||
@@ -8,42 +11,44 @@ class InputFile implements IInputItem
|
||||
public $filename;
|
||||
public $size;
|
||||
public $type;
|
||||
public $error;
|
||||
public $errors;
|
||||
public $tmpName;
|
||||
|
||||
public function __construct($index)
|
||||
public function __construct(string $index)
|
||||
{
|
||||
$this->index = $index;
|
||||
|
||||
$this->errors = 0;
|
||||
|
||||
// Make the name human friendly, by replace _ with space
|
||||
$this->name = ucfirst(str_replace('_', ' ', $this->index));
|
||||
$this->name = ucfirst(str_replace('_', ' ', strtolower($this->index)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from array
|
||||
*
|
||||
* @param array $values
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws InvalidArgumentException
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromArray(array $values)
|
||||
public static function createFromArray(array $values): self
|
||||
{
|
||||
if (!isset($values['index'])) {
|
||||
throw new \InvalidArgumentException('Index key is required');
|
||||
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'])
|
||||
->setSize((int)$values['size'])
|
||||
->setError($values['error'])
|
||||
->setType($values['type'])
|
||||
->setTmpName($values['tmp_name'])
|
||||
@@ -54,7 +59,7 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIndex()
|
||||
public function getIndex(): string
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
@@ -62,9 +67,9 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* Set input index
|
||||
* @param string $index
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setIndex($index)
|
||||
public function setIndex(string $index): IInputItem
|
||||
{
|
||||
$this->index = $index;
|
||||
|
||||
@@ -74,7 +79,7 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSize()
|
||||
public function getSize(): string
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
@@ -82,9 +87,9 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* Set file size
|
||||
* @param int $size
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setSize($size)
|
||||
public function setSize(int $size): IInputItem
|
||||
{
|
||||
$this->size = $size;
|
||||
|
||||
@@ -95,7 +100,7 @@ class InputFile implements IInputItem
|
||||
* Get mime-type of file
|
||||
* @return string
|
||||
*/
|
||||
public function getMime()
|
||||
public function getMime(): string
|
||||
{
|
||||
return $this->getType();
|
||||
}
|
||||
@@ -103,7 +108,7 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
@@ -111,9 +116,9 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* Set type
|
||||
* @param string $type
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setType($type)
|
||||
public function setType(string $type): IInputItem
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
@@ -125,7 +130,7 @@ class InputFile implements IInputItem
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtension()
|
||||
public function getExtension(): string
|
||||
{
|
||||
return pathinfo($this->getFilename(), PATHINFO_EXTENSION);
|
||||
}
|
||||
@@ -135,7 +140,7 @@ class InputFile implements IInputItem
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
@@ -145,9 +150,9 @@ class InputFile implements IInputItem
|
||||
* Useful for adding validation etc.
|
||||
*
|
||||
* @param string $name
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(string $name): IInputItem
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
@@ -158,9 +163,9 @@ class InputFile implements IInputItem
|
||||
* Set filename
|
||||
*
|
||||
* @param string $name
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setFilename($name)
|
||||
public function setFilename($name): IInputItem
|
||||
{
|
||||
$this->filename = $name;
|
||||
|
||||
@@ -172,7 +177,7 @@ class InputFile implements IInputItem
|
||||
*
|
||||
* @return string mixed
|
||||
*/
|
||||
public function getFilename()
|
||||
public function getFilename(): string
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
@@ -183,7 +188,7 @@ class InputFile implements IInputItem
|
||||
* @param string $destination
|
||||
* @return bool
|
||||
*/
|
||||
public function move($destination)
|
||||
public function move($destination): bool
|
||||
{
|
||||
return move_uploaded_file($this->tmpName, $destination);
|
||||
}
|
||||
@@ -193,7 +198,7 @@ class InputFile implements IInputItem
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContents()
|
||||
public function getContents(): string
|
||||
{
|
||||
return file_get_contents($this->tmpName);
|
||||
}
|
||||
@@ -203,7 +208,7 @@ class InputFile implements IInputItem
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasError()
|
||||
public function hasError(): bool
|
||||
{
|
||||
return ($this->getError() !== 0);
|
||||
}
|
||||
@@ -213,20 +218,20 @@ class InputFile implements IInputItem
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getError()
|
||||
public function getError(): string
|
||||
{
|
||||
return $this->error;
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set error
|
||||
*
|
||||
* @param int $error
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setError($error)
|
||||
public function setError($error): IInputItem
|
||||
{
|
||||
$this->error = (int)$error;
|
||||
$this->errors = (int)$error;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -234,7 +239,7 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTmpName()
|
||||
public function getTmpName(): string
|
||||
{
|
||||
return $this->tmpName;
|
||||
}
|
||||
@@ -242,9 +247,9 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* Set file temp. name
|
||||
* @param string $name
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setTmpName($name)
|
||||
public function setTmpName($name): IInputItem
|
||||
{
|
||||
$this->tmpName = $name;
|
||||
|
||||
@@ -256,19 +261,31 @@ class InputFile implements IInputItem
|
||||
return $this->getTmpName();
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->getFilename();
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
/**
|
||||
* @param string $value
|
||||
* @return static
|
||||
*/
|
||||
public function setValue(string $value): IInputItem
|
||||
{
|
||||
$this->filename = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'tmp_name' => $this->tmpName,
|
||||
'type' => $this->type,
|
||||
'size' => $this->size,
|
||||
'name' => $this->filename,
|
||||
'error' => $this->error,
|
||||
'name' => $this->name,
|
||||
'error' => $this->errors,
|
||||
'filename' => $this->filename,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
use Pecee\Exceptions\InvalidArgumentException;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class Input
|
||||
class InputHandler
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
@@ -25,6 +27,10 @@ class Input
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Input constructor.
|
||||
* @param Request $request
|
||||
*/
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
@@ -32,48 +38,59 @@ class Input
|
||||
$this->parseInputs();
|
||||
}
|
||||
|
||||
public function parseInputs()
|
||||
/**
|
||||
* Parse input values
|
||||
*
|
||||
*/
|
||||
public function parseInputs(): void
|
||||
{
|
||||
/* Parse get requests */
|
||||
if (count($_GET) > 0) {
|
||||
if (\count($_GET) !== 0) {
|
||||
$this->get = $this->handleGetPost($_GET);
|
||||
}
|
||||
|
||||
/* Parse post requests */
|
||||
$postVars = $_POST;
|
||||
|
||||
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete'], false) === true) {
|
||||
if (\in_array($this->request->getMethod(), ['put', 'patch', 'delete'], false) === true) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public function parseFiles()
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function parseFiles(): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ((array)$_FILES as $key => $value) {
|
||||
|
||||
// Handle array input
|
||||
if (is_array($value['name']) === false) {
|
||||
if (\is_array($value['name']) === false) {
|
||||
$values['index'] = $key;
|
||||
$list[$key] = InputFile::createFromArray(array_merge($value, $values));
|
||||
try {
|
||||
$list[$key] = InputFile::createFromArray($values + $value);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
|
||||
}
|
||||
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;
|
||||
@@ -84,51 +101,47 @@ class Input
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected function rearrangeFiles(array $values, &$index, $original)
|
||||
protected function rearrangeFiles(array $values, &$index, $original): array
|
||||
{
|
||||
|
||||
$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,
|
||||
'filename' => $getItem($key),
|
||||
'error' => $getItem($key, 'error'),
|
||||
'tmp_name' => $getItem($key, 'tmp_name'),
|
||||
'type' => $getItem($key, 'type'),
|
||||
'size' => $getItem($key, 'size'),
|
||||
]);
|
||||
try {
|
||||
|
||||
$file = InputFile::createFromArray([
|
||||
'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]) === true) {
|
||||
$output[$key][] = $file;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($output[$key])) {
|
||||
$output[$key][] = $file;
|
||||
} else {
|
||||
$output[$key] = $file;
|
||||
}
|
||||
continue;
|
||||
|
||||
continue;
|
||||
} catch (InvalidArgumentException $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$index[] = $key;
|
||||
|
||||
$files = $this->rearrangeFiles($value, $index, $original);
|
||||
|
||||
if (isset($output[$key])) {
|
||||
if (isset($output[$key]) === true) {
|
||||
$output[$key][] = $files;
|
||||
} else {
|
||||
$output[$key] = $files;
|
||||
@@ -139,20 +152,14 @@ class Input
|
||||
return $output;
|
||||
}
|
||||
|
||||
protected function handleGetPost(array $array)
|
||||
protected function handleGetPost(array $array): array
|
||||
{
|
||||
$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) {
|
||||
if (\is_array($value) === false) {
|
||||
$list[$key] = new InputItem($key, $value);
|
||||
continue;
|
||||
}
|
||||
@@ -172,9 +179,9 @@ class Input
|
||||
* @param string|null $defaultValue
|
||||
* @return InputItem|string
|
||||
*/
|
||||
public function findPost($index, $defaultValue = null)
|
||||
public function findPost(string $index, ?string $defaultValue = null)
|
||||
{
|
||||
return isset($this->post[$index]) ? $this->post[$index] : $defaultValue;
|
||||
return $this->post[$index] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,9 +191,9 @@ class Input
|
||||
* @param string|null $defaultValue
|
||||
* @return InputFile|string
|
||||
*/
|
||||
public function findFile($index, $defaultValue = null)
|
||||
public function findFile(string $index, ?string $defaultValue = null)
|
||||
{
|
||||
return isset($this->file[$index]) ? $this->file[$index] : $defaultValue;
|
||||
return $this->file[$index] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,9 +203,9 @@ class Input
|
||||
* @param string|null $defaultValue
|
||||
* @return InputItem|string
|
||||
*/
|
||||
public function findGet($index, $defaultValue = null)
|
||||
public function findGet(string $index, ?string $defaultValue = null)
|
||||
{
|
||||
return isset($this->get[$index]) ? $this->get[$index] : $defaultValue;
|
||||
return $this->get[$index] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,27 +216,27 @@ class Input
|
||||
* @param array|string|null $methods
|
||||
* @return IInputItem|string
|
||||
*/
|
||||
public function getObject($index, $defaultValue = null, $methods = null)
|
||||
public function getObject(string $index, ?string $defaultValue = null, $methods = null)
|
||||
{
|
||||
if ($methods !== null && is_string($methods) === true) {
|
||||
if ($methods !== null && \is_string($methods) === true) {
|
||||
$methods = [$methods];
|
||||
}
|
||||
|
||||
$element = null;
|
||||
|
||||
if ($methods === null || in_array('get', $methods)) {
|
||||
if ($methods === null || \in_array('get', $methods, true) === 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, true) === 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, true) === true)) {
|
||||
$element = $this->findFile($index);
|
||||
}
|
||||
|
||||
return ($element !== null) ? $element : $defaultValue;
|
||||
return $element ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +247,7 @@ class Input
|
||||
* @param array|string|null $methods
|
||||
* @return InputItem|string
|
||||
*/
|
||||
public function get($index, $defaultValue = null, $methods = null)
|
||||
public function get(string $index, ?string $defaultValue = null, $methods = null)
|
||||
{
|
||||
$input = $this->getObject($index, $defaultValue, $methods);
|
||||
|
||||
@@ -257,7 +264,7 @@ class Input
|
||||
* @param string $index
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($index)
|
||||
public function exists(string $index): bool
|
||||
{
|
||||
return ($this->getObject($index) !== null);
|
||||
}
|
||||
@@ -267,23 +274,23 @@ class Input
|
||||
* @param array|null $filter Only take items in filter
|
||||
* @return array
|
||||
*/
|
||||
public function all(array $filter = null)
|
||||
public function all(array $filter = null): array
|
||||
{
|
||||
$output = $_POST;
|
||||
$output = $_GET + $_POST;
|
||||
|
||||
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 += $post;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ($filter !== null) ? array_intersect_key($output, array_flip($filter)) : array_merge($_GET, $output);
|
||||
return ($filter !== null) ? array_intersect_key($output, array_flip($filter)) : $output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Input;
|
||||
|
||||
class InputItem implements IInputItem
|
||||
@@ -7,24 +8,24 @@ class InputItem implements IInputItem
|
||||
public $name;
|
||||
public $value;
|
||||
|
||||
public function __construct($index, $value = null)
|
||||
public function __construct(string $index, ?string $value = null)
|
||||
{
|
||||
$this->index = $index;
|
||||
$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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIndex()
|
||||
public function getIndex(): string
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
public function setIndex($index)
|
||||
public function setIndex(string $index): IInputItem
|
||||
{
|
||||
$this->index = $index;
|
||||
|
||||
@@ -34,7 +35,7 @@ class InputItem implements IInputItem
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
@@ -42,9 +43,9 @@ class InputItem implements IInputItem
|
||||
/**
|
||||
* Set input name
|
||||
* @param string $name
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(string $name): IInputItem
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
@@ -54,7 +55,7 @@ class InputItem implements IInputItem
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
@@ -62,9 +63,9 @@ class InputItem implements IInputItem
|
||||
/**
|
||||
* Set input value
|
||||
* @param string $value
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setValue($value)
|
||||
public function setValue(string $value): IInputItem
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
<?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
|
||||
{
|
||||
const POST_KEY = 'csrf-token';
|
||||
const HEADER_KEY = 'X-CSRF-TOKEN';
|
||||
public const POST_KEY = 'csrf-token';
|
||||
public const HEADER_KEY = 'X-CSRF-TOKEN';
|
||||
|
||||
protected $except;
|
||||
protected $csrfToken;
|
||||
protected $token;
|
||||
protected $tokenProvider;
|
||||
|
||||
/**
|
||||
* BaseCsrfVerifier constructor.
|
||||
* @throws \Pecee\Http\Security\Exceptions\SecurityException
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,23 +29,23 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function skip(Request $request)
|
||||
protected function skip(Request $request): bool
|
||||
{
|
||||
if ($this->except === null || is_array($this->except) === false) {
|
||||
if ($this->except === null || \count($this->except) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$max = count($this->except) - 1;
|
||||
$max = \count($this->except) - 1;
|
||||
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
$url = $this->except[$i];
|
||||
|
||||
$url = rtrim($url, '/');
|
||||
if ($url[strlen($url) - 1] === '*') {
|
||||
if ($url[\strlen($url) - 1] === '*') {
|
||||
$url = rtrim($url, '*');
|
||||
$skip = (stripos($request->getUri()->getPath(), $url) === 0);
|
||||
$skip = $request->getUrl()->contains($url);
|
||||
} else {
|
||||
$skip = ($url === $request->getUri()->getPath());
|
||||
$skip = ($url === $request->getUrl()->getOriginalUrl());
|
||||
}
|
||||
|
||||
if ($skip === true) {
|
||||
@@ -54,46 +56,47 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handle(Request $request)
|
||||
/**
|
||||
* Handle request
|
||||
*
|
||||
* @param Request $request
|
||||
* @throws TokenMismatchException
|
||||
*/
|
||||
public function handle(Request $request): void
|
||||
{
|
||||
|
||||
if ($this->skip($request) === false && in_array($request->getMethod(), ['post', 'put', 'delete'], false) === true) {
|
||||
if ($this->skip($request) === false && \in_array($request->getMethod(), ['post', 'put', 'delete'], true) === true) {
|
||||
|
||||
$token = $request->getInput()->get(static::POST_KEY, null, 'post');
|
||||
$token = $request->getInputHandler()->get(static::POST_KEY, null, 'post');
|
||||
|
||||
// If the token is not posted, check headers for valid x-csrf-token
|
||||
if ($token === null) {
|
||||
$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(): ITokenProvider
|
||||
{
|
||||
$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): void
|
||||
{
|
||||
if ($this->token !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->csrfToken->hasToken();
|
||||
}
|
||||
|
||||
public function getToken()
|
||||
{
|
||||
return $this->token;
|
||||
$this->tokenProvider = $provider;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Middleware\Exceptions;
|
||||
|
||||
class TokenMismatchException extends \Exception
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Middleware;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
@@ -7,8 +8,7 @@ interface IMiddleware
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return Request|null
|
||||
*/
|
||||
public function handle(Request $request);
|
||||
public function handle(Request $request): void;
|
||||
|
||||
}
|
||||
+132
-113
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
use Pecee\Http\Input\Input;
|
||||
use Pecee\Http\Input\InputHandler;
|
||||
use Pecee\SimpleRouter\Route\ILoadableRoute;
|
||||
use Pecee\SimpleRouter\Route\RouteUrl;
|
||||
use Pecee\SimpleRouter\SimpleRouter;
|
||||
@@ -9,11 +10,13 @@ use Pecee\SimpleRouter\SimpleRouter;
|
||||
class Request
|
||||
{
|
||||
private $data = [];
|
||||
protected $headers;
|
||||
protected $headers = [];
|
||||
protected $host;
|
||||
protected $uri;
|
||||
protected $url;
|
||||
protected $method;
|
||||
protected $input;
|
||||
protected $inputHandler;
|
||||
|
||||
protected $hasRewrite = false;
|
||||
|
||||
/**
|
||||
* @var ILoadableRoute|null
|
||||
@@ -22,64 +25,55 @@ class Request
|
||||
protected $rewriteUrl;
|
||||
|
||||
/**
|
||||
* @var ILoadableRoute|null
|
||||
* @var array
|
||||
*/
|
||||
protected $loadedRoute;
|
||||
protected $loadedRoutes = [];
|
||||
|
||||
/**
|
||||
* Request constructor.
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->parseHeaders();
|
||||
$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'));
|
||||
}
|
||||
|
||||
protected function parseHeaders()
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
$this->setHost($this->getHeader('http-host'));
|
||||
|
||||
// Check if special IIS header exist, otherwise use default.
|
||||
$this->setUrl($this->getHeader('unencoded-url', $this->getHeader('request-uri')));
|
||||
|
||||
$this->inputHandler = new InputHandler($this);
|
||||
$this->method = strtolower($this->inputHandler->get('_method', $this->getHeader('request-method')));
|
||||
}
|
||||
|
||||
public function isSecure()
|
||||
public function isSecure(): bool
|
||||
{
|
||||
return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Uri
|
||||
* @return Url
|
||||
*/
|
||||
public function getUri()
|
||||
public function getUrl(): Url
|
||||
{
|
||||
return $this->uri;
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHost()
|
||||
public function getHost(): ?string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMethod()
|
||||
public function getMethod(): ?string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
@@ -88,7 +82,7 @@ class Request
|
||||
* Get http basic auth user
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUser()
|
||||
public function getUser(): ?string
|
||||
{
|
||||
return $this->getHeader('php-auth-user');
|
||||
}
|
||||
@@ -97,7 +91,7 @@ class Request
|
||||
* Get http basic auth password
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPassword()
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->getHeader('php-auth-pw');
|
||||
}
|
||||
@@ -106,16 +100,16 @@ class Request
|
||||
* Get all headers
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders()
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id address
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getIp()
|
||||
public function getIp(): ?string
|
||||
{
|
||||
if ($this->getHeader('http-cf-connecting-ip') !== null) {
|
||||
return $this->getHeader('http-cf-connecting-ip');
|
||||
@@ -132,27 +126,27 @@ class Request
|
||||
* Get remote address/ip
|
||||
*
|
||||
* @alias static::getIp
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRemoteAddr()
|
||||
public function getRemoteAddr(): ?string
|
||||
{
|
||||
return $this->getIp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get referer
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getReferer()
|
||||
public function getReferer(): ?string
|
||||
{
|
||||
return $this->getHeader('http-referer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user agent
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUserAgent()
|
||||
public function getUserAgent(): ?string
|
||||
{
|
||||
return $this->getHeader('http-user-agent');
|
||||
}
|
||||
@@ -165,35 +159,18 @@ class Request
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHeader($name, $defaultValue = null)
|
||||
public function getHeader($name, $defaultValue = null): ?string
|
||||
{
|
||||
if (array_key_exists(strtolower($name), $this->headers) === true) {
|
||||
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 $this->headers[strtolower($name)] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input class
|
||||
* @return Input
|
||||
* @return InputHandler
|
||||
*/
|
||||
public function getInput()
|
||||
public function getInputHandler(): InputHandler
|
||||
{
|
||||
return $this->input;
|
||||
return $this->inputHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,32 +180,43 @@ class Request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFormatAccepted($format)
|
||||
public function isFormatAccepted($format): bool
|
||||
{
|
||||
return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) > -1);
|
||||
return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request is made through Ajax
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAjax(): bool
|
||||
{
|
||||
return (strtolower($this->getHeader('http-x-requested-with')) === 'xmlhttprequest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get accept formats
|
||||
* @return array
|
||||
*/
|
||||
public function getAcceptFormats()
|
||||
public function getAcceptFormats(): array
|
||||
{
|
||||
return explode(',', $this->getHeader('http-accept'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Uri $uri
|
||||
* @param string|Url $url
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public function setUri(Uri $uri)
|
||||
public function setUrl($url): void
|
||||
{
|
||||
$this->uri = $uri;
|
||||
$this->url = ($url instanceof Url) ? $url : new Url($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
* @param string|null $host
|
||||
*/
|
||||
public function setHost($host)
|
||||
public function setHost(?string $host): void
|
||||
{
|
||||
$this->host = $host;
|
||||
}
|
||||
@@ -236,7 +224,7 @@ class Request
|
||||
/**
|
||||
* @param string $method
|
||||
*/
|
||||
public function setMethod($method)
|
||||
public function setMethod(string $method): void
|
||||
{
|
||||
$this->method = $method;
|
||||
}
|
||||
@@ -247,28 +235,10 @@ class Request
|
||||
* @param ILoadableRoute $route
|
||||
* @return static
|
||||
*/
|
||||
public function setRewriteRoute(ILoadableRoute $route)
|
||||
public function setRewriteRoute(ILoadableRoute $route): self
|
||||
{
|
||||
$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->hasRewrite = true;
|
||||
$this->rewriteRoute = SimpleRouter::addDefaultNamespace($route);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -278,7 +248,7 @@ class Request
|
||||
*
|
||||
* @return ILoadableRoute|null
|
||||
*/
|
||||
public function getRewriteRoute()
|
||||
public function getRewriteRoute(): ?ILoadableRoute
|
||||
{
|
||||
return $this->rewriteRoute;
|
||||
}
|
||||
@@ -286,9 +256,9 @@ class Request
|
||||
/**
|
||||
* Get rewrite url
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRewriteUrl()
|
||||
public function getRewriteUrl(): ?string
|
||||
{
|
||||
return $this->rewriteUrl;
|
||||
}
|
||||
@@ -299,48 +269,97 @@ class Request
|
||||
* @param string $rewriteUrl
|
||||
* @return static
|
||||
*/
|
||||
public function setRewriteUrl($rewriteUrl)
|
||||
public function setRewriteUrl(string $rewriteUrl): self
|
||||
{
|
||||
$this->rewriteUrl = $rewriteUrl;
|
||||
$this->hasRewrite = true;
|
||||
$this->rewriteUrl = rtrim($rewriteUrl, '/') . '/';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set rewrite callback
|
||||
* @param string $callback
|
||||
* @param string|\Closure $callback
|
||||
* @return static
|
||||
*/
|
||||
public function setRewriteCallback($callback)
|
||||
public function setRewriteCallback($callback): self
|
||||
{
|
||||
return $this->setRewriteRoute(new RouteUrl($this->uri, $callback));
|
||||
$this->hasRewrite = true;
|
||||
|
||||
return $this->setRewriteRoute(new RouteUrl($this->getUrl()->getPath(), $callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loaded route
|
||||
* @return ILoadableRoute|null
|
||||
*/
|
||||
public function getLoadedRoute()
|
||||
public function getLoadedRoute(): ?ILoadableRoute
|
||||
{
|
||||
return $this->loadedRoute;
|
||||
return (\count($this->loadedRoutes) > 0) ? end($this->loadedRoutes) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set loaded route
|
||||
* Get all loaded routes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLoadedRoutes(): array
|
||||
{
|
||||
return $this->loadedRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set loaded routes
|
||||
*
|
||||
* @param array $routes
|
||||
* @return static
|
||||
*/
|
||||
public function setLoadedRoutes(array $routes): self
|
||||
{
|
||||
$this->loadedRoutes = $routes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Added loaded route
|
||||
*
|
||||
* @param ILoadableRoute $route
|
||||
* @return static
|
||||
*/
|
||||
public function setLoadedRoute(ILoadableRoute $route)
|
||||
public function addLoadedRoute(ILoadableRoute $route): self
|
||||
{
|
||||
$this->loadedRoute = $route;
|
||||
$this->loadedRoutes[] = $route;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request contains a rewrite
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRewrite(): bool
|
||||
{
|
||||
return $this->hasRewrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the current request contains a rewrite.
|
||||
*
|
||||
* @param bool $boolean
|
||||
* @return Request
|
||||
*/
|
||||
public function setHasRewrite(bool $boolean): self
|
||||
{
|
||||
$this->hasRewrite = $boolean;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __isset($name)
|
||||
{
|
||||
return array_key_exists($name, $this->data);
|
||||
return array_key_exists($name, $this->data) === true;
|
||||
}
|
||||
|
||||
public function __set($name, $value = null)
|
||||
@@ -350,7 +369,7 @@ class Request
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return isset($this->data[$name]) ? $this->data[$name] : null;
|
||||
return $this->data[$name] ?? null;
|
||||
}
|
||||
|
||||
}
|
||||
+29
-20
@@ -1,6 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
use Pecee\Exceptions\InvalidArgumentException;
|
||||
|
||||
class Response
|
||||
{
|
||||
protected $request;
|
||||
@@ -16,7 +19,7 @@ class Response
|
||||
* @param int $code
|
||||
* @return static
|
||||
*/
|
||||
public function httpCode($code)
|
||||
public function httpCode(int $code): self
|
||||
{
|
||||
http_response_code($code);
|
||||
|
||||
@@ -29,19 +32,19 @@ class Response
|
||||
* @param string $url
|
||||
* @param int $httpCode
|
||||
*/
|
||||
public function redirect($url, $httpCode = null)
|
||||
public function redirect(string $url, ?int $httpCode = null): void
|
||||
{
|
||||
if ($httpCode !== null) {
|
||||
$this->httpCode($httpCode);
|
||||
}
|
||||
|
||||
$this->header('location: ' . $url);
|
||||
die();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
public function refresh()
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->redirect($this->request->getUri()->getPath());
|
||||
$this->redirect($this->request->getUrl()->getOriginalUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +52,7 @@ class Response
|
||||
* @param string $name
|
||||
* @return static
|
||||
*/
|
||||
public function auth($name = '')
|
||||
public function auth(string $name = ''): self
|
||||
{
|
||||
$this->headers([
|
||||
'WWW-Authenticate: Basic realm="' . $name . '"',
|
||||
@@ -59,37 +62,43 @@ class Response
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cache($eTag, $lastModified = 2592000)
|
||||
public function cache(string $eTag, int $lastModifiedTime = 2592000): self
|
||||
{
|
||||
|
||||
$this->headers([
|
||||
'Cache-Control: public',
|
||||
'Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT',
|
||||
'Etag: ' . $eTag,
|
||||
sprintf('Last-Modified: %s GMT', gmdate('D, d M Y H:i:s', $lastModifiedTime)),
|
||||
sprintf('Etag: %s', $eTag),
|
||||
]);
|
||||
|
||||
$httpModified = $this->request->getHeader('http-if-modified-since');
|
||||
$httpIfNoneMatch = $this->request->getHeader('http-if-none-match');
|
||||
|
||||
if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModified)) {
|
||||
if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModifiedTime)) {
|
||||
|
||||
$this->header('HTTP/1.1 304 Not Modified');
|
||||
|
||||
exit();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, ?int $options = null, int $dept = 512): void
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +106,7 @@ class Response
|
||||
* @param string $value
|
||||
* @return static
|
||||
*/
|
||||
public function header($value)
|
||||
public function header(string $value): self
|
||||
{
|
||||
header($value);
|
||||
|
||||
@@ -109,7 +118,7 @@ class Response
|
||||
* @param array $headers
|
||||
* @return static
|
||||
*/
|
||||
public function headers(array $headers)
|
||||
public function headers(array $headers): self
|
||||
{
|
||||
foreach ($headers as $header) {
|
||||
$this->header($header);
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Security;
|
||||
|
||||
use Pecee\Http\Security\Exceptions\SecurityException;
|
||||
|
||||
class CookieTokenProvider implements ITokenProvider
|
||||
{
|
||||
public const CSRF_KEY = 'CSRF-TOKEN';
|
||||
|
||||
protected $token;
|
||||
protected $cookieTimeoutMinutes = 120;
|
||||
|
||||
/**
|
||||
* CookieTokenProvider constructor.
|
||||
* @throws SecurityException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->token = $this->getToken();
|
||||
|
||||
if ($this->token === null) {
|
||||
$this->token = $this->generateToken();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random identifier for CSRF token
|
||||
*
|
||||
* @return string
|
||||
* @throws SecurityException
|
||||
*/
|
||||
public function generateToken(): string
|
||||
{
|
||||
try {
|
||||
return bin2hex(random_bytes(32));
|
||||
} catch (\Exception $e) {
|
||||
throw new SecurityException($e->getMessage(), (int)$e->getCode(), $e->getPrevious());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate valid CSRF token
|
||||
*
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(string $token): bool
|
||||
{
|
||||
if ($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(string $token): void
|
||||
{
|
||||
$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(?string $defaultValue = null): ?string
|
||||
{
|
||||
$this->token = ($this->hasToken() === true) ? $_COOKIE[static::CSRF_KEY] : null;
|
||||
|
||||
return $this->token ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh existing token
|
||||
*/
|
||||
public function refresh(): void
|
||||
{
|
||||
if ($this->token !== null) {
|
||||
$this->setToken($this->token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the csrf token has been defined
|
||||
* @return bool
|
||||
*/
|
||||
public function hasToken(): bool
|
||||
{
|
||||
return isset($_COOKIE[static::CSRF_KEY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timeout for cookie in minutes
|
||||
* @return int
|
||||
*/
|
||||
public function getCookieTimeoutMinutes(): int
|
||||
{
|
||||
return $this->cookieTimeoutMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cookie timeout in minutes
|
||||
* @param int $minutes
|
||||
*/
|
||||
public function setCookieTimeoutMinutes(int $minutes): void
|
||||
{
|
||||
$this->cookieTimeoutMinutes = $minutes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Security\Exceptions;
|
||||
|
||||
class SecurityException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\Http\Security;
|
||||
|
||||
interface ITokenProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Refresh existing token
|
||||
*/
|
||||
public function refresh(): void;
|
||||
|
||||
/**
|
||||
* Validate valid CSRF token
|
||||
*
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(string $token): bool;
|
||||
|
||||
/**
|
||||
* Get token token
|
||||
*
|
||||
* @param string|null $defaultValue
|
||||
* @return string|null
|
||||
*/
|
||||
public function getToken(?string $defaultValue = null): ?string;
|
||||
|
||||
}
|
||||
@@ -2,36 +2,44 @@
|
||||
|
||||
namespace Pecee\Http;
|
||||
|
||||
class Uri
|
||||
use Pecee\Http\Exceptions\MalformedUrlException;
|
||||
|
||||
class Url
|
||||
{
|
||||
private $originalUrl;
|
||||
private $data = [
|
||||
'scheme',
|
||||
'host',
|
||||
'port',
|
||||
'user',
|
||||
'pass',
|
||||
'path',
|
||||
'query',
|
||||
'fragment',
|
||||
'scheme' => null,
|
||||
'host' => null,
|
||||
'port' => null,
|
||||
'user' => null,
|
||||
'pass' => null,
|
||||
'path' => null,
|
||||
'query' => null,
|
||||
'fragment' => null,
|
||||
];
|
||||
|
||||
public function __construct($url)
|
||||
/**
|
||||
* Url constructor.
|
||||
* @param string $url
|
||||
* @throws MalformedUrlException
|
||||
*/
|
||||
public function __construct(?string $url)
|
||||
{
|
||||
$this->originalUrl = $url;
|
||||
$this->data = array_merge($this->data, $this->parseUrl(urldecode($url)));
|
||||
if ($url !== null) {
|
||||
$this->data = $this->parseUrl($url) + $this->data;
|
||||
|
||||
if (isset($this->data['path']) === true && $this->data['path'] !== '/') {
|
||||
$this->data['path'] = rtrim($this->data['path'], '/') . '/';
|
||||
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()
|
||||
public function isSecure(): bool
|
||||
{
|
||||
return (strtolower($this->getScheme()) === 'https');
|
||||
}
|
||||
@@ -40,7 +48,7 @@ class Uri
|
||||
* Checks if url is relative
|
||||
* @return bool
|
||||
*/
|
||||
public function isRelative()
|
||||
public function isRelative(): bool
|
||||
{
|
||||
return ($this->getHost() === null);
|
||||
}
|
||||
@@ -49,7 +57,7 @@ class Uri
|
||||
* Get url scheme
|
||||
* @return string|null
|
||||
*/
|
||||
public function getScheme()
|
||||
public function getScheme(): ?string
|
||||
{
|
||||
return $this->data['scheme'];
|
||||
}
|
||||
@@ -58,7 +66,7 @@ class Uri
|
||||
* Get url host
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHost()
|
||||
public function getHost(): ?string
|
||||
{
|
||||
return $this->data['host'];
|
||||
}
|
||||
@@ -67,7 +75,7 @@ class Uri
|
||||
* Get url port
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPort()
|
||||
public function getPort(): ?int
|
||||
{
|
||||
return ($this->data['port'] !== null) ? (int)$this->data['port'] : null;
|
||||
}
|
||||
@@ -76,7 +84,7 @@ class Uri
|
||||
* Parse username from url
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUserName()
|
||||
public function getUserName(): ?string
|
||||
{
|
||||
return $this->data['user'];
|
||||
}
|
||||
@@ -85,7 +93,7 @@ class Uri
|
||||
* Parse password from url
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPassword()
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->data['pass'];
|
||||
}
|
||||
@@ -94,16 +102,16 @@ class Uri
|
||||
* Get path from url
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
public function getPath(): ?string
|
||||
{
|
||||
return $this->data['path'];
|
||||
return $this->data['path'] ?? '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get querystring from url
|
||||
* @return string|null
|
||||
*/
|
||||
public function getQueryString()
|
||||
public function getQueryString(): ?string
|
||||
{
|
||||
return $this->data['query'];
|
||||
}
|
||||
@@ -112,7 +120,7 @@ class Uri
|
||||
* Get fragment from url (everything after #)
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFragment()
|
||||
public function getFragment(): ?string
|
||||
{
|
||||
return $this->data['fragment'];
|
||||
}
|
||||
@@ -120,7 +128,7 @@ class Uri
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalUrl()
|
||||
public function getOriginalUrl(): string
|
||||
{
|
||||
return $this->originalUrl;
|
||||
}
|
||||
@@ -129,13 +137,13 @@ class Uri
|
||||
* UTF-8 aware parse_url() replacement.
|
||||
* @param string $url
|
||||
* @param int $component
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws MalformedUrlException
|
||||
* @return array
|
||||
*/
|
||||
public function parseUrl($url, $component = -1)
|
||||
public function parseUrl(string $url, int $component = -1): array
|
||||
{
|
||||
$encodedUrl = preg_replace_callback(
|
||||
'%[^:/@?&=#]+%u',
|
||||
'/[^:\/@?&=#]+/u',
|
||||
function ($matches) {
|
||||
return urlencode($matches[0]);
|
||||
},
|
||||
@@ -145,21 +153,42 @@ class Uri
|
||||
$parts = parse_url($encodedUrl, $component);
|
||||
|
||||
if ($parts === false) {
|
||||
throw new \InvalidArgumentException('Malformed URL: ' . $url);
|
||||
throw new MalformedUrlException('Malformed URL: ' . $url);
|
||||
}
|
||||
|
||||
foreach ((array)$parts as $name => $value) {
|
||||
$parts[$name] = urldecode($value);
|
||||
}
|
||||
return array_map('urldecode', $parts);
|
||||
}
|
||||
|
||||
return $parts;
|
||||
/**
|
||||
* Get position of value.
|
||||
* Returns -1 on failure.
|
||||
*
|
||||
* @param string $value
|
||||
* @return int
|
||||
*/
|
||||
public function indexOf(string $value): int
|
||||
{
|
||||
$index = stripos($this->getOriginalUrl(), $value);
|
||||
|
||||
return ($index === false) ? -1 : $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if url contains value.
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public function contains(string $value): bool
|
||||
{
|
||||
return (stripos($this->getOriginalUrl(), $value) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data array with information about the url
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Exceptions;
|
||||
|
||||
class HttpException extends \Exception
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Exceptions;
|
||||
|
||||
class NotFoundHttpException extends HttpException
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
@@ -9,7 +10,6 @@ interface IRouterBootManager
|
||||
* Called when router loads it's routes
|
||||
*
|
||||
* @param Request $request
|
||||
* @return Request
|
||||
*/
|
||||
public function boot(Request $request);
|
||||
public function boot(Request $request): void;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
interface IControllerRoute extends IRoute
|
||||
@@ -8,7 +9,7 @@ interface IControllerRoute extends IRoute
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getController();
|
||||
public function getController(): string;
|
||||
|
||||
/**
|
||||
* Set controller class-name
|
||||
@@ -16,21 +17,6 @@ interface IControllerRoute extends IRoute
|
||||
* @param string $controller
|
||||
* @return static
|
||||
*/
|
||||
public function setController($controller);
|
||||
|
||||
/**
|
||||
* Return active method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod();
|
||||
|
||||
/**
|
||||
* Set active method
|
||||
*
|
||||
* @param string $method
|
||||
* @return static
|
||||
*/
|
||||
public function setMethod($method);
|
||||
public function setController(string $controller): self;
|
||||
|
||||
}
|
||||
@@ -13,21 +13,21 @@ interface IGroupRoute extends IRoute
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function matchDomain(Request $request);
|
||||
public function matchDomain(Request $request): bool;
|
||||
|
||||
/**
|
||||
* Add exception handler
|
||||
*
|
||||
* @param IExceptionHandler|string $handler
|
||||
* @return static $this;
|
||||
* @return static
|
||||
*/
|
||||
public function addExceptionHandler($handler);
|
||||
public function addExceptionHandler($handler): self;
|
||||
|
||||
/**
|
||||
* Set exception-handlers for group
|
||||
*
|
||||
* @param array $handlers
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setExceptionHandlers(array $handlers);
|
||||
|
||||
@@ -36,35 +36,35 @@ interface IGroupRoute extends IRoute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExceptionHandlers();
|
||||
public function getExceptionHandlers(): array;
|
||||
|
||||
/**
|
||||
* Get domains for domain.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDomains();
|
||||
public function getDomains(): array;
|
||||
|
||||
/**
|
||||
* Set allowed domains for group.
|
||||
*
|
||||
* @param array $domains
|
||||
* @return $this
|
||||
* @return static
|
||||
*/
|
||||
public function setDomains(array $domains);
|
||||
public function setDomains(array $domains): self;
|
||||
|
||||
/**
|
||||
* Set prefix that child-routes will inherit.
|
||||
*
|
||||
* @param string $prefix
|
||||
* @return string
|
||||
* @return static
|
||||
*/
|
||||
public function setPrefix($prefix);
|
||||
public function setPrefix($prefix): self;
|
||||
|
||||
/**
|
||||
* Get prefix.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPrefix();
|
||||
public function getPrefix(): ?string;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
interface ILoadableRoute extends IRoute
|
||||
{
|
||||
@@ -14,25 +16,35 @@ interface ILoadableRoute extends IRoute
|
||||
* @param string|null $name
|
||||
* @return string
|
||||
*/
|
||||
public function findUrl($method = null, $parameters = null, $name = null);
|
||||
public function findUrl($method = null, $parameters = null, $name = null): string;
|
||||
|
||||
/**
|
||||
* Loads and renders middlewares-classes
|
||||
* Loads and renders middleware-classes
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Router $router
|
||||
*/
|
||||
public function loadMiddleware(Request $request);
|
||||
public function loadMiddleware(Request $request, Router $router): void;
|
||||
|
||||
public function getUrl();
|
||||
/**
|
||||
* Get url
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl(): string;
|
||||
|
||||
public function setUrl($url);
|
||||
/**
|
||||
* Set url
|
||||
* @param string $url
|
||||
* @return static
|
||||
*/
|
||||
public function setUrl(string $url): self;
|
||||
|
||||
/**
|
||||
* Returns the provided name for the router.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName();
|
||||
public function getName(): ?string;
|
||||
|
||||
/**
|
||||
* Check if route has given name.
|
||||
@@ -40,22 +52,22 @@ interface ILoadableRoute extends IRoute
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasName($name);
|
||||
public function hasName(string $name): bool;
|
||||
|
||||
/**
|
||||
* Sets the router name, which makes it easier to obtain the url or router at a later point.
|
||||
*
|
||||
* @param string $name
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setName($name);
|
||||
public function setName(string $name): self;
|
||||
|
||||
/**
|
||||
* Get regular expression match used for matching route (if defined).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMatch();
|
||||
public function getMatch(): ?string;
|
||||
|
||||
/**
|
||||
* Add regular expression match for the entire route.
|
||||
@@ -63,6 +75,6 @@ interface ILoadableRoute extends IRoute
|
||||
* @param string $regex
|
||||
* @return static
|
||||
*/
|
||||
public function setMatch($regex);
|
||||
public function setMatch($regex): self;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
interface IPartialGroupRoute
|
||||
{
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
interface IRoute
|
||||
{
|
||||
@@ -13,17 +14,18 @@ interface IRoute
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function matchRoute($route, Request $request);
|
||||
public function matchRoute($route, Request $request): bool;
|
||||
|
||||
/**
|
||||
* Called when route is matched.
|
||||
* Returns class to be rendered.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Router $router
|
||||
* @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException
|
||||
* @return void
|
||||
* @return string
|
||||
*/
|
||||
public function renderRoute(Request $request);
|
||||
public function renderRoute(Request $request, Router $router): ?string;
|
||||
|
||||
/**
|
||||
* Returns callback name/identifier for the current route based on the callback.
|
||||
@@ -32,50 +34,50 @@ interface IRoute
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
public function getIdentifier(): string;
|
||||
|
||||
/**
|
||||
* Set allowed request methods
|
||||
*
|
||||
* @param array $methods
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setRequestMethods(array $methods);
|
||||
public function setRequestMethods(array $methods): self;
|
||||
|
||||
/**
|
||||
* Get allowed request methods
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequestMethods();
|
||||
public function getRequestMethods(): array;
|
||||
|
||||
/**
|
||||
* @return IRoute|null
|
||||
*/
|
||||
public function getParent();
|
||||
public function getParent(): ?IRoute;
|
||||
|
||||
/**
|
||||
* Get the group for the route.
|
||||
*
|
||||
* @return IGroupRoute|null
|
||||
*/
|
||||
public function getGroup();
|
||||
public function getGroup(): ?IGroupRoute;
|
||||
|
||||
/**
|
||||
* Set group
|
||||
*
|
||||
* @param IGroupRoute $group
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setGroup(IGroupRoute $group);
|
||||
public function setGroup(IGroupRoute $group): self;
|
||||
|
||||
/**
|
||||
* Set parent route
|
||||
*
|
||||
* @param IRoute $parent
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setParent(IRoute $parent);
|
||||
public function setParent(IRoute $parent): self;
|
||||
|
||||
/**
|
||||
* Set callback
|
||||
@@ -83,44 +85,64 @@ interface IRoute
|
||||
* @param string $callback
|
||||
* @return static
|
||||
*/
|
||||
public function setCallback($callback);
|
||||
public function setCallback($callback): self;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|callable
|
||||
*/
|
||||
public function getCallback();
|
||||
|
||||
public function getMethod();
|
||||
/**
|
||||
* Return active method
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMethod(): ?string;
|
||||
|
||||
public function getClass();
|
||||
/**
|
||||
* Set active method
|
||||
*
|
||||
* @param string $method
|
||||
* @return static
|
||||
*/
|
||||
public function setMethod(string $method): self;
|
||||
|
||||
public function setMethod($method);
|
||||
/**
|
||||
* Get class
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getClass(): ?string;
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setNamespace($namespace);
|
||||
public function setNamespace(string $namespace): self;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getNamespace();
|
||||
public function getNamespace(): ?string;
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setDefaultNamespace($namespace);
|
||||
public function setDefaultNamespace($namespace): IRoute;
|
||||
|
||||
public function getDefaultNamespace();
|
||||
/**
|
||||
* Get default namespace
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDefaultNamespace(): ?string;
|
||||
|
||||
/**
|
||||
* Get parameter names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getWhere();
|
||||
public function getWhere(): array;
|
||||
|
||||
/**
|
||||
* Set parameter names.
|
||||
@@ -128,45 +150,45 @@ interface IRoute
|
||||
* @param array $options
|
||||
* @return static
|
||||
*/
|
||||
public function setWhere(array $options);
|
||||
public function setWhere(array $options): self;
|
||||
|
||||
/**
|
||||
* Get parameters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParameters();
|
||||
public function getParameters(): array;
|
||||
|
||||
/**
|
||||
* Get parameters
|
||||
*
|
||||
* @param array $parameters
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setParameters(array $parameters);
|
||||
public function setParameters(array $parameters): self;
|
||||
|
||||
/**
|
||||
* Merge with information from another route.
|
||||
*
|
||||
* @param array $settings
|
||||
* @param bool $merge
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setSettings(array $settings, $merge = false);
|
||||
public function setSettings(array $settings, bool $merge = false): self;
|
||||
|
||||
/**
|
||||
* Export route settings to array so they can be merged with another route.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray();
|
||||
public function toArray(): array;
|
||||
|
||||
/**
|
||||
* Get middlewares array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMiddlewares();
|
||||
public function getMiddlewares(): array;
|
||||
|
||||
/**
|
||||
* Set middleware class-name
|
||||
@@ -174,14 +196,14 @@ interface IRoute
|
||||
* @param string $middleware
|
||||
* @return static
|
||||
*/
|
||||
public function addMiddleware($middleware);
|
||||
public function addMiddleware($middleware): self;
|
||||
|
||||
/**
|
||||
* Set middlewares array
|
||||
*
|
||||
* @param array $middlewares
|
||||
* @return $this
|
||||
* @return static
|
||||
*/
|
||||
public function setMiddlewares(array $middlewares);
|
||||
public function setMiddlewares(array $middlewares): self;
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace Pecee\SimpleRouter\Route;
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Exceptions\HttpException;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
{
|
||||
@@ -24,32 +25,32 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* Loads and renders middlewares-classes
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Router $router
|
||||
* @throws HttpException
|
||||
*/
|
||||
public function loadMiddleware(Request $request)
|
||||
public function loadMiddleware(Request $request, Router $router): void
|
||||
{
|
||||
$max = count($this->getMiddlewares());
|
||||
$router->debug('Loading middlewares');
|
||||
|
||||
if ($max > 0) {
|
||||
foreach ($this->getMiddlewares() as $middleware) {
|
||||
|
||||
for ($i = 0; $i < $max; $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 inherit the IMiddleware interface');
|
||||
}
|
||||
|
||||
$middleware->handle($request);
|
||||
if (\is_object($middleware) === false) {
|
||||
$middleware = $this->loadClass($middleware);
|
||||
}
|
||||
|
||||
if (($middleware instanceof IMiddleware) === false) {
|
||||
throw new HttpException($middleware . ' must be inherit the IMiddleware interface');
|
||||
}
|
||||
|
||||
$router->debug('Loading middleware "%s"', \get_class($middleware));
|
||||
$middleware->handle($request);
|
||||
$router->debug('Finished loading middleware');
|
||||
}
|
||||
|
||||
$router->debug('Finished loading middlewares');
|
||||
}
|
||||
|
||||
public function matchRegex(Request $request, $url)
|
||||
public function matchRegex(Request $request, $url): ?bool
|
||||
{
|
||||
/* Match on custom defined regular expression */
|
||||
|
||||
@@ -57,7 +58,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
return null;
|
||||
}
|
||||
|
||||
return (preg_match($this->regex, $request->getHost() . $url) > 0);
|
||||
return ((bool)preg_match($this->regex, $request->getHost() . $url) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +67,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* @param string $url
|
||||
* @return static
|
||||
*/
|
||||
public function setUrl($url)
|
||||
public function setUrl(string $url): ILoadableRoute
|
||||
{
|
||||
$this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/';
|
||||
|
||||
@@ -74,7 +75,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
|
||||
$regex = sprintf(static::PARAMETERS_REGEX_FORMAT, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
|
||||
|
||||
if (preg_match_all('/' . $regex . '/u', $this->url, $matches)) {
|
||||
if ((bool)preg_match_all('/' . $regex . '/u', $this->url, $matches) !== false) {
|
||||
$this->parameters = array_fill_keys($matches[1], null);
|
||||
}
|
||||
}
|
||||
@@ -82,7 +83,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
public function getUrl(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
@@ -96,13 +97,13 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* @param string|null $name
|
||||
* @return string
|
||||
*/
|
||||
public function findUrl($method = null, $parameters = null, $name = null)
|
||||
public function findUrl($method = null, $parameters = null, $name = null): string
|
||||
{
|
||||
$url = $this->getUrl();
|
||||
|
||||
$group = $this->getGroup();
|
||||
|
||||
if ($group !== null && count($group->getDomains()) > 0) {
|
||||
if ($group !== null && \count($group->getDomains()) !== 0) {
|
||||
$url = '//' . $group->getDomains()[0] . $url;
|
||||
}
|
||||
|
||||
@@ -118,13 +119,10 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
/* Replace any {parameter} in the url with the correct value */
|
||||
|
||||
$params = $this->getParameters();
|
||||
$max = count($params) - 1;
|
||||
$keys = array_keys($params);
|
||||
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
$param = $keys[$i];
|
||||
foreach (array_keys($params) as $param) {
|
||||
|
||||
if ($parameters === '' || (is_array($parameters) && count($parameters) === 0)) {
|
||||
if ($parameters === '' || (\is_array($parameters) === true && \count($parameters) === 0)) {
|
||||
$value = '';
|
||||
} else {
|
||||
$p = (array)$parameters;
|
||||
@@ -144,8 +142,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
}
|
||||
}
|
||||
|
||||
$url = '/' . ltrim($url, '/');
|
||||
$url .= join('/', $unknownParams);
|
||||
$url = '/' . ltrim($url, '/') . implode('/', $unknownParams);
|
||||
|
||||
return rtrim($url, '/') . '/';
|
||||
}
|
||||
@@ -155,7 +152,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
@@ -166,9 +163,9 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasName($name)
|
||||
public function hasName(string $name): bool
|
||||
{
|
||||
return (strtolower($this->name) === strtolower($name));
|
||||
return strtolower($this->name) === strtolower($name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +174,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* @param string $regex
|
||||
* @return static
|
||||
*/
|
||||
public function setMatch($regex)
|
||||
public function setMatch($regex): ILoadableRoute
|
||||
{
|
||||
$this->regex = $regex;
|
||||
|
||||
@@ -189,7 +186,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMatch()
|
||||
public function getMatch(): string
|
||||
{
|
||||
return $this->regex;
|
||||
}
|
||||
@@ -202,7 +199,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* @param string|array $name
|
||||
* @return static
|
||||
*/
|
||||
public function name($name)
|
||||
public function name($name): ILoadableRoute
|
||||
{
|
||||
return $this->setName($name);
|
||||
}
|
||||
@@ -211,9 +208,9 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* Sets the router name, which makes it easier to obtain the url or router at a later point.
|
||||
*
|
||||
* @param string $name
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(string $name): ILoadableRoute
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
@@ -227,17 +224,20 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
* @param bool $merge
|
||||
* @return static
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
public function setSettings(array $values, bool $merge = false): IRoute
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,19 @@ namespace Pecee\SimpleRouter\Route;
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
|
||||
abstract class Route implements IRoute
|
||||
{
|
||||
const PARAMETERS_REGEX_FORMAT = '%s([\w]+)(\%s?)%s';
|
||||
const PARAMETERS_DEFAULT_REGEX = '[\w]+';
|
||||
protected const PARAMETERS_REGEX_FORMAT = '%s([\w]+)(\%s?)%s';
|
||||
protected const PARAMETERS_DEFAULT_REGEX = '[\w]+';
|
||||
|
||||
const REQUEST_TYPE_GET = 'get';
|
||||
const REQUEST_TYPE_POST = 'post';
|
||||
const REQUEST_TYPE_PUT = 'put';
|
||||
const REQUEST_TYPE_PATCH = 'patch';
|
||||
const REQUEST_TYPE_OPTIONS = 'options';
|
||||
const REQUEST_TYPE_DELETE = 'delete';
|
||||
public const REQUEST_TYPE_GET = 'get';
|
||||
public const REQUEST_TYPE_POST = 'post';
|
||||
public const REQUEST_TYPE_PUT = 'put';
|
||||
public const REQUEST_TYPE_PATCH = 'patch';
|
||||
public const REQUEST_TYPE_OPTIONS = 'options';
|
||||
public const REQUEST_TYPE_DELETE = 'delete';
|
||||
|
||||
public static $requestTypes = [
|
||||
self::REQUEST_TYPE_GET,
|
||||
@@ -33,7 +34,7 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $filterEmptyParams = false;
|
||||
protected $filterEmptyParams = true;
|
||||
|
||||
/**
|
||||
* Default regular expression used for parsing parameters.
|
||||
@@ -42,6 +43,7 @@ abstract class Route implements IRoute
|
||||
protected $defaultParameterRegex;
|
||||
protected $paramModifiers = '{}';
|
||||
protected $paramOptionalSymbol = '?';
|
||||
protected $urlRegex = '/^%s\/?$/u';
|
||||
protected $group;
|
||||
protected $parent;
|
||||
protected $callback;
|
||||
@@ -55,7 +57,13 @@ abstract class Route implements IRoute
|
||||
protected $originalParameters = [];
|
||||
protected $middlewares = [];
|
||||
|
||||
protected function loadClass($name)
|
||||
/**
|
||||
* Load class by name
|
||||
* @param string $name
|
||||
* @return object
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
protected function loadClass($name): object
|
||||
{
|
||||
if (class_exists($name) === false) {
|
||||
throw new NotFoundHttpException(sprintf('Class "%s" does not exist', $name), 404);
|
||||
@@ -64,20 +72,42 @@ abstract class Route implements IRoute
|
||||
return new $name();
|
||||
}
|
||||
|
||||
public function renderRoute(Request $request)
|
||||
/**
|
||||
* Render route
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Router $router
|
||||
* @return string|null
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function renderRoute(Request $request, Router $router): ?string
|
||||
{
|
||||
$router->debug('Starting rendering route');
|
||||
|
||||
$callback = $this->getCallback();
|
||||
|
||||
if ($callback === null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$router->debug('Parsing parameters');
|
||||
$parameters = $this->getParameters();
|
||||
$router->debug('Finished parsing parameters');
|
||||
|
||||
/* Filter parameters with null-value */
|
||||
if ($this->filterEmptyParams === true) {
|
||||
$parameters = array_filter($parameters, function ($var) {
|
||||
return ($var !== null);
|
||||
});
|
||||
}
|
||||
|
||||
/* Render callback function */
|
||||
if (is_callable($callback) === true) {
|
||||
if (\is_callable($callback) === true) {
|
||||
$router->debug('Executing callback');
|
||||
|
||||
/* When the callback is a function */
|
||||
return call_user_func_array($callback, $this->getParameters());
|
||||
|
||||
return \call_user_func_array($callback, $parameters);
|
||||
}
|
||||
|
||||
/* When the callback is a class + method */
|
||||
@@ -87,6 +117,7 @@ abstract class Route implements IRoute
|
||||
|
||||
$className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0];
|
||||
|
||||
$router->debug('Loading class %s', $className);
|
||||
$class = $this->loadClass($className);
|
||||
$method = $controller[1];
|
||||
|
||||
@@ -94,17 +125,9 @@ abstract class Route implements IRoute
|
||||
throw new NotFoundHttpException(sprintf('Method "%s" does not exist in class "%s"', $method, $className), 404);
|
||||
}
|
||||
|
||||
$parameters = $this->getParameters();
|
||||
$router->debug('Executing callback');
|
||||
|
||||
/* 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);
|
||||
return \call_user_func_array([$class, $method], $parameters);
|
||||
}
|
||||
|
||||
protected function parseParameters($route, $url, $parameterRegex = null)
|
||||
@@ -116,15 +139,17 @@ abstract class Route implements IRoute
|
||||
// Ensures that hostnames/domains will work with parameters
|
||||
$url = '/' . ltrim($url, '/');
|
||||
|
||||
if (preg_match_all('/' . $regex . '/u', $route, $parameters)) {
|
||||
if ((bool)preg_match_all('/' . $regex . '/u', $route, $parameters) === false) {
|
||||
$urlRegex = preg_quote($route, '/');
|
||||
} else {
|
||||
|
||||
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', rtrim($route, '/'));
|
||||
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', $route);
|
||||
|
||||
foreach ($urlParts as $key => $t) {
|
||||
|
||||
$regex = '';
|
||||
|
||||
if ($key < count($parameters[1])) {
|
||||
if ($key < \count($parameters[1])) {
|
||||
|
||||
$name = $parameters[1][$key];
|
||||
|
||||
@@ -137,39 +162,35 @@ abstract class Route implements IRoute
|
||||
if ($parameterRegex !== null) {
|
||||
$regex = $parameterRegex;
|
||||
} else {
|
||||
$regex = ($this->defaultParameterRegex === null) ? static::PARAMETERS_DEFAULT_REGEX : $this->defaultParameterRegex;
|
||||
$regex = $this->defaultParameterRegex ?? static::PARAMETERS_DEFAULT_REGEX;
|
||||
}
|
||||
}
|
||||
|
||||
$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 . '\/?$/u', $url, $matches) > 0) {
|
||||
if ((bool)preg_match(sprintf($this->urlRegex, $urlRegex), $url, $matches) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$values = [];
|
||||
$values = [];
|
||||
|
||||
if (isset($parameters[1]) === true) {
|
||||
if (isset($parameters[1]) === true) {
|
||||
|
||||
/* Only take matched parameters with name */
|
||||
foreach ($parameters[1] as $name) {
|
||||
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
|
||||
}
|
||||
/* Only take matched parameters with name */
|
||||
foreach ((array)$parameters[1] as $name) {
|
||||
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
return null;
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,9 +200,9 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier()
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
|
||||
if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
@@ -192,9 +213,9 @@ abstract class Route implements IRoute
|
||||
* Set allowed request methods
|
||||
*
|
||||
* @param array $methods
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setRequestMethods(array $methods)
|
||||
public function setRequestMethods(array $methods): IRoute
|
||||
{
|
||||
$this->requestMethods = $methods;
|
||||
|
||||
@@ -206,7 +227,7 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequestMethods()
|
||||
public function getRequestMethods(): array
|
||||
{
|
||||
return $this->requestMethods;
|
||||
}
|
||||
@@ -214,7 +235,7 @@ abstract class Route implements IRoute
|
||||
/**
|
||||
* @return IRoute|null
|
||||
*/
|
||||
public function getParent()
|
||||
public function getParent(): ?IRoute
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
@@ -224,7 +245,7 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @return IGroupRoute|null
|
||||
*/
|
||||
public function getGroup()
|
||||
public function getGroup(): ?IGroupRoute
|
||||
{
|
||||
return $this->group;
|
||||
}
|
||||
@@ -233,12 +254,15 @@ abstract class Route implements IRoute
|
||||
* Set group
|
||||
*
|
||||
* @param IGroupRoute $group
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setGroup(IGroupRoute $group)
|
||||
public function setGroup(IGroupRoute $group): IRoute
|
||||
{
|
||||
$this->group = $group;
|
||||
|
||||
/* Add/merge parent settings with child */
|
||||
$this->setSettings($group->toArray(), true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -246,9 +270,9 @@ abstract class Route implements IRoute
|
||||
* Set parent route
|
||||
*
|
||||
* @param IRoute $parent
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setParent(IRoute $parent)
|
||||
public function setParent(IRoute $parent): IRoute
|
||||
{
|
||||
$this->parent = $parent;
|
||||
|
||||
@@ -261,7 +285,7 @@ abstract class Route implements IRoute
|
||||
* @param string $callback
|
||||
* @return static
|
||||
*/
|
||||
public function setCallback($callback)
|
||||
public function setCallback($callback): IRoute
|
||||
{
|
||||
$this->callback = $callback;
|
||||
|
||||
@@ -269,16 +293,16 @@ abstract class Route implements IRoute
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|callable
|
||||
*/
|
||||
public function getCallback()
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
public function getMethod()
|
||||
public function getMethod(): ?string
|
||||
{
|
||||
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
|
||||
if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
|
||||
$tmp = explode('@', $this->callback);
|
||||
|
||||
return $tmp[1];
|
||||
@@ -287,9 +311,9 @@ abstract class Route implements IRoute
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getClass()
|
||||
public function getClass(): ?string
|
||||
{
|
||||
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
|
||||
if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
|
||||
$tmp = explode('@', $this->callback);
|
||||
|
||||
return $tmp[0];
|
||||
@@ -298,14 +322,14 @@ abstract class Route implements IRoute
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setMethod($method)
|
||||
public function setMethod(string $method): IRoute
|
||||
{
|
||||
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setClass($class)
|
||||
public function setClass(string $class): IRoute
|
||||
{
|
||||
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
|
||||
|
||||
@@ -314,9 +338,9 @@ abstract class Route implements IRoute
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
public function setNamespace(string $namespace): IRoute
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
|
||||
@@ -325,26 +349,26 @@ abstract class Route implements IRoute
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setDefaultNamespace($namespace)
|
||||
public function setDefaultNamespace($namespace): IRoute
|
||||
{
|
||||
$this->defaultNamespace = $namespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDefaultNamespace()
|
||||
public function getDefaultNamespace(): ?string
|
||||
{
|
||||
return $this->defaultNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getNamespace()
|
||||
public function getNamespace(): ?string
|
||||
{
|
||||
return ($this->namespace === null) ? $this->defaultNamespace : $this->namespace;
|
||||
return $this->namespace ?? $this->defaultNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,7 +376,7 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
public function toArray(): array
|
||||
{
|
||||
$values = [];
|
||||
|
||||
@@ -360,15 +384,15 @@ 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;
|
||||
}
|
||||
|
||||
@@ -384,32 +408,32 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @param array $values
|
||||
* @param bool $merge
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
public function setSettings(array $values, bool $merge = false): IRoute
|
||||
{
|
||||
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'])) {
|
||||
if (isset($values['defaultParameterRegex']) === true) {
|
||||
$this->setDefaultParameterRegex($values['defaultParameterRegex']);
|
||||
}
|
||||
|
||||
@@ -421,7 +445,7 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getWhere()
|
||||
public function getWhere(): array
|
||||
{
|
||||
return $this->where;
|
||||
}
|
||||
@@ -432,7 +456,7 @@ abstract class Route implements IRoute
|
||||
* @param array $options
|
||||
* @return static
|
||||
*/
|
||||
public function setWhere(array $options)
|
||||
public function setWhere(array $options): IRoute
|
||||
{
|
||||
$this->where = $options;
|
||||
|
||||
@@ -457,12 +481,12 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParameters()
|
||||
public function getParameters(): array
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -473,15 +497,15 @@ abstract class Route implements IRoute
|
||||
* Get parameters
|
||||
*
|
||||
* @param array $parameters
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setParameters(array $parameters)
|
||||
public function setParameters(array $parameters): 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;
|
||||
}
|
||||
|
||||
@@ -510,7 +534,7 @@ abstract class Route implements IRoute
|
||||
* @param IMiddleware|string $middleware
|
||||
* @return static
|
||||
*/
|
||||
public function addMiddleware($middleware)
|
||||
public function addMiddleware($middleware): IRoute
|
||||
{
|
||||
$this->middlewares[] = $middleware;
|
||||
|
||||
@@ -521,9 +545,9 @@ abstract class Route implements IRoute
|
||||
* Set middlewares array
|
||||
*
|
||||
* @param array $middlewares
|
||||
* @return $this
|
||||
* @return static
|
||||
*/
|
||||
public function setMiddlewares(array $middlewares)
|
||||
public function setMiddlewares(array $middlewares): IRoute
|
||||
{
|
||||
$this->middlewares = $middlewares;
|
||||
|
||||
@@ -531,9 +555,9 @@ abstract class Route implements IRoute
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|array
|
||||
* @return array
|
||||
*/
|
||||
public function getMiddlewares()
|
||||
public function getMiddlewares(): array
|
||||
{
|
||||
return $this->middlewares;
|
||||
}
|
||||
@@ -543,7 +567,7 @@ abstract class Route implements IRoute
|
||||
* This is used when no custom parameter regex is found.
|
||||
*
|
||||
* @param string $regex
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setDefaultParameterRegex($regex)
|
||||
{
|
||||
@@ -557,7 +581,7 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultParameterRegex()
|
||||
public function getDefaultParameterRegex(): string
|
||||
{
|
||||
return $this->defaultParameterRegex;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
@@ -23,7 +24,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasName($name)
|
||||
public function hasName(string $name): bool
|
||||
{
|
||||
if ($this->name === null) {
|
||||
return false;
|
||||
@@ -34,7 +35,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
$method = substr($name, strrpos($name, '.') + 1);
|
||||
$newName = substr($name, 0, strrpos($name, '.'));
|
||||
|
||||
if (in_array($method, $this->names, false) === true && strtolower($this->name) === strtolower($newName)) {
|
||||
if (\in_array($method, $this->names, true) === true && strtolower($this->name) === strtolower($newName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -48,7 +49,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
* @param string|null $name
|
||||
* @return string
|
||||
*/
|
||||
public function findUrl($method = null, $parameters = null, $name = null)
|
||||
public function findUrl($method = null, $parameters = null, $name = null): string
|
||||
{
|
||||
if (strpos($name, '.') !== false) {
|
||||
$found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, false);
|
||||
@@ -66,7 +67,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
foreach (static::$requestTypes as $requestType) {
|
||||
|
||||
if (stripos($method, $requestType) === 0) {
|
||||
$method = (string)substr($method, strlen($requestType));
|
||||
$method = (string)substr($method, \strlen($requestType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -76,17 +77,21 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
|
||||
$group = $this->getGroup();
|
||||
|
||||
if ($group !== null && count($group->getDomains()) > 0) {
|
||||
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)
|
||||
public function matchRoute($url, Request $request): bool
|
||||
{
|
||||
if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Match global regular-expression for route */
|
||||
$regexMatch = $this->matchRegex($request, $url);
|
||||
|
||||
@@ -97,12 +102,12 @@ 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);
|
||||
|
||||
$this->parameters = array_slice($path, 1);
|
||||
$this->parameters = \array_slice($path, 1);
|
||||
|
||||
// Set callback
|
||||
$this->setCallback($this->controller . '@' . $this->method);
|
||||
@@ -118,7 +123,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getController()
|
||||
public function getController(): string
|
||||
{
|
||||
return $this->controller;
|
||||
}
|
||||
@@ -129,7 +134,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
* @param string $controller
|
||||
* @return static
|
||||
*/
|
||||
public function setController($controller)
|
||||
public function setController(string $controller): IControllerRoute
|
||||
{
|
||||
$this->controller = $controller;
|
||||
|
||||
@@ -139,9 +144,9 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
/**
|
||||
* Return active method
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMethod()
|
||||
public function getMethod(): ?string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
@@ -152,7 +157,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
* @param string $method
|
||||
* @return static
|
||||
*/
|
||||
public function setMethod($method)
|
||||
public function setMethod(string $method): IRoute
|
||||
{
|
||||
$this->method = $method;
|
||||
|
||||
@@ -166,9 +171,9 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
* @param bool $merge
|
||||
* @return static
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
public function setSettings(array $values, bool $merge = false): IRoute
|
||||
{
|
||||
if (isset($values['names'])) {
|
||||
if (isset($values['names']) === true) {
|
||||
$this->names = $values['names'];
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function matchDomain(Request $request)
|
||||
public function matchDomain(Request $request): bool
|
||||
{
|
||||
if ($this->domains === null || count($this->domains) === 0) {
|
||||
if ($this->domains === null || \count($this->domains) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,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;
|
||||
|
||||
@@ -46,8 +46,12 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function matchRoute($url, Request $request)
|
||||
public function matchRoute($url, Request $request): bool
|
||||
{
|
||||
if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Skip if prefix doesn't match */
|
||||
if ($this->prefix !== null && stripos($url, $this->prefix) === false) {
|
||||
return false;
|
||||
@@ -60,9 +64,9 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
* Add exception handler
|
||||
*
|
||||
* @param IExceptionHandler|string $handler
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function addExceptionHandler($handler)
|
||||
public function addExceptionHandler($handler): IGroupRoute
|
||||
{
|
||||
$this->exceptionHandlers[] = $handler;
|
||||
|
||||
@@ -73,9 +77,9 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
* Set exception-handlers for group
|
||||
*
|
||||
* @param array $handlers
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setExceptionHandlers(array $handlers)
|
||||
public function setExceptionHandlers(array $handlers): IGroupRoute
|
||||
{
|
||||
$this->exceptionHandlers = $handlers;
|
||||
|
||||
@@ -87,7 +91,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExceptionHandlers()
|
||||
public function getExceptionHandlers(): array
|
||||
{
|
||||
return $this->exceptionHandlers;
|
||||
}
|
||||
@@ -97,7 +101,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDomains()
|
||||
public function getDomains(): array
|
||||
{
|
||||
return $this->domains;
|
||||
}
|
||||
@@ -106,9 +110,9 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
* Set allowed domains for group.
|
||||
*
|
||||
* @param array $domains
|
||||
* @return $this
|
||||
* @return static
|
||||
*/
|
||||
public function setDomains(array $domains)
|
||||
public function setDomains(array $domains): IGroupRoute
|
||||
{
|
||||
$this->domains = $domains;
|
||||
|
||||
@@ -119,7 +123,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
* @param string $prefix
|
||||
* @return static
|
||||
*/
|
||||
public function setPrefix($prefix)
|
||||
public function setPrefix($prefix): IGroupRoute
|
||||
{
|
||||
$this->prefix = '/' . trim($prefix, '/');
|
||||
|
||||
@@ -129,9 +133,9 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
/**
|
||||
* Set prefix that child-routes will inherit.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPrefix()
|
||||
public function getPrefix(): ?string
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
@@ -143,27 +147,30 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
* @param bool $merge
|
||||
* @return static
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
public function setSettings(array $values, bool $merge = false): IRoute
|
||||
{
|
||||
|
||||
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);
|
||||
@@ -176,7 +183,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
public function toArray(): array
|
||||
{
|
||||
$values = [];
|
||||
|
||||
@@ -188,7 +195,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,40 @@
|
||||
<?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): bool
|
||||
{
|
||||
if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasName($name)
|
||||
public function hasName(string $name): bool
|
||||
{
|
||||
if ($this->name === null) {
|
||||
return false;
|
||||
@@ -60,7 +60,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
return (strtolower($this->name) === strtolower($name));
|
||||
}
|
||||
|
||||
public function findUrl($method = null, $parameters = null, $name = null)
|
||||
public function findUrl($method = null, $parameters = null, $name = null): string
|
||||
{
|
||||
$url = array_search($name, $this->names, false);
|
||||
if ($url !== false) {
|
||||
@@ -77,8 +77,12 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
return true;
|
||||
}
|
||||
|
||||
public function matchRoute($url, Request $request)
|
||||
public function matchRoute($url, Request $request): bool
|
||||
{
|
||||
if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Match global regular-expression for route */
|
||||
$regexMatch = $this->matchRegex($request, $url);
|
||||
|
||||
@@ -110,7 +114,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
}
|
||||
|
||||
// Update
|
||||
if ($id !== null && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], false) === true) {
|
||||
if ($id !== null && \in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], true) === true) {
|
||||
return $this->call($this->methodNames['update']);
|
||||
}
|
||||
|
||||
@@ -141,7 +145,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getController()
|
||||
public function getController(): string
|
||||
{
|
||||
return $this->controller;
|
||||
}
|
||||
@@ -150,14 +154,14 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
* @param string $controller
|
||||
* @return static
|
||||
*/
|
||||
public function setController($controller)
|
||||
public function setController(string $controller): IControllerRoute
|
||||
{
|
||||
$this->controller = $controller;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
public function setName(string $name): ILoadableRoute
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
@@ -190,9 +194,9 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
/**
|
||||
* Get method names
|
||||
*
|
||||
* @return array $this
|
||||
* @return array
|
||||
*/
|
||||
public function getMethodNames()
|
||||
public function getMethodNames(): array
|
||||
{
|
||||
return $this->methodNames;
|
||||
}
|
||||
@@ -204,13 +208,13 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
* @param bool $merge
|
||||
* @return static
|
||||
*/
|
||||
public function setSettings(array $values, $merge = false)
|
||||
public function setSettings(array $values, bool $merge = false): IRoute
|
||||
{
|
||||
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'];
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,12 @@ class RouteUrl extends LoadableRoute
|
||||
$this->setCallback($callback);
|
||||
}
|
||||
|
||||
public function matchRoute($url, Request $request)
|
||||
public function matchRoute($url, Request $request): bool
|
||||
{
|
||||
if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Match global regular-expression for route */
|
||||
$regexMatch = $this->matchRegex($request, $url);
|
||||
|
||||
|
||||
+308
-162
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exceptions\InvalidArgumentException;
|
||||
use Pecee\Handlers\IExceptionHandler;
|
||||
use Pecee\Http\Middleware\BaseCsrfVerifier;
|
||||
use Pecee\Http\Request;
|
||||
@@ -10,17 +11,12 @@ 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
|
||||
{
|
||||
|
||||
/**
|
||||
* The instance of this class
|
||||
* @var static
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* Current request
|
||||
* @var Request
|
||||
@@ -70,12 +66,45 @@ class Router
|
||||
*/
|
||||
protected $exceptionHandlers;
|
||||
|
||||
/**
|
||||
* List of loaded exception that has been loaded.
|
||||
* Used to ensure that exception-handlers aren't loaded twice when rewriting route.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $loadedExceptionHandlers;
|
||||
|
||||
/**
|
||||
* Enable or disabled debugging
|
||||
* @var bool
|
||||
*/
|
||||
protected $debugEnabled = false;
|
||||
|
||||
/**
|
||||
* The start time used when debugging is enabled
|
||||
* @var float
|
||||
*/
|
||||
protected $debugStartTime;
|
||||
|
||||
/**
|
||||
* List containing all debug messages
|
||||
* @var array
|
||||
*/
|
||||
protected $debugList = [];
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function reset()
|
||||
/**
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public function reset(): void
|
||||
{
|
||||
$this->processingRoute = false;
|
||||
$this->request = new Request();
|
||||
@@ -84,6 +113,7 @@ class Router
|
||||
$this->routeStack = [];
|
||||
$this->processedRoutes = [];
|
||||
$this->exceptionHandlers = [];
|
||||
$this->loadedExceptionHandlers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,95 +121,110 @@ class Router
|
||||
* @param IRoute $route
|
||||
* @return IRoute
|
||||
*/
|
||||
public function addRoute(IRoute $route)
|
||||
public function addRoute(IRoute $route): IRoute
|
||||
{
|
||||
/*
|
||||
* If a route is currently being processed, that means that the
|
||||
* route being added are rendered from the parent routes callback,
|
||||
* so we add them to the stack instead.
|
||||
* If a route is currently being processed, that means that the route being added are rendered from the parent
|
||||
* routes callback, so we add them to the stack instead.
|
||||
*/
|
||||
if ($this->processingRoute === true) {
|
||||
$this->routeStack[] = $route;
|
||||
} else {
|
||||
$this->routes[] = $route;
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
$this->routes[] = $route;
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render and process any new routes added.
|
||||
*
|
||||
* @param IRoute $route
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
protected function renderAndProcess(IRoute $route): void
|
||||
{
|
||||
|
||||
$this->processingRoute = true;
|
||||
$route->renderRoute($this->request, $this);
|
||||
$this->processingRoute = false;
|
||||
|
||||
if (\count($this->routeStack) !== 0) {
|
||||
|
||||
/* Pop and grab the routes added when executing group callback earlier */
|
||||
$stack = $this->routeStack;
|
||||
$this->routeStack = [];
|
||||
|
||||
/* Route any routes added to the stack */
|
||||
$this->processRoutes($stack, $route);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process added routes.
|
||||
*
|
||||
* @param array $routes
|
||||
* @param IGroupRoute|null $group
|
||||
* @param IRoute|null $parent
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null)
|
||||
protected function processRoutes(array $routes, ?IGroupRoute $group = null): void
|
||||
{
|
||||
// Loop through each route-request
|
||||
$max = count($routes) - 1;
|
||||
$this->debug('Processing routes');
|
||||
|
||||
// Loop through each route-request
|
||||
$exceptionHandlers = [];
|
||||
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
|
||||
// Stop processing routes if no valid route is found.
|
||||
if ($this->request->getRewriteRoute() === null && $this->request->getUrl() === null) {
|
||||
$this->debug('Halted route-processing as no valid route was found');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$url = $this->request->getRewriteUrl() ?? $this->request->getUrl()->getPath();
|
||||
|
||||
/* @var $route IRoute */
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
foreach ($routes as $route) {
|
||||
|
||||
$route = $routes[$i];
|
||||
|
||||
/* @var $route IGroupRoute */
|
||||
if ($route instanceof IGroupRoute) {
|
||||
|
||||
$group = $route;
|
||||
|
||||
$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) {
|
||||
/** @noinspection AdditionOperationOnArraysInspection */
|
||||
$exceptionHandlers += $route->getExceptionHandlers();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
$this->debug('Processing route "%s"', \get_class($route));
|
||||
|
||||
if ($group !== null) {
|
||||
|
||||
/* Add the parent group */
|
||||
$route->setGroup($group);
|
||||
}
|
||||
|
||||
if ($parent !== null) {
|
||||
/* @var $route IGroupRoute */
|
||||
if ($route instanceof IGroupRoute) {
|
||||
|
||||
/* Add the parent route */
|
||||
$route->setParent($parent);
|
||||
if ($route->matchRoute($url, $this->request) === true) {
|
||||
|
||||
/* Add/merge parent settings with child */
|
||||
$route->setSettings($parent->toArray(), 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 === true) {
|
||||
$this->renderAndProcess($route);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($route instanceof IPartialGroupRoute === false) {
|
||||
$this->renderAndProcess($route);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($route instanceof ILoadableRoute) {
|
||||
if ($route instanceof ILoadableRoute === true) {
|
||||
|
||||
/* 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) {
|
||||
|
||||
/* Pop and grab the routes added when executing group callback earlier */
|
||||
$stack = $this->routeStack;
|
||||
$this->routeStack = [];
|
||||
|
||||
/* Route any routes added to the stack */
|
||||
$this->processRoutes($stack, $route, $group);
|
||||
}
|
||||
}
|
||||
|
||||
$this->exceptionHandlers = array_merge($exceptionHandlers, $this->exceptionHandlers);
|
||||
@@ -190,27 +235,37 @@ class Router
|
||||
* @throws NotFoundHttpException
|
||||
* @return void
|
||||
*/
|
||||
public function loadRoutes()
|
||||
public function loadRoutes(): void
|
||||
{
|
||||
$this->debug('Loading routes');
|
||||
|
||||
/* Initialize boot-managers */
|
||||
if (count($this->bootManagers) > 0) {
|
||||
|
||||
$max = count($this->bootManagers) - 1;
|
||||
|
||||
/* @var $manager IRouterBootManager */
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
$manager = $this->bootManagers[$i];
|
||||
$manager->boot($this->request);
|
||||
}
|
||||
/* @var $manager IRouterBootManager */
|
||||
foreach ($this->bootManagers as $manager) {
|
||||
$this->debug('Rendering bootmanager %s', \get_class($manager));
|
||||
$manager->boot($this->request);
|
||||
$this->debug('Finished rendering bootmanager');
|
||||
}
|
||||
|
||||
/* Loop through each route-request */
|
||||
$this->processRoutes($this->routes);
|
||||
|
||||
$this->debug('Finished loading routes');
|
||||
}
|
||||
|
||||
public function routeRequest($rewrite = false)
|
||||
/**
|
||||
* Routes the request
|
||||
*
|
||||
* @param bool $rewrite
|
||||
* @return string|null
|
||||
* @throws HttpException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function routeRequest(bool $rewrite = false): ?string
|
||||
{
|
||||
$routeNotAllowed = false;
|
||||
$this->debug('Started routing request (rewrite: %s)', $rewrite === true ? 'yes' : 'no');
|
||||
|
||||
$methodNotAllowed = false;
|
||||
|
||||
try {
|
||||
|
||||
@@ -224,52 +279,45 @@ class Router
|
||||
}
|
||||
}
|
||||
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
|
||||
|
||||
$max = count($this->processedRoutes) - 1;
|
||||
$url = $this->request->getRewriteUrl() ?? $this->request->getUrl()->getPath();
|
||||
|
||||
/* @var $route ILoadableRoute */
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
foreach ($this->processedRoutes as $key => $route) {
|
||||
|
||||
$route = $this->processedRoutes[$i];
|
||||
$this->debug('Matching route "%s"', \get_class($route));
|
||||
|
||||
/* If the route matches */
|
||||
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) {
|
||||
$routeNotAllowed = true;
|
||||
if (\count($route->getRequestMethods()) !== 0 && \in_array($this->request->getMethod(), $route->getRequestMethods(), true) === false) {
|
||||
$this->debug('Method "%s" not allowed', $this->request->getMethod());
|
||||
$methodNotAllowed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$route->loadMiddleware($this->request);
|
||||
$route->loadMiddleware($this->request, $this);
|
||||
|
||||
$rewriteRoute = $this->request->getRewriteRoute();
|
||||
|
||||
if ($rewriteRoute !== null) {
|
||||
$rewriteRoute->loadMiddleware($this->request);
|
||||
|
||||
return $rewriteRoute->renderRoute($this->request);
|
||||
}
|
||||
|
||||
/* If the request has changed */
|
||||
$rewriteUrl = $this->request->getRewriteUrl();
|
||||
|
||||
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
|
||||
unset($this->processedRoutes[$i]);
|
||||
$this->processedRoutes = array_values($this->processedRoutes);
|
||||
$this->routeRequest(true);
|
||||
|
||||
return;
|
||||
$output = $this->handleRouteRewrite($key, $url);
|
||||
if ($output !== null) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
/* Render route */
|
||||
$routeNotAllowed = false;
|
||||
$this->request->setLoadedRoute($route);
|
||||
$methodNotAllowed = false;
|
||||
|
||||
return $route->renderRoute($this->request);
|
||||
$this->request->addLoadedRoute($route);
|
||||
|
||||
break;
|
||||
$output = $route->renderRoute($this->request, $this);
|
||||
|
||||
if ($output !== null) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$output = $this->handleRouteRewrite($key, $url);
|
||||
if ($output !== null) {
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,77 +325,113 @@ class Router
|
||||
$this->handleException($e);
|
||||
}
|
||||
|
||||
if ($routeNotAllowed === true) {
|
||||
$this->handleException(new HttpException('Route or method not allowed', 403));
|
||||
if ($methodNotAllowed === true) {
|
||||
$message = sprintf('Route "%s" or method "%s" not allowed.', $this->request->getUrl()->getPath(), $this->request->getMethod());
|
||||
$this->handleException(new HttpException($message, 403));
|
||||
}
|
||||
|
||||
if ($this->request->getLoadedRoute() === null) {
|
||||
if (\count($this->request->getLoadedRoutes()) === 0) {
|
||||
|
||||
$rewriteUrl = $this->request->getRewriteUrl();
|
||||
|
||||
if ($rewriteUrl !== null) {
|
||||
$message = sprintf('Route not found: "%s" (rewrite from: "%s")', $rewriteUrl, $this->request->getUri()->getPath());
|
||||
$message = sprintf('Route not found: "%s" (rewrite from: "%s")', $rewriteUrl, $this->request->getUrl()->getPath());
|
||||
} else {
|
||||
$message = sprintf('Route not found: "%s"', $this->request->getUri()->getPath());
|
||||
$message = sprintf('Route not found: "%s"', $this->request->getUrl()->getPath());
|
||||
}
|
||||
|
||||
$this->handleException(new NotFoundHttpException($message, 404));
|
||||
$this->debug($message);
|
||||
|
||||
return $this->handleException(new NotFoundHttpException($message, 404));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle route-rewrite
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $url
|
||||
* @return string|null
|
||||
* @throws HttpException
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function handleRouteRewrite($key, string $url): ?string
|
||||
{
|
||||
/* If the request has changed */
|
||||
if ($this->request->hasRewrite() === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$route = $this->request->getRewriteRoute();
|
||||
|
||||
if ($route !== null) {
|
||||
/* Add rewrite route */
|
||||
$this->processedRoutes[] = $route;
|
||||
}
|
||||
|
||||
if ($this->request->getRewriteUrl() !== $url) {
|
||||
unset($this->processedRoutes[$key]);
|
||||
$this->request->setHasRewrite(false);
|
||||
|
||||
return $this->routeRequest(true);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Exception $e
|
||||
* @throws HttpException
|
||||
* @throws \Exception
|
||||
* @return string|null
|
||||
*/
|
||||
protected function handleException(\Exception $e)
|
||||
protected function handleException(\Exception $e): ?string
|
||||
{
|
||||
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
|
||||
|
||||
$max = count($this->exceptionHandlers);
|
||||
$this->debug('Starting exception handling for "%s"', \get_class($e));
|
||||
|
||||
/* @var $handler IExceptionHandler */
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
foreach ($this->exceptionHandlers as $key => $handler) {
|
||||
|
||||
$handler = $this->exceptionHandlers[$i];
|
||||
|
||||
if (is_object($handler) === false) {
|
||||
if (\is_object($handler) === false) {
|
||||
$handler = new $handler();
|
||||
}
|
||||
|
||||
$this->debug('Processing exception-handler "%s"', \get_class($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();
|
||||
$this->debug('Start rendering exception handler');
|
||||
$handler->handleError($this->request, $e);
|
||||
$this->debug('Finished rendering exception-handler');
|
||||
|
||||
if ($rewriteRoute !== null) {
|
||||
$rewriteRoute->loadMiddleware($this->request);
|
||||
if (isset($this->loadedExceptionHandlers[$key]) === false && $this->request->hasRewrite() === true) {
|
||||
$this->loadedExceptionHandlers[$key] = $handler;
|
||||
|
||||
return $rewriteRoute->renderRoute($this->request);
|
||||
$this->debug('Exception handler contains rewrite, reloading routes');
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
$this->debug('Finished processing');
|
||||
}
|
||||
|
||||
$this->debug('Finished exception handling - exception not handled, throwing');
|
||||
throw $e;
|
||||
}
|
||||
|
||||
public function arrayToParams(array $getParams = [], $includeEmpty = true)
|
||||
public function arrayToParams(array $getParams = [], bool $includeEmpty = true): string
|
||||
{
|
||||
if (count($getParams) > 0) {
|
||||
if (\count($getParams) !== 0) {
|
||||
|
||||
if ($includeEmpty === false) {
|
||||
$getParams = array_filter($getParams, function ($item) {
|
||||
@@ -367,49 +451,60 @@ class Router
|
||||
* @param string $name
|
||||
* @return ILoadableRoute|null
|
||||
*/
|
||||
public function findRoute($name)
|
||||
public function findRoute(string $name): ?ILoadableRoute
|
||||
{
|
||||
$max = count($this->processedRoutes) - 1;
|
||||
|
||||
$this->debug('Finding route by name "%s"', $name);
|
||||
|
||||
/* @var $route ILoadableRoute */
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
|
||||
$route = $this->processedRoutes[$i];
|
||||
foreach ($this->processedRoutes as $route) {
|
||||
|
||||
/* Check if the name matches with a name on the route. Should match either router alias or controller alias. */
|
||||
if ($route->hasName($name)) {
|
||||
if ($route->hasName($name) === true) {
|
||||
$this->debug('Found route "%s" by name "%s"', $route->getUrl(), $name);
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/* Direct match to controller */
|
||||
if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($name)) {
|
||||
$this->debug('Found route "%s" by controller "%s"', $route->getUrl(), $name);
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/* Using @ is most definitely a controller@method or alias@method */
|
||||
if (is_string($name) === true && strpos($name, '@') !== false) {
|
||||
list($controller, $method) = array_map('strtolower', explode('@', $name));
|
||||
if (\is_string($name) === true && strpos($name, '@') !== false) {
|
||||
[$controller, $method] = array_map('strtolower', explode('@', $name));
|
||||
|
||||
if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) {
|
||||
$this->debug('Found route "%s" by controller "%s" and method "%s"', $route->getUrl(), $controller, $method);
|
||||
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if callback matches (if it's not a function) */
|
||||
if (is_string($name) === true && is_string($route->getCallback()) && strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && is_callable($route->getCallback()) === false) {
|
||||
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)) {
|
||||
$this->debug('Found route "%s" by callback "%s"', $route->getUrl(), $name);
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
/* Check if the class part of the callback matches (class@method) */
|
||||
if (strtolower($name) === strtolower($route->getClass())) {
|
||||
$this->debug('Found route "%s" by class "%s"', $route->getUrl(), $name);
|
||||
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->debug('Route not found');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -428,13 +523,15 @@ class Router
|
||||
* @param string|null $name
|
||||
* @param string|array|null $parameters
|
||||
* @param array|null $getParams
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws InvalidArgumentException
|
||||
* @return string
|
||||
*/
|
||||
public function getUrl($name = null, $parameters = null, $getParams = null)
|
||||
public function getUrl(?string $name = null, $parameters = null, $getParams = null): string
|
||||
{
|
||||
if ($getParams !== null && is_array($getParams) === false) {
|
||||
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
|
||||
$this->debug('Finding url', \func_get_args());
|
||||
|
||||
if ($getParams !== null && \is_array($getParams) === false) {
|
||||
throw new InvalidArgumentException('Invalid type for getParams. Must be array or null');
|
||||
}
|
||||
|
||||
if ($name === '' && $parameters === '') {
|
||||
@@ -450,7 +547,7 @@ class Router
|
||||
|
||||
/* Return current route if no options has been specified */
|
||||
if ($name === null && $parameters === null) {
|
||||
return $this->request->getUri()->getPath() . $this->arrayToParams($getParams);
|
||||
return $this->request->getUrl()->getPath() . $this->arrayToParams($getParams);
|
||||
}
|
||||
|
||||
$loadedRoute = $this->request->getLoadedRoute();
|
||||
@@ -468,20 +565,16 @@ class Router
|
||||
}
|
||||
|
||||
/* Using @ is most definitely a controller@method or alias@method */
|
||||
if (is_string($name) === true && strpos($name, '@') !== false) {
|
||||
list($controller, $method) = explode('@', $name);
|
||||
if (\is_string($name) === true && strpos($name, '@') !== false) {
|
||||
[$controller, $method] = explode('@', $name);
|
||||
|
||||
/* Loop through all the routes to see if we can find a match */
|
||||
|
||||
$max = count($this->processedRoutes) - 1;
|
||||
|
||||
/* @var $route ILoadableRoute */
|
||||
for ($i = $max; $i >= 0; $i--) {
|
||||
|
||||
$route = $this->processedRoutes[$i];
|
||||
foreach ($this->processedRoutes as $route) {
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
@@ -494,42 +587,52 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bootmanagers
|
||||
* Get BootManagers
|
||||
* @return array
|
||||
*/
|
||||
public function getBootManagers()
|
||||
public function getBootManagers(): array
|
||||
{
|
||||
return $this->bootManagers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bootmanagers
|
||||
* Set BootManagers
|
||||
* @param array $bootManagers
|
||||
*/
|
||||
public function setBootManagers(array $bootManagers)
|
||||
public function setBootManagers(array $bootManagers): void
|
||||
{
|
||||
$this->bootManagers = $bootManagers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bootmanager
|
||||
* Add BootManager
|
||||
* @param IRouterBootManager $bootManager
|
||||
*/
|
||||
public function addBootManager(IRouterBootManager $bootManager)
|
||||
public function addBootManager(IRouterBootManager $bootManager): void
|
||||
{
|
||||
$this->bootManagers[] = $bootManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get routes that has been processed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRoutes()
|
||||
public function getProcessedRoutes(): array
|
||||
{
|
||||
return $this->processedRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRoutes(): array
|
||||
{
|
||||
return $this->routes;
|
||||
}
|
||||
@@ -538,9 +641,9 @@ class Router
|
||||
* Set routes
|
||||
*
|
||||
* @param array $routes
|
||||
* @return static $this
|
||||
* @return static
|
||||
*/
|
||||
public function setRoutes(array $routes)
|
||||
public function setRoutes(array $routes): self
|
||||
{
|
||||
$this->routes = $routes;
|
||||
|
||||
@@ -552,7 +655,7 @@ class Router
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest()
|
||||
public function getRequest(): Request
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
@@ -561,7 +664,7 @@ class Router
|
||||
* Get csrf verifier class
|
||||
* @return BaseCsrfVerifier
|
||||
*/
|
||||
public function getCsrfVerifier()
|
||||
public function getCsrfVerifier(): ?BaseCsrfVerifier
|
||||
{
|
||||
return $this->csrfVerifier;
|
||||
}
|
||||
@@ -579,4 +682,47 @@ class Router
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new debug message
|
||||
* @param string $message
|
||||
* @param array $args
|
||||
*/
|
||||
public function debug(string $message, ...$args): void
|
||||
{
|
||||
if ($this->debugEnabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$this->debugList[] = [
|
||||
'message' => vsprintf($message, $args),
|
||||
'time' => number_format(microtime(true) - $this->debugStartTime, 10),
|
||||
'trace' => end($trace),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disables debugging
|
||||
*
|
||||
* @param bool $boolean
|
||||
*/
|
||||
public function setDebugEnabled(bool $boolean): void
|
||||
{
|
||||
if ($boolean === true) {
|
||||
$this->debugStartTime = microtime(true);
|
||||
}
|
||||
|
||||
$this->debugEnabled = $boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list containing all debug messages.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDebugLog(): array
|
||||
{
|
||||
return $this->debugList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,14 +10,18 @@
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exceptions\InvalidArgumentException;
|
||||
use Pecee\Handlers\CallbackExceptionHandler;
|
||||
use Pecee\Http\Middleware\BaseCsrfVerifier;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\Http\Response;
|
||||
use Pecee\SimpleRouter\Exceptions\HttpException;
|
||||
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
|
||||
use Pecee\SimpleRouter\Route\IGroupRoute;
|
||||
use Pecee\SimpleRouter\Route\IPartialGroupRoute;
|
||||
use Pecee\SimpleRouter\Route\IRoute;
|
||||
use Pecee\SimpleRouter\Route\RouteController;
|
||||
use Pecee\SimpleRouter\Route\RouteGroup;
|
||||
use Pecee\SimpleRouter\Route\RoutePartialGroup;
|
||||
use Pecee\SimpleRouter\Route\RouteResource;
|
||||
use Pecee\SimpleRouter\Route\RouteUrl;
|
||||
|
||||
@@ -25,7 +29,7 @@ class SimpleRouter
|
||||
{
|
||||
/**
|
||||
* Default namespace added to all routes
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $defaultNamespace;
|
||||
|
||||
@@ -42,22 +46,76 @@ class SimpleRouter
|
||||
protected static $router;
|
||||
|
||||
/**
|
||||
* Start/route request
|
||||
* Start routing
|
||||
*
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
* @throws HttpException
|
||||
* @throws NotFoundHttpException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function start()
|
||||
public static function start(): void
|
||||
{
|
||||
echo static::router()->routeRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the routing an return array with debugging-information
|
||||
*
|
||||
* @return array
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function startDebug(): array
|
||||
{
|
||||
$routerOutput = null;
|
||||
|
||||
try {
|
||||
ob_start();
|
||||
static::router()->setDebugEnabled(true);
|
||||
static::start();
|
||||
$routerOutput = ob_get_contents();
|
||||
ob_end_clean();
|
||||
} catch (\Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
// Try to parse library version
|
||||
$composerFile = \dirname(__DIR__, 3) . '/composer.lock';
|
||||
$version = false;
|
||||
|
||||
if (is_file($composerFile) === true) {
|
||||
$composerInfo = json_decode(file_get_contents($composerFile), true);
|
||||
|
||||
if (isset($composerInfo['packages']) === true && \is_array($composerInfo['packages']) === true) {
|
||||
foreach ($composerInfo['packages'] as $package) {
|
||||
if (isset($package['name']) === true && strtolower($package['name']) === 'pecee/simple-router') {
|
||||
$version = $package['version'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'url' => static::request()->getUrl(),
|
||||
'method' => static::request()->getMethod(),
|
||||
'host' => static::request()->getHost(),
|
||||
'loaded_routes' => static::request()->getLoadedRoutes(),
|
||||
'all_routes' => static::router()->getRoutes(),
|
||||
'boot_managers' => static::router()->getBootManagers(),
|
||||
'csrf_verifier' => static::router()->getCsrfVerifier(),
|
||||
'log' => static::router()->getDebugLog(),
|
||||
'router_output' => $routerOutput,
|
||||
'library_version' => $version,
|
||||
'php_version' => PHP_VERSION,
|
||||
'server_params' => static::request()->getHeaders(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default namespace which will be prepended to all routes.
|
||||
*
|
||||
* @param string $defaultNamespace
|
||||
*/
|
||||
public static function setDefaultNamespace($defaultNamespace)
|
||||
public static function setDefaultNamespace(string $defaultNamespace): void
|
||||
{
|
||||
static::$defaultNamespace = $defaultNamespace;
|
||||
}
|
||||
@@ -66,8 +124,9 @@ class SimpleRouter
|
||||
* Base CSRF verifier
|
||||
*
|
||||
* @param BaseCsrfVerifier $baseCsrfVerifier
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier)
|
||||
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier): void
|
||||
{
|
||||
static::router()->setCsrfVerifier($baseCsrfVerifier);
|
||||
}
|
||||
@@ -77,8 +136,9 @@ class SimpleRouter
|
||||
* Perfect if you want to load pretty-urls from a file or database.
|
||||
*
|
||||
* @param IRouterBootManager $bootManager
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function addBootManager(IRouterBootManager $bootManager)
|
||||
public static function addBootManager(IRouterBootManager $bootManager): void
|
||||
{
|
||||
static::router()->addBootManager($bootManager);
|
||||
}
|
||||
@@ -89,9 +149,11 @@ class SimpleRouter
|
||||
* @param string $url
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
*
|
||||
* @return RouteUrl
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function get($url, $callback, array $settings = null)
|
||||
public static function get(string $url, $callback, array $settings = null): IRoute
|
||||
{
|
||||
return static::match(['get'], $url, $callback, $settings);
|
||||
}
|
||||
@@ -103,8 +165,9 @@ class SimpleRouter
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function post($url, $callback, array $settings = null)
|
||||
public static function post(string $url, $callback, array $settings = null): IRoute
|
||||
{
|
||||
return static::match(['post'], $url, $callback, $settings);
|
||||
}
|
||||
@@ -116,8 +179,9 @@ class SimpleRouter
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function put($url, $callback, array $settings = null)
|
||||
public static function put(string $url, $callback, array $settings = null): IRoute
|
||||
{
|
||||
return static::match(['put'], $url, $callback, $settings);
|
||||
}
|
||||
@@ -129,8 +193,9 @@ class SimpleRouter
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function patch($url, $callback, array $settings = null)
|
||||
public static function patch(string $url, $callback, array $settings = null): IRoute
|
||||
{
|
||||
return static::match(['patch'], $url, $callback, $settings);
|
||||
}
|
||||
@@ -142,8 +207,9 @@ class SimpleRouter
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function options($url, $callback, array $settings = null)
|
||||
public static function options(string $url, $callback, array $settings = null): IRoute
|
||||
{
|
||||
return static::match(['options'], $url, $callback, $settings);
|
||||
}
|
||||
@@ -155,8 +221,9 @@ class SimpleRouter
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function delete($url, $callback, array $settings = null)
|
||||
public static function delete(string $url, $callback, array $settings = null): IRoute
|
||||
{
|
||||
return static::match(['delete'], $url, $callback, $settings);
|
||||
}
|
||||
@@ -166,19 +233,48 @@ class SimpleRouter
|
||||
*
|
||||
* @param array $settings
|
||||
* @param \Closure $callback
|
||||
* @throws \InvalidArgumentException
|
||||
* @return RouteGroup
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function group(array $settings = [], \Closure $callback)
|
||||
public static function group(array $settings = [], \Closure $callback): IGroupRoute
|
||||
{
|
||||
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);
|
||||
|
||||
if (is_callable($callback) === false) {
|
||||
throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported');
|
||||
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 \Closure $callback
|
||||
* @param array $settings
|
||||
* @return RoutePartialGroup
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function partialGroup(string $url, \Closure $callback, array $settings = []): IPartialGroupRoute
|
||||
{
|
||||
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;
|
||||
@@ -192,8 +288,9 @@ class SimpleRouter
|
||||
* @param array|null $settings
|
||||
* @see SimpleRouter::form
|
||||
* @return RouteUrl
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function basic($url, $callback, array $settings = null)
|
||||
public static function basic(string $url, $callback, array $settings = null): IRoute
|
||||
{
|
||||
return static::match(['get', 'post'], $url, $callback, $settings);
|
||||
}
|
||||
@@ -207,8 +304,9 @@ class SimpleRouter
|
||||
* @param array|null $settings
|
||||
* @see SimpleRouter::form
|
||||
* @return RouteUrl
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function form($url, $callback, array $settings = null)
|
||||
public static function form(string $url, $callback, array $settings = null): IRoute
|
||||
{
|
||||
return static::match(['get', 'post'], $url, $callback, $settings);
|
||||
}
|
||||
@@ -221,8 +319,9 @@ class SimpleRouter
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl|IRoute
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function match(array $requestMethods, $url, $callback, array $settings = null)
|
||||
public static function match(array $requestMethods, string $url, $callback, array $settings = null)
|
||||
{
|
||||
$route = new RouteUrl($url, $callback);
|
||||
$route->setRequestMethods($requestMethods);
|
||||
@@ -244,8 +343,9 @@ class SimpleRouter
|
||||
* @param string|\Closure $callback
|
||||
* @param array|null $settings
|
||||
* @return RouteUrl|IRoute
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function all($url, $callback, array $settings = null)
|
||||
public static function all(string $url, $callback, array $settings = null)
|
||||
{
|
||||
$route = new RouteUrl($url, $callback);
|
||||
$route = static::addDefaultNamespace($route);
|
||||
@@ -266,8 +366,9 @@ class SimpleRouter
|
||||
* @param string $controller
|
||||
* @param array|null $settings
|
||||
* @return RouteController|IRoute
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function controller($url, $controller, array $settings = null)
|
||||
public static function controller(string $url, $controller, array $settings = null)
|
||||
{
|
||||
$route = new RouteController($url, $controller);
|
||||
$route = static::addDefaultNamespace($route);
|
||||
@@ -288,8 +389,9 @@ class SimpleRouter
|
||||
* @param string $controller
|
||||
* @param array|null $settings
|
||||
* @return RouteResource|IRoute
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function resource($url, $controller, array $settings = null)
|
||||
public static function resource(string $url, $controller, array $settings = null)
|
||||
{
|
||||
$route = new RouteResource($url, $controller);
|
||||
$route = static::addDefaultNamespace($route);
|
||||
@@ -308,8 +410,9 @@ class SimpleRouter
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return CallbackExceptionHandler $callbackHandler
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function error(\Closure $callback)
|
||||
public static function error(\Closure $callback): CallbackExceptionHandler
|
||||
{
|
||||
$routes = static::router()->getRoutes();
|
||||
|
||||
@@ -340,10 +443,11 @@ class SimpleRouter
|
||||
* @param string|null $name
|
||||
* @param string|array|null $parameters
|
||||
* @param array|null $getParams
|
||||
* @throws \Exception
|
||||
* @throws \Pecee\Exceptions\InvalidArgumentException
|
||||
* @return string
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function getUrl($name = null, $parameters = null, $getParams = null)
|
||||
public static function getUrl(?string $name = null, $parameters = null, $getParams = null): string
|
||||
{
|
||||
return static::router()->getUrl($name, $parameters, $getParams);
|
||||
}
|
||||
@@ -352,8 +456,9 @@ class SimpleRouter
|
||||
* Get the request
|
||||
*
|
||||
* @return \Pecee\Http\Request
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function request()
|
||||
public static function request(): Request
|
||||
{
|
||||
return static::router()->getRequest();
|
||||
}
|
||||
@@ -362,8 +467,9 @@ class SimpleRouter
|
||||
* Get the response object
|
||||
*
|
||||
* @return Response
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function response()
|
||||
public static function response(): Response
|
||||
{
|
||||
if (static::$response === null) {
|
||||
static::$response = new Response(static::request());
|
||||
@@ -376,8 +482,9 @@ class SimpleRouter
|
||||
* Returns the router instance
|
||||
*
|
||||
* @return Router
|
||||
* @throws \Pecee\Http\Exceptions\MalformedUrlException
|
||||
*/
|
||||
public static function router()
|
||||
public static function router(): Router
|
||||
{
|
||||
if (static::$router === null) {
|
||||
static::$router = new Router();
|
||||
@@ -392,14 +499,14 @@ class SimpleRouter
|
||||
* @param IRoute $route
|
||||
* @return IRoute
|
||||
*/
|
||||
protected static function addDefaultNamespace(IRoute $route)
|
||||
public static function addDefaultNamespace(IRoute $route): IRoute
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -419,9 +526,9 @@ class SimpleRouter
|
||||
|
||||
/**
|
||||
* Get default namespace
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getDefaultNamespace()
|
||||
public static function getDefaultNamespace(): ?string
|
||||
{
|
||||
return static::$defaultNamespace;
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
class ResourceController implements \Pecee\Controllers\IResourceController
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
echo 'index';
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
echo 'show ' . $id;
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
echo 'store';
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
echo 'create';
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
echo 'edit ' . $id;
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
echo 'update ' . $id;
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
echo 'destroy ' . $id;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Exceptions/ResponseException.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandlerThird.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
|
||||
class RouteRewriteTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* Redirects to another route through 3 exception handlers.
|
||||
*
|
||||
* You will see "ExceptionHandler 1 loaded" 2 times. This happen because
|
||||
* the exceptionhandler is asking the router to reload.
|
||||
*
|
||||
* That means that the exceptionhandler is loaded again, but this time
|
||||
* the router ignores the same rewrite-route to avoid loop - loads
|
||||
* the second which have same behavior and is also ignored before
|
||||
* throwing the final Exception in ExceptionHandler 3.
|
||||
*
|
||||
* So this tests:
|
||||
* 1. If ExceptionHandlers loads
|
||||
* 2. If ExceptionHandlers load in the correct order
|
||||
* 3. If ExceptionHandlers can rewrite the page on error
|
||||
* 4. If the router can avoid redirect-loop due to developer has started loop.
|
||||
* 5. And finally if we reaches the last exception-handler and that the correct
|
||||
* exception-type is being thrown.
|
||||
*/
|
||||
public function testExceptionHandlerRewrite()
|
||||
{
|
||||
global $stack;
|
||||
$stack = [];
|
||||
|
||||
TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () {
|
||||
|
||||
TestRouter::group(['exceptionHandler' => ExceptionHandlerThird::class], function () {
|
||||
|
||||
TestRouter::get('/my-path', 'DummyController@method1');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
TestRouter::debug('/my-non-existing-path', 'get');
|
||||
} catch (\ResponseException $e) {
|
||||
|
||||
}
|
||||
|
||||
$expectedStack = [
|
||||
ExceptionHandlerFirst::class,
|
||||
ExceptionHandlerSecond::class,
|
||||
ExceptionHandlerThird::class,
|
||||
];
|
||||
|
||||
$this->assertEquals($expectedStack, $stack);
|
||||
|
||||
}
|
||||
|
||||
public function testRewriteExceptionMessage()
|
||||
{
|
||||
$this->setExpectedException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class);
|
||||
|
||||
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) {
|
||||
|
||||
if (strtolower($request->getUri()->getPath()) == '/my/test') {
|
||||
$request->setRewriteUrl('/another-non-existing');
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
TestRouter::debug('/my/test', 'get');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
require_once 'Exceptions/MiddlewareLoadedException.php';
|
||||
require_once 'Exception/MiddlewareLoadedException.php';
|
||||
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class DummyMiddleware implements \Pecee\Http\Middleware\IMiddleware
|
||||
{
|
||||
public function handle(Request $request)
|
||||
public function handle(Request $request) : void
|
||||
{
|
||||
throw new MiddlewareLoadedException('Middleware loaded!');
|
||||
}
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
class ExceptionHandlerException extends \Exception
|
||||
{
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
class ExceptionHandler implements \Pecee\Handlers\IExceptionHandler
|
||||
{
|
||||
public function handleError(\Pecee\Http\Request $request, \Exception $error)
|
||||
public function handleError(\Pecee\Http\Request $request, \Exception $error) : void
|
||||
{
|
||||
echo $error->getMessage();
|
||||
}
|
||||
+2
-3
@@ -2,13 +2,12 @@
|
||||
|
||||
class ExceptionHandlerFirst implements \Pecee\Handlers\IExceptionHandler
|
||||
{
|
||||
public function handleError(\Pecee\Http\Request $request, \Exception $error)
|
||||
public function handleError(\Pecee\Http\Request $request, \Exception $error) : void
|
||||
{
|
||||
global $stack;
|
||||
$stack[] = static::class;
|
||||
|
||||
$request->setUri(new \Pecee\Http\Uri('/'));
|
||||
return $request;
|
||||
$request->setUrl('/');
|
||||
}
|
||||
|
||||
}
|
||||
+2
-3
@@ -2,13 +2,12 @@
|
||||
|
||||
class ExceptionHandlerSecond implements \Pecee\Handlers\IExceptionHandler
|
||||
{
|
||||
public function handleError(\Pecee\Http\Request $request, \Exception $error)
|
||||
public function handleError(\Pecee\Http\Request $request, \Exception $error) : void
|
||||
{
|
||||
global $stack;
|
||||
$stack[] = static::class;
|
||||
|
||||
$request->setUri(new \Pecee\Http\Uri('/'));
|
||||
return $request;
|
||||
$request->setUrl('/');
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
class ExceptionHandlerThird implements \Pecee\Handlers\IExceptionHandler
|
||||
{
|
||||
public function handleError(\Pecee\Http\Request $request, \Exception $error)
|
||||
public function handleError(\Pecee\Http\Request $request, \Exception $error) : void
|
||||
{
|
||||
global $stack;
|
||||
$stack[] = static::class;
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class RewriteMiddleware implements IMiddleware {
|
||||
|
||||
public function handle(Request $request) : void {
|
||||
|
||||
$request->setRewriteCallback(function() {
|
||||
return 'ok';
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
class ResourceController implements \Pecee\Controllers\IResourceController
|
||||
{
|
||||
|
||||
public function index() : ?string
|
||||
{
|
||||
echo 'index';
|
||||
return null;
|
||||
}
|
||||
|
||||
public function show($id) : ?string
|
||||
{
|
||||
echo 'show ' . $id;
|
||||
return null;
|
||||
}
|
||||
|
||||
public function store() : ?string
|
||||
{
|
||||
echo 'store';
|
||||
return null;
|
||||
}
|
||||
|
||||
public function create() : ?string
|
||||
{
|
||||
echo 'create';
|
||||
return null;
|
||||
}
|
||||
|
||||
public function edit($id) : ?string
|
||||
{
|
||||
echo 'edit ' . $id;
|
||||
return null;
|
||||
}
|
||||
|
||||
public function update($id) : ?string
|
||||
{
|
||||
echo 'update ' . $id;
|
||||
return null;
|
||||
}
|
||||
|
||||
public function destroy($id) : ?string
|
||||
{
|
||||
echo 'destroy ' . $id;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
|
||||
class GroupTest extends PHPUnit_Framework_TestCase
|
||||
class GroupTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected $result;
|
||||
|
||||
@@ -37,6 +36,7 @@ class GroupTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
TestRouter::debug('/api/v1/test', 'get');
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testMultipleRoutes()
|
||||
@@ -61,6 +61,8 @@ class GroupTest extends PHPUnit_Framework_TestCase
|
||||
});
|
||||
|
||||
TestRouter::debug('/my/match', 'get');
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testUrls()
|
||||
@@ -3,13 +3,12 @@
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandler.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
|
||||
class MiddlewareTest extends PHPUnit_Framework_TestCase
|
||||
class MiddlewareTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testMiddlewareFound()
|
||||
{
|
||||
$this->setExpectedException(MiddlewareLoadedException::class);
|
||||
$this->expectException(MiddlewareLoadedException::class);
|
||||
|
||||
TestRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
|
||||
TestRouter::get('/my/test/url', 'DummyController@method1', ['middleware' => 'DummyMiddleware']);
|
||||
@@ -29,6 +28,8 @@ class MiddlewareTest extends PHPUnit_Framework_TestCase
|
||||
TestRouter::get('/my/test/url', 'DummyController@method1');
|
||||
|
||||
TestRouter::debug('/my/test/url', 'get');
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
}
|
||||
+5
-4
@@ -2,15 +2,14 @@
|
||||
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Exceptions/ExceptionHandlerException.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
require_once 'Dummy/Exception/ExceptionHandlerException.php';
|
||||
|
||||
class RouterCallbackExceptionHandlerTest extends PHPUnit_Framework_TestCase
|
||||
class RouterCallbackExceptionHandlerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
|
||||
public function testCallbackExceptionHandler()
|
||||
{
|
||||
$this->setExpectedException(ExceptionHandlerException::class);
|
||||
$this->expectException(ExceptionHandlerException::class);
|
||||
|
||||
// Match normal route on alias
|
||||
TestRouter::get('/my-new-url', 'DummyController@method2');
|
||||
@@ -22,6 +21,8 @@ class RouterCallbackExceptionHandlerTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
TestRouter::debugNoReset('/404-url', 'get');
|
||||
TestRouter::router()->reset();
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
<?php
|
||||
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
|
||||
class RouterControllerTest extends PHPUnit_Framework_TestCase
|
||||
class RouterControllerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
|
||||
public function testGet()
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandler.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,9 +1,8 @@
|
||||
<?php
|
||||
|
||||
require_once 'Dummy/ResourceController.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
|
||||
class RouterResourceTest extends PHPUnit_Framework_TestCase
|
||||
class RouterResourceTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
|
||||
public function testResourceStore()
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Exception/ResponseException.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandlerThird.php';
|
||||
require_once 'Dummy/Middleware/RewriteMiddleware.php';
|
||||
|
||||
class RouteRewriteTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* Redirects to another route through 3 exception handlers.
|
||||
*
|
||||
* You will see "ExceptionHandler 1 loaded" 2 times. This happen because
|
||||
* the exceptionhandler is asking the router to reload.
|
||||
*
|
||||
* That means that the exceptionhandler is loaded again, but this time
|
||||
* the router ignores the same rewrite-route to avoid loop - loads
|
||||
* the second which have same behavior and is also ignored before
|
||||
* throwing the final Exception in ExceptionHandler 3.
|
||||
*
|
||||
* So this tests:
|
||||
* 1. If ExceptionHandlers loads
|
||||
* 2. If ExceptionHandlers load in the correct order
|
||||
* 3. If ExceptionHandlers can rewrite the page on error
|
||||
* 4. If the router can avoid redirect-loop due to developer has started loop.
|
||||
* 5. And finally if we reaches the last exception-handler and that the correct
|
||||
* exception-type is being thrown.
|
||||
*/
|
||||
public function testExceptionHandlerRewrite()
|
||||
{
|
||||
global $stack;
|
||||
$stack = [];
|
||||
|
||||
TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () use ($stack) {
|
||||
|
||||
TestRouter::group(['exceptionHandler' => ExceptionHandlerThird::class], function () use ($stack) {
|
||||
|
||||
TestRouter::get('/my-path', 'DummyController@method1');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
TestRouter::debug('/my-non-existing-path', 'get');
|
||||
} catch (\ResponseException $e) {
|
||||
|
||||
}
|
||||
|
||||
$expectedStack = [
|
||||
ExceptionHandlerFirst::class,
|
||||
ExceptionHandlerSecond::class,
|
||||
ExceptionHandlerThird::class,
|
||||
];
|
||||
|
||||
$this->assertEquals($expectedStack, $stack);
|
||||
|
||||
}
|
||||
|
||||
public function testRewriteExceptionMessage()
|
||||
{
|
||||
$this->expectException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class);
|
||||
|
||||
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) {
|
||||
|
||||
if (strtolower($request->getUrl()->getPath()) === '/my/test/') {
|
||||
$request->setRewriteUrl('/another-non-existing');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
TestRouter::debug('/my/test', 'get');
|
||||
}
|
||||
|
||||
public function testRewriteUrlFromRoute()
|
||||
{
|
||||
|
||||
TestRouter::get('/old', function () {
|
||||
TestRouter::request()->setRewriteUrl('/new');
|
||||
});
|
||||
|
||||
TestRouter::get('/new', function () {
|
||||
echo 'ok';
|
||||
});
|
||||
|
||||
TestRouter::get('/new1', function () {
|
||||
echo 'ok';
|
||||
});
|
||||
|
||||
TestRouter::get('/new2', function () {
|
||||
echo 'ok';
|
||||
});
|
||||
|
||||
$output = TestRouter::debugOutput('/old');
|
||||
|
||||
$this->assertEquals('ok', $output);
|
||||
|
||||
}
|
||||
|
||||
public function testRewriteCallbackFromRoute()
|
||||
{
|
||||
|
||||
TestRouter::get('/old', function () {
|
||||
TestRouter::request()->setRewriteUrl('/new');
|
||||
});
|
||||
|
||||
TestRouter::get('/new', function () {
|
||||
return 'ok';
|
||||
});
|
||||
|
||||
TestRouter::get('/new1', function () {
|
||||
return 'fail';
|
||||
});
|
||||
|
||||
TestRouter::get('/new/2', function () {
|
||||
return 'fail';
|
||||
});
|
||||
|
||||
$output = TestRouter::debugOutput('/old');
|
||||
|
||||
TestRouter::router()->reset();
|
||||
|
||||
$this->assertEquals('ok', $output);
|
||||
|
||||
}
|
||||
|
||||
public function testRewriteRouteFromRoute()
|
||||
{
|
||||
|
||||
TestRouter::get('/match', function () {
|
||||
TestRouter::request()->setRewriteRoute(new \Pecee\SimpleRouter\Route\RouteUrl('/match', function () {
|
||||
return 'ok';
|
||||
}));
|
||||
});
|
||||
|
||||
TestRouter::get('/old1', function () {
|
||||
return 'fail';
|
||||
});
|
||||
|
||||
TestRouter::get('/old/2', function () {
|
||||
return 'fail';
|
||||
});
|
||||
|
||||
TestRouter::get('/new2', function () {
|
||||
return 'fail';
|
||||
});
|
||||
|
||||
$output = TestRouter::debugOutput('/match');
|
||||
|
||||
TestRouter::router()->reset();
|
||||
|
||||
$this->assertEquals('ok', $output);
|
||||
|
||||
}
|
||||
|
||||
public function testMiddlewareRewrite()
|
||||
{
|
||||
|
||||
TestRouter::group(['middleware' => 'RewriteMiddleware'], function () {
|
||||
TestRouter::get('/', function () {
|
||||
return 'fail';
|
||||
});
|
||||
|
||||
TestRouter::get('no/match', function () {
|
||||
return 'fail';
|
||||
});
|
||||
});
|
||||
|
||||
$output = TestRouter::debugOutput('/');
|
||||
|
||||
$this->assertEquals('ok', $output);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Exceptions/ExceptionHandlerException.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
require_once 'Dummy/Exception/ExceptionHandlerException.php';
|
||||
|
||||
class RouterRouteTest extends PHPUnit_Framework_TestCase
|
||||
class RouterRouteTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected $result = false;
|
||||
|
||||
@@ -27,7 +26,7 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->setExpectedException('\Pecee\SimpleRouter\Exceptions\NotFoundHttpException');
|
||||
$this->expectException('\Pecee\SimpleRouter\Exceptions\NotFoundHttpException');
|
||||
TestRouter::get('/non-existing-path', 'DummyController@method1');
|
||||
TestRouter::debug('/test-param1-param2', 'post');
|
||||
}
|
||||
@@ -36,24 +35,32 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
TestRouter::get('/my/test/url', 'DummyController@method1');
|
||||
TestRouter::debug('/my/test/url', 'get');
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testPost()
|
||||
{
|
||||
TestRouter::post('/my/test/url', 'DummyController@method1');
|
||||
TestRouter::debug('/my/test/url', 'post');
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testPut()
|
||||
{
|
||||
TestRouter::put('/my/test/url', 'DummyController@method1');
|
||||
TestRouter::debug('/my/test/url', 'put');
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
{
|
||||
TestRouter::delete('/my/test/url', 'DummyController@method1');
|
||||
TestRouter::debug('/my/test/url', 'delete');
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testMethodNotAllowed()
|
||||
@@ -123,6 +130,22 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
TestRouter::get('/my/{path}', 'DummyController@method1')->where(['path' => '[a-zA-Z\-]+']);
|
||||
TestRouter::debug('/my/custom-path', 'get');
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testParameterDefaultValue() {
|
||||
|
||||
$defaultVariable = null;
|
||||
|
||||
TestRouter::get('/my/{path?}', function($path = 'working') use(&$defaultVariable) {
|
||||
$defaultVariable = $path;
|
||||
});
|
||||
|
||||
TestRouter::debug('/my/');
|
||||
|
||||
$this->assertEquals('working', $defaultVariable);
|
||||
|
||||
}
|
||||
|
||||
public function testDefaultParameterRegex()
|
||||
@@ -3,9 +3,8 @@
|
||||
require_once 'Dummy/DummyMiddleware.php';
|
||||
require_once 'Dummy/DummyController.php';
|
||||
require_once 'Dummy/Handler/ExceptionHandler.php';
|
||||
require_once 'Helpers/TestRouter.php';
|
||||
|
||||
class RouterUrlTest extends PHPUnit_Framework_TestCase
|
||||
class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
|
||||
public function testIssue253()
|
||||
@@ -32,21 +31,22 @@ class RouterUrlTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
// Test spanish characters
|
||||
TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']);
|
||||
TestRouter::get('/test/{param}', '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());
|
||||
|
||||
TestRouter::debugNoReset('/test/Dermatología');
|
||||
$parameters = TestRouter::request()->getLoadedRoute()->getParameters();
|
||||
|
||||
$this->assertEquals('Dermatología', $parameters['param']);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
class TestRouter extends \Pecee\SimpleRouter\SimpleRouter
|
||||
{
|
||||
|
||||
public static function debugNoReset($testUri, $testMethod = 'get')
|
||||
public static function debugNoReset($testUrl, $testMethod = 'get')
|
||||
{
|
||||
static::request()->setUri(new \Pecee\Http\Uri($testUri));
|
||||
static::request()->setUrl($testUrl);
|
||||
static::request()->setMethod($testMethod);
|
||||
|
||||
static::start();
|
||||
}
|
||||
|
||||
public static function debug($testUri, $testMethod = 'get')
|
||||
public static function debug($testUrl, $testMethod = 'get')
|
||||
{
|
||||
try {
|
||||
static::debugNoReset($testUri, $testMethod);
|
||||
static::debugNoReset($testUrl, $testMethod);
|
||||
} catch(\Exception $e) {
|
||||
static::router()->reset();
|
||||
throw $e;
|
||||
@@ -24,13 +24,13 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter
|
||||
|
||||
}
|
||||
|
||||
public static function debugOutput($testUri, $testMethod = 'get')
|
||||
public static function debugOutput($testUrl, $testMethod = 'get')
|
||||
{
|
||||
$response = null;
|
||||
|
||||
// Route request
|
||||
ob_start();
|
||||
static::debug($testUri, $testMethod);
|
||||
static::debug($testUrl, $testMethod);
|
||||
$response = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
require_once 'TestRouter.php';
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
use \Pecee\SimpleRouter\SimpleRouter;
|
||||
|
||||
SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
|
||||
$debugInfo = SimpleRouter::startDebug();
|
||||
echo sprintf('<pre>%s</pre>', var_export($debugInfo, true));
|
||||
exit;
|
||||
Reference in New Issue
Block a user