mirror of
https://github.com/skipperbent/simple-php-router.git
synced 2026-06-17 16:57:53 +00:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0634ba79dc | |||
| 3534233a76 | |||
| 0f55480156 | |||
| 99ed44eb1e | |||
| 565a926bd3 | |||
| 64483652ff | |||
| d17ee96221 | |||
| ed1ed43484 | |||
| a275366a90 | |||
| 4d1caddce4 | |||
| 0970bd00c6 | |||
| 49b132da93 | |||
| 08d78c8f71 | |||
| 5986dc9a08 | |||
| cdf165d0f4 | |||
| adfe70f191 | |||
| cd891d5334 | |||
| 7feb464af1 | |||
| d3b1577095 | |||
| 12b6e3c1ab | |||
| f085134ae3 | |||
| 00d1c534de | |||
| 37f826f24c | |||
| f3c6015a59 | |||
| b2851e41f1 | |||
| 8ffa1088ab | |||
| 9b8843aa08 | |||
| e105f266e3 | |||
| a49d7c13b6 | |||
| f565014dff | |||
| ad765b9856 | |||
| 4778a8f29e | |||
| 97b61fb8bf | |||
| 847cb3e273 | |||
| 2b4ae2b211 | |||
| fa05d64a76 | |||
| 0ff9258776 | |||
| b937b610de | |||
| 5ac747374b | |||
| d6642a7f7b | |||
| ebf9224407 | |||
| c635771fcd | |||
| 791d69b24d | |||
| aa654a3ac6 | |||
| 6c6d81d3c9 | |||
| 5dc3e99d6e | |||
| fadb783d3c | |||
| 8c79b74e14 | |||
| 578fa10fc9 | |||
| 8477ea19d4 | |||
| 77da37e00e | |||
| 5946397c15 | |||
| 72ebada821 | |||
| 4121011ef2 | |||
| dc3b1fe74e | |||
| c622ef97b0 | |||
| e5b5b0898f | |||
| 5dbfc3dbfe | |||
| 8be42ca1a6 | |||
| 4a7360909c | |||
| 74c52931e9 | |||
| 515fbc173c | |||
| 0aea8673d9 | |||
| 5ab5087f8e | |||
| b7c1b52a57 | |||
| 89b766ff2f | |||
| 9c66a4dfd8 | |||
| 941149d8d7 | |||
| 1764b67112 | |||
| 3742998537 | |||
| 20e00efbed | |||
| 7dd176a771 | |||
| abda9d468b | |||
| 23a29ce5d1 | |||
| 0cb7fc416d | |||
| d5bf77cbd4 | |||
| e34fe47a04 |
@@ -17,10 +17,10 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- windows-latest
|
||||
php-version:
|
||||
- 7.1
|
||||
- 7.4
|
||||
- 8.0
|
||||
phpunit-version:
|
||||
- 7.5.20
|
||||
- 8.5.32
|
||||
dependencies:
|
||||
- lowest
|
||||
- highest
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
composer.lock
|
||||
vendor/
|
||||
.idea/
|
||||
.phpunit.result.cache
|
||||
.phpunit.result.cache
|
||||
tests/tmp
|
||||
@@ -36,11 +36,12 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c
|
||||
- [Available methods](#available-methods)
|
||||
- [Multiple HTTP-verbs](#multiple-http-verbs)
|
||||
- [Route parameters](#route-parameters)
|
||||
- [Required parameters](#required-parameters)
|
||||
- [Optional parameters](#optional-parameters)
|
||||
- [Regular expression constraints](#regular-expression-constraints)
|
||||
- [Regular expression route-match](#regular-expression-route-match)
|
||||
- [Custom regex for matching parameters](#custom-regex-for-matching-parameters)
|
||||
- [Required parameters](#required-parameters)
|
||||
- [Optional parameters](#optional-parameters)
|
||||
- [Including slash in parameters](#including-slash-in-parameters)
|
||||
- [Regular expression constraints](#regular-expression-constraints)
|
||||
- [Regular expression route-match](#regular-expression-route-match)
|
||||
- [Custom regex for matching parameters](#custom-regex-for-matching-parameters)
|
||||
- [Named routes](#named-routes)
|
||||
- [Generating URLs To Named Routes](#generating-urls-to-named-routes)
|
||||
- [Router groups](#router-groups)
|
||||
@@ -84,7 +85,7 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c
|
||||
- [Registering new event](#registering-new-event)
|
||||
- [Custom EventHandlers](#custom-eventhandlers)
|
||||
- [Advanced](#advanced)
|
||||
- [Disable multiple route rendering](#disable-multiple-route-rendering)
|
||||
- [Multiple route rendering](#multiple-route-rendering)
|
||||
- [Restrict access to IP](#restrict-access-to-ip)
|
||||
- [Setting custom base path](#setting-custom-base-path)
|
||||
- [Url rewriting](#url-rewriting)
|
||||
@@ -490,6 +491,28 @@ SimpleRouter::get('/user/{name?}', function ($name = 'Simon') {
|
||||
});
|
||||
```
|
||||
|
||||
### Including slash in parameters
|
||||
|
||||
If you're working with WebDAV services the url could mean the difference between a file and a folder.
|
||||
|
||||
For instance `/path` will be considered a file - whereas `/path/` will be considered a folder.
|
||||
|
||||
The router can add the ending slash for the last parameter in your route based on the path. So if `/path/` is requested the parameter will contain the value of `path/` and visa versa.
|
||||
|
||||
To ensure compatibility with older versions, this feature is disabled by default and has to be enabled by setting
|
||||
the `setSettings(['includeSlash' => true])` or by using setting `setSlashParameterEnabled(true)` for your route.
|
||||
|
||||
**Example**
|
||||
|
||||
```php
|
||||
SimpleRouter::get('/path/{fileOrFolder}', function ($fileOrFolder) {
|
||||
return $fileOrFolder;
|
||||
})->setSettings(['includeSlash' => true]);
|
||||
```
|
||||
|
||||
- Requesting `/path/file` will return the `$fileOrFolder` value: `file`.
|
||||
- Requesting `/path/folder/` will return the `$fileOrFolder` value: `folder/`.
|
||||
|
||||
### Regular expression constraints
|
||||
|
||||
You may constrain the format of your route parameters using the where method on a route instance. The where method accepts the name of the parameter and a regular expression defining how the parameter should be constrained:
|
||||
@@ -1013,6 +1036,17 @@ class CustomExceptionHandler implements IExceptionHandler
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/* Other error */
|
||||
if($error instanceof MyCustomException) {
|
||||
|
||||
$request->setRewriteRoute(
|
||||
// Add new route based on current url (minus query-string) and add custom parameters.
|
||||
(new RouteUrl(url(null, null, []), 'PageController@error'))->setParameters(['exception' => $error])
|
||||
);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
throw $error;
|
||||
|
||||
@@ -1469,11 +1503,12 @@ class DatabaseDebugHandler implements IEventHandler
|
||||
|
||||
# Advanced
|
||||
|
||||
## Disable multiple route rendering
|
||||
## Multiple route rendering
|
||||
|
||||
By default the router will try to execute all routes that matches a given url. To stop the router from executing any further routes any method can return a value.
|
||||
If you need multiple routes to be executed on the same url, you can enable this feature by setting `SimpleRouter::enableMultiRouteRendering(true)`
|
||||
in your `routes.php` file.
|
||||
|
||||
This behavior can be easily disabled by setting `SimpleRouter::enableMultiRouteRendering(false)` in your `routes.php` file. This is the same behavior as version 3 and below.
|
||||
This is most commonly used in advanced cases, for example in CMS systems where multiple routes needs to be rendered.
|
||||
|
||||
## Restrict access to IP
|
||||
|
||||
@@ -1707,6 +1742,7 @@ SimpleRouter::setCustomClassLoader(new MyCustomClassLoader());
|
||||
php-di support was discontinued by version 4.3, however you can easily add it again by creating your own class-loader like the example below:
|
||||
|
||||
```php
|
||||
use Pecee\SimpleRouter\ClassLoader\IClassLoader;
|
||||
use Pecee\SimpleRouter\Exceptions\ClassNotFoundHttpException;
|
||||
|
||||
class MyCustomClassLoader implements IClassLoader
|
||||
@@ -1727,19 +1763,14 @@ class MyCustomClassLoader implements IClassLoader
|
||||
*
|
||||
* @param string $class
|
||||
* @return object
|
||||
* @throws NotFoundHttpException
|
||||
* @throws ClassNotFoundHttpException
|
||||
*/
|
||||
public function loadClass(string $class)
|
||||
{
|
||||
if (class_exists($class) === false) {
|
||||
throw new NotFoundHttpException(sprintf('Class "%s" does not exist', $class), 404);
|
||||
if ($this->container->has($class) === false) {
|
||||
throw new ClassNotFoundHttpException($class, null, sprintf('Class "%s" does not exist', $class), 404, null);
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->container->get($class);
|
||||
} catch (\Exception $e) {
|
||||
throw new NotFoundHttpException($e->getMessage(), (int)$e->getCode(), $e->getPrevious());
|
||||
}
|
||||
return $this->container->get($class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1747,15 +1778,11 @@ class MyCustomClassLoader implements IClassLoader
|
||||
* @param object $class
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return object
|
||||
* @return string
|
||||
*/
|
||||
public function loadClassMethod($class, string $method, array $parameters)
|
||||
{
|
||||
try {
|
||||
return $this->container->call([$class, $method], $parameters);
|
||||
} catch (\Exception $e) {
|
||||
throw new NotFoundHttpException($e->getMessage(), (int)$e->getCode(), $e->getPrevious());
|
||||
}
|
||||
return (string)$this->container->call([$class, $method], $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1763,15 +1790,11 @@ class MyCustomClassLoader implements IClassLoader
|
||||
*
|
||||
* @param Callable $closure
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function loadClosure(callable $closure, array $parameters)
|
||||
{
|
||||
try {
|
||||
return $this->container->call($closure, $parameters);
|
||||
} catch (\Exception $e) {
|
||||
throw new NotFoundHttpException($e->getMessage(), (int)$e->getCode(), $e->getPrevious());
|
||||
}
|
||||
return (string)$this->container->call($closure, $parameters);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
+10
-5
@@ -31,12 +31,12 @@
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7",
|
||||
"phpunit/phpunit": "^8",
|
||||
"mockery/mockery": "^1",
|
||||
"phpstan/phpstan": "^0",
|
||||
"phpstan/phpstan-phpunit": "^0",
|
||||
"phpstan/phpstan-deprecation-rules": "^0",
|
||||
"phpstan/phpstan-strict-rules": "^0"
|
||||
"phpstan/phpstan": "^1",
|
||||
"phpstan/phpstan-phpunit": "^1",
|
||||
"phpstan/phpstan-deprecation-rules": "^1",
|
||||
"phpstan/phpstan-strict-rules": "^1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
@@ -47,5 +47,10 @@
|
||||
"psr-4": {
|
||||
"Pecee\\": "src/Pecee/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"ocramius/package-versions": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,37 +9,37 @@ class InputFile implements IInputItem
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $index;
|
||||
public string $index;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
public string $name;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $filename;
|
||||
public ?string $filename = null;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
public $size;
|
||||
public ?int $size = null;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
* @var string|null
|
||||
*/
|
||||
public $type;
|
||||
public ?string $type = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $errors;
|
||||
public int $errors = 0;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $tmpName;
|
||||
public ?string $tmpName = null;
|
||||
|
||||
public function __construct(string $index)
|
||||
{
|
||||
@@ -74,7 +74,7 @@ class InputFile implements IInputItem
|
||||
'error' => null,
|
||||
];
|
||||
|
||||
return (new static($values['index']))
|
||||
return (new self($values['index']))
|
||||
->setSize((int)$values['size'])
|
||||
->setError((int)$values['error'])
|
||||
->setType($values['type'])
|
||||
@@ -104,9 +104,9 @@ class InputFile implements IInputItem
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return int
|
||||
*/
|
||||
public function getSize(): string
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
@@ -10,40 +10,40 @@ class InputHandler
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $get = [];
|
||||
protected array $get = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $post = [];
|
||||
protected array $post = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $file = [];
|
||||
protected array $file = [];
|
||||
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
protected Request $request;
|
||||
|
||||
/**
|
||||
* Original post variables
|
||||
* @var array
|
||||
*/
|
||||
protected $originalPost = [];
|
||||
protected array $originalPost = [];
|
||||
|
||||
/**
|
||||
* Original get/params variables
|
||||
* @var array
|
||||
*/
|
||||
protected $originalParams = [];
|
||||
protected array $originalParams = [];
|
||||
|
||||
/**
|
||||
* Get original file variables
|
||||
* @var array
|
||||
*/
|
||||
protected $originalFile = [];
|
||||
protected array $originalFile = [];
|
||||
|
||||
/**
|
||||
* Input constructor.
|
||||
@@ -82,6 +82,10 @@ class InputHandler
|
||||
if ($post !== false) {
|
||||
$this->originalPost += $post;
|
||||
}
|
||||
} else {
|
||||
$post = [];
|
||||
parse_str($contents, $post);
|
||||
$this->originalPost += $post;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +112,7 @@ class InputHandler
|
||||
foreach ($files as $key => $value) {
|
||||
|
||||
// Parse multi dept file array
|
||||
if(isset($value['name']) === false && is_array($value) === true) {
|
||||
if (isset($value['name']) === false && is_array($value) === true) {
|
||||
$list[$key] = $this->parseFiles($value, $key);
|
||||
continue;
|
||||
}
|
||||
@@ -161,12 +165,12 @@ class InputHandler
|
||||
try {
|
||||
|
||||
$file = InputFile::createFromArray([
|
||||
'index' => ($key === '' && $originalIndex !== '') ? $originalIndex : $key,
|
||||
'name' => $original['name'][$key],
|
||||
'error' => $original['error'][$key],
|
||||
'index' => ($key === '' && $originalIndex !== '') ? $originalIndex : $key,
|
||||
'name' => $original['name'][$key],
|
||||
'error' => $original['error'][$key],
|
||||
'tmp_name' => $original['tmp_name'][$key],
|
||||
'type' => $original['type'][$key],
|
||||
'size' => $original['size'][$key],
|
||||
'type' => $original['type'][$key],
|
||||
'size' => $original['size'][$key],
|
||||
]);
|
||||
|
||||
if (isset($output[$key]) === true) {
|
||||
@@ -231,7 +235,7 @@ class InputHandler
|
||||
{
|
||||
$element = null;
|
||||
|
||||
if(count($methods) > 0) {
|
||||
if (count($methods) > 0) {
|
||||
$methods = is_array(...$methods) ? array_values(...$methods) : $methods;
|
||||
}
|
||||
|
||||
@@ -303,9 +307,9 @@ class InputHandler
|
||||
public function exists($index, ...$methods): bool
|
||||
{
|
||||
// Check array
|
||||
if(is_array($index) === true) {
|
||||
foreach($index as $key) {
|
||||
if($this->value($key, null, ...$methods) === null) {
|
||||
if (is_array($index) === true) {
|
||||
foreach ($index as $key) {
|
||||
if ($this->value($key, null, ...$methods) === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,18 @@ use IteratorAggregate;
|
||||
|
||||
class InputItem implements ArrayAccess, IInputItem, IteratorAggregate
|
||||
{
|
||||
public $index;
|
||||
public $name;
|
||||
public string $index;
|
||||
public string $name;
|
||||
|
||||
/**
|
||||
* @var mixed|null
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param string $index
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct(string $index, $value = null)
|
||||
{
|
||||
$this->index = $index;
|
||||
@@ -81,7 +89,8 @@ class InputItem implements ArrayAccess, IInputItem, IteratorAggregate
|
||||
return isset($this->value[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset): ?self
|
||||
{
|
||||
if ($this->offsetExists($offset) === true) {
|
||||
return $this->value[$offset];
|
||||
|
||||
@@ -17,18 +17,18 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
* For example: /admin/*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $except;
|
||||
protected array $except = [];
|
||||
|
||||
/**
|
||||
* Urls to include. Can be used to include urls from a certain path.
|
||||
* @var array|null
|
||||
*/
|
||||
protected $include;
|
||||
protected array $include = [];
|
||||
|
||||
/**
|
||||
* @var ITokenProvider
|
||||
*/
|
||||
protected $tokenProvider;
|
||||
protected ITokenProvider $tokenProvider;
|
||||
|
||||
/**
|
||||
* BaseCsrfVerifier constructor.
|
||||
@@ -38,6 +38,23 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
$this->tokenProvider = new CookieTokenProvider();
|
||||
}
|
||||
|
||||
protected function isIncluded(Request $request): bool
|
||||
{
|
||||
if (count($this->include) > 0) {
|
||||
foreach ($this->include as $includeUrl) {
|
||||
$includeUrl = rtrim($includeUrl, '/');
|
||||
if ($includeUrl[strlen($includeUrl) - 1] === '*') {
|
||||
$includeUrl = rtrim($includeUrl, '*');
|
||||
return $request->getUrl()->contains($includeUrl);
|
||||
}
|
||||
|
||||
return ($includeUrl === rtrim($request->getUrl()->getRelativeUrl(false), '/'));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the url matches the urls in the except property
|
||||
* @param Request $request
|
||||
@@ -45,11 +62,11 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
*/
|
||||
protected function skip(Request $request): bool
|
||||
{
|
||||
if ($this->except === null || count($this->except) === 0) {
|
||||
if (count($this->except) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($this->except as $url) {
|
||||
foreach ($this->except as $url) {
|
||||
$url = rtrim($url, '/');
|
||||
if ($url[strlen($url) - 1] === '*') {
|
||||
$url = rtrim($url, '*');
|
||||
@@ -60,20 +77,9 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
|
||||
if ($skip === true) {
|
||||
|
||||
if(is_array($this->include) === true && count($this->include) > 0) {
|
||||
foreach($this->include as $includeUrl) {
|
||||
$includeUrl = rtrim($includeUrl, '/');
|
||||
if ($includeUrl[strlen($includeUrl) - 1] === '*') {
|
||||
$includeUrl = rtrim($includeUrl, '*');
|
||||
$skip = !$request->getUrl()->contains($includeUrl);
|
||||
break;
|
||||
}
|
||||
$skip = !$this->isIncluded($request);
|
||||
|
||||
$skip = !($includeUrl === rtrim($request->getUrl()->getRelativeUrl(false), '/'));
|
||||
}
|
||||
}
|
||||
|
||||
if($skip === false) {
|
||||
if ($skip === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -92,12 +98,11 @@ class BaseCsrfVerifier implements IMiddleware
|
||||
*/
|
||||
public function handle(Request $request): void
|
||||
{
|
||||
if ($this->skip($request) === false && $request->isPostBack() === true) {
|
||||
if ($this->skip($request) === false && ($request->isPostBack() === true || $request->isPostBack() === true && $this->isIncluded($request) === true)) {
|
||||
|
||||
$token = $request->getInputHandler()->value(
|
||||
static::POST_KEY,
|
||||
$request->getHeader(static::HEADER_KEY),
|
||||
Request::$requestTypesPost
|
||||
);
|
||||
|
||||
if ($this->tokenProvider->validate((string)$token) === false) {
|
||||
|
||||
@@ -7,8 +7,8 @@ use Pecee\SimpleRouter\Exceptions\HttpException;
|
||||
|
||||
abstract class IpRestrictAccess implements IMiddleware
|
||||
{
|
||||
protected $ipBlacklist = [];
|
||||
protected $ipWhitelist = [];
|
||||
protected array $ipBlacklist = [];
|
||||
protected array $ipWhitelist = [];
|
||||
|
||||
protected function validate(string $ip): bool
|
||||
{
|
||||
|
||||
+34
-31
@@ -29,7 +29,7 @@ class Request
|
||||
* All request-types
|
||||
* @var string[]
|
||||
*/
|
||||
public static $requestTypes = [
|
||||
public static array $requestTypes = [
|
||||
self::REQUEST_TYPE_GET,
|
||||
self::REQUEST_TYPE_POST,
|
||||
self::REQUEST_TYPE_PUT,
|
||||
@@ -43,7 +43,7 @@ class Request
|
||||
* Post request-types.
|
||||
* @var string[]
|
||||
*/
|
||||
public static $requestTypesPost = [
|
||||
public static array $requestTypesPost = [
|
||||
self::REQUEST_TYPE_POST,
|
||||
self::REQUEST_TYPE_PUT,
|
||||
self::REQUEST_TYPE_PATCH,
|
||||
@@ -55,65 +55,65 @@ class Request
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
private array $data = [];
|
||||
|
||||
/**
|
||||
* Server headers
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [];
|
||||
protected array $headers = [];
|
||||
|
||||
/**
|
||||
* Request ContentType
|
||||
* @var string
|
||||
*/
|
||||
protected $contentType;
|
||||
protected string $contentType;
|
||||
|
||||
/**
|
||||
* Request host
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
protected $host;
|
||||
protected ?string $host;
|
||||
|
||||
/**
|
||||
* Current request url
|
||||
* @var Url
|
||||
*/
|
||||
protected $url;
|
||||
protected Url $url;
|
||||
|
||||
/**
|
||||
* Request method
|
||||
* @var string
|
||||
*/
|
||||
protected $method;
|
||||
protected string $method;
|
||||
|
||||
/**
|
||||
* Input handler
|
||||
* @var InputHandler
|
||||
*/
|
||||
protected $inputHandler;
|
||||
protected InputHandler $inputHandler;
|
||||
|
||||
/**
|
||||
* Defines if request has pending rewrite
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasPendingRewrite = false;
|
||||
protected bool $hasPendingRewrite = false;
|
||||
|
||||
/**
|
||||
* @var ILoadableRoute|null
|
||||
*/
|
||||
protected $rewriteRoute;
|
||||
protected ?ILoadableRoute $rewriteRoute = null;
|
||||
|
||||
/**
|
||||
* Rewrite url
|
||||
* @var string|null
|
||||
*/
|
||||
protected $rewriteUrl;
|
||||
protected ?string $rewriteUrl = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $loadedRoutes = [];
|
||||
protected array $loadedRoutes = [];
|
||||
|
||||
/**
|
||||
* Request constructor.
|
||||
@@ -130,10 +130,10 @@ class Request
|
||||
|
||||
// Check if special IIS header exist, otherwise use default.
|
||||
$url = $this->getHeader('unencoded-url');
|
||||
if($url !== null){
|
||||
if ($url !== null) {
|
||||
$this->setUrl(new Url($url));
|
||||
}else{
|
||||
$this->setUrl(new Url(urldecode($this->getHeader('request-uri'))));
|
||||
} else {
|
||||
$this->setUrl(new Url(urldecode((string)$this->getHeader('request-uri'))));
|
||||
}
|
||||
$this->setContentType((string)$this->getHeader('content-type'));
|
||||
$this->setMethod((string)($_POST[static::FORCE_METHOD_KEY] ?? $this->getHeader('request-method')));
|
||||
@@ -224,15 +224,17 @@ class Request
|
||||
*/
|
||||
public function getIp(bool $safeMode = false): ?string
|
||||
{
|
||||
$headers = ['remote-addr'];
|
||||
if($safeMode === false) {
|
||||
$headers = array_merge($headers, [
|
||||
$headers = [];
|
||||
if ($safeMode === false) {
|
||||
$headers = [
|
||||
'http-cf-connecting-ip',
|
||||
'http-client-ip',
|
||||
'http-x-forwarded-for',
|
||||
]);
|
||||
];
|
||||
}
|
||||
|
||||
$headers[] = 'remote-addr';
|
||||
|
||||
return $this->getFirstHeader($headers);
|
||||
}
|
||||
|
||||
@@ -301,9 +303,9 @@ class Request
|
||||
*/
|
||||
public function getFirstHeader(array $headers, $defaultValue = null)
|
||||
{
|
||||
foreach($headers as $header) {
|
||||
foreach ($headers as $header) {
|
||||
$header = $this->getHeader($header);
|
||||
if($header !== null) {
|
||||
if ($header !== null) {
|
||||
return $header;
|
||||
}
|
||||
}
|
||||
@@ -327,7 +329,7 @@ class Request
|
||||
*/
|
||||
protected function setContentType(string $contentType): self
|
||||
{
|
||||
if(strpos($contentType, ';') > 0) {
|
||||
if (strpos($contentType, ';') > 0) {
|
||||
$this->contentType = strtolower(substr($contentType, 0, strpos($contentType, ';')));
|
||||
} else {
|
||||
$this->contentType = strtolower($contentType);
|
||||
@@ -364,12 +366,12 @@ class Request
|
||||
*/
|
||||
public function isAjax(): bool
|
||||
{
|
||||
return (strtolower($this->getHeader('http-x-requested-with')) === 'xmlhttprequest');
|
||||
return (strtolower((string)$this->getHeader('http-x-requested-with')) === 'xmlhttprequest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when request-method is type that could contain data in the page body.
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPostBack(): bool
|
||||
@@ -393,11 +395,7 @@ class Request
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
if ($this->url->getHost() === null) {
|
||||
$this->url->setHost((string)$this->getHost());
|
||||
}
|
||||
|
||||
if($this->isSecure() === true) {
|
||||
if ($this->isSecure() === true) {
|
||||
$this->url->setScheme('https');
|
||||
}
|
||||
}
|
||||
@@ -407,6 +405,11 @@ class Request
|
||||
*/
|
||||
public function setHost(?string $host): void
|
||||
{
|
||||
// Strip any potential ports from hostname
|
||||
if (strpos((string)$host, ':') !== false) {
|
||||
$host = strstr($host, strrchr($host, ':'), true);
|
||||
}
|
||||
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use Pecee\Exceptions\InvalidArgumentException;
|
||||
|
||||
class Response
|
||||
{
|
||||
protected $request;
|
||||
protected Request $request;
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
@@ -32,6 +32,8 @@ class Response
|
||||
*
|
||||
* @param string $url
|
||||
* @param ?int $httpCode
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function redirect(string $url, ?int $httpCode = null): void
|
||||
{
|
||||
@@ -65,7 +67,6 @@ class Response
|
||||
|
||||
public function cache(string $eTag, int $lastModifiedTime = 2592000): self
|
||||
{
|
||||
|
||||
$this->headers([
|
||||
'Cache-Control: public',
|
||||
sprintf('Last-Modified: %s GMT', gmdate('D, d M Y H:i:s', $lastModifiedTime)),
|
||||
@@ -87,11 +88,11 @@ class Response
|
||||
/**
|
||||
* 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 $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($value, ?int $options = null, int $dept = 512): void
|
||||
public function json($value, int $options = 0, int $dept = 512): void
|
||||
{
|
||||
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.');
|
||||
@@ -128,4 +129,4 @@ class Response
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,12 @@ class CookieTokenProvider implements ITokenProvider
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $token;
|
||||
protected ?string $token = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $cookieTimeoutMinutes = 120;
|
||||
protected int $cookieTimeoutMinutes = 120;
|
||||
|
||||
/**
|
||||
* CookieTokenProvider constructor.
|
||||
|
||||
+45
-15
@@ -10,47 +10,53 @@ class Url implements JsonSerializable
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $originalUrl;
|
||||
private ?string $originalUrl = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $scheme;
|
||||
private ?string $scheme = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $host;
|
||||
private ?string $host = null;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $port;
|
||||
private ?int $port = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $username;
|
||||
private ?string $username = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $password;
|
||||
private ?string $password = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $path;
|
||||
private ?string $path = null;
|
||||
|
||||
/**
|
||||
* Original path with no sanitization to ending slash
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $originalPath = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $params = [];
|
||||
private array $params = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $fragment;
|
||||
private ?string $fragment = null;
|
||||
|
||||
/**
|
||||
* Url constructor.
|
||||
@@ -61,8 +67,12 @@ class Url implements JsonSerializable
|
||||
public function __construct(?string $url)
|
||||
{
|
||||
$this->originalUrl = $url;
|
||||
$this->parse($url, true);
|
||||
}
|
||||
|
||||
if ($url !== null && $url !== '/') {
|
||||
public function parse(?string $url, bool $setOriginalPath = false): self
|
||||
{
|
||||
if ($url !== null) {
|
||||
$data = $this->parseUrl($url);
|
||||
|
||||
$this->scheme = $data['scheme'] ?? null;
|
||||
@@ -73,6 +83,10 @@ class Url implements JsonSerializable
|
||||
|
||||
if (isset($data['path']) === true) {
|
||||
$this->setPath($data['path']);
|
||||
|
||||
if ($setOriginalPath === true) {
|
||||
$this->originalPath = $data['path'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->fragment = $data['fragment'] ?? null;
|
||||
@@ -81,6 +95,8 @@ class Url implements JsonSerializable
|
||||
$this->setQueryString($data['query']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,10 +145,15 @@ class Url implements JsonSerializable
|
||||
/**
|
||||
* Get url host
|
||||
*
|
||||
* @param bool $includeTrails Prepend // in front of hostname
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHost(): ?string
|
||||
public function getHost(bool $includeTrails = false): ?string
|
||||
{
|
||||
if ((string)$this->host !== '' && $includeTrails === true) {
|
||||
return '//' . $this->host;
|
||||
}
|
||||
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
@@ -226,6 +247,15 @@ class Url implements JsonSerializable
|
||||
return $this->path ?? '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get original path with no sanitization of ending trail/slash.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOriginalPath(): ?string
|
||||
{
|
||||
return $this->originalPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the url path
|
||||
*
|
||||
@@ -284,7 +314,7 @@ class Url implements JsonSerializable
|
||||
$params = [];
|
||||
parse_str($queryString, $params);
|
||||
|
||||
if(count($params) > 0) {
|
||||
if (count($params) > 0) {
|
||||
return $this->setParams($params);
|
||||
}
|
||||
|
||||
@@ -469,7 +499,7 @@ class Url implements JsonSerializable
|
||||
{
|
||||
$path = $this->path ?? '/';
|
||||
|
||||
if($includeParams === false) {
|
||||
if ($includeParams === false) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
@@ -506,12 +536,12 @@ class Url implements JsonSerializable
|
||||
*/
|
||||
public function jsonSerialize(): string
|
||||
{
|
||||
return $this->getRelativeUrl();
|
||||
return $this->getHost(true) . $this->getRelativeUrl();
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getRelativeUrl();
|
||||
return $this->getHost(true) . $this->getRelativeUrl();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,11 +27,11 @@ class ClassLoader implements IClassLoader
|
||||
* @param object $class
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return object
|
||||
* @return string
|
||||
*/
|
||||
public function loadClassMethod($class, string $method, array $parameters)
|
||||
public function loadClassMethod($class, string $method, array $parameters): string
|
||||
{
|
||||
return call_user_func_array([$class, $method], array_values($parameters));
|
||||
return (string)call_user_func_array([$class, $method], array_values($parameters));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,11 +39,11 @@ class ClassLoader implements IClassLoader
|
||||
*
|
||||
* @param Callable $closure
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function loadClosure(Callable $closure, array $parameters)
|
||||
public function loadClosure(callable $closure, array $parameters): string
|
||||
{
|
||||
return call_user_func_array($closure, array_values($parameters));
|
||||
return (string)call_user_func_array($closure, array_values($parameters));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,7 @@ interface IClassLoader
|
||||
* @param object $class
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return object
|
||||
* @return mixed
|
||||
*/
|
||||
public function loadClassMethod($class, string $method, array $parameters);
|
||||
|
||||
|
||||
@@ -12,17 +12,17 @@ class EventArgument implements IEventArgument
|
||||
* Event name
|
||||
* @var string
|
||||
*/
|
||||
protected $eventName;
|
||||
protected string $eventName;
|
||||
|
||||
/**
|
||||
* @var Router
|
||||
*/
|
||||
protected $router;
|
||||
protected Router $router;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
protected array $arguments = [];
|
||||
|
||||
public function __construct(string $eventName, Router $router, array $arguments = [])
|
||||
{
|
||||
|
||||
@@ -9,12 +9,12 @@ class ClassNotFoundHttpException extends NotFoundHttpException
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $class;
|
||||
protected string $class;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $method;
|
||||
protected ?string $method = null;
|
||||
|
||||
public function __construct(string $class, ?string $method = null, string $message = "", int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ class CallbackExceptionHandler implements IExceptionHandler
|
||||
/**
|
||||
* @var Closure
|
||||
*/
|
||||
protected $callback;
|
||||
protected Closure $callback;
|
||||
|
||||
public function __construct(Closure $callback)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ class DebugEventHandler implements IEventHandler
|
||||
* Debug callback
|
||||
* @var Closure
|
||||
*/
|
||||
protected $callback;
|
||||
protected Closure $callback;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
@@ -97,7 +97,7 @@ class EventHandler implements IEventHandler
|
||||
* All available events
|
||||
* @var array
|
||||
*/
|
||||
public static $events = [
|
||||
public static array $events = [
|
||||
self::EVENT_ALL,
|
||||
self::EVENT_INIT,
|
||||
self::EVENT_LOAD,
|
||||
@@ -120,7 +120,7 @@ class EventHandler implements IEventHandler
|
||||
* List of all registered events
|
||||
* @var array
|
||||
*/
|
||||
private $registeredEvents = [];
|
||||
private array $registeredEvents = [];
|
||||
|
||||
/**
|
||||
* Register new event
|
||||
|
||||
@@ -6,23 +6,24 @@ use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\Exceptions\HttpException;
|
||||
use Pecee\SimpleRouter\Router;
|
||||
use Pecee\SimpleRouter\SimpleRouter;
|
||||
|
||||
abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
protected string $url;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
protected ?string $name = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $regex;
|
||||
protected ?string $regex = null;
|
||||
|
||||
/**
|
||||
* Loads and renders middlewares-classes
|
||||
@@ -82,15 +83,18 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
{
|
||||
$this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/';
|
||||
|
||||
$parameters = [];
|
||||
if (strpos($this->url, $this->paramModifiers[0]) !== false) {
|
||||
|
||||
$regex = sprintf(static::PARAMETERS_REGEX_FORMAT, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
|
||||
|
||||
if ((bool)preg_match_all('/' . $regex . '/u', $this->url, $matches) !== false) {
|
||||
$this->parameters = array_fill_keys($matches[1], null);
|
||||
$parameters = array_fill_keys($matches[1], null);
|
||||
}
|
||||
}
|
||||
|
||||
$this->parameters = $parameters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -135,12 +139,6 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
{
|
||||
$url = $this->getUrl();
|
||||
|
||||
$group = $this->getGroup();
|
||||
|
||||
if ($group !== null && count($group->getDomains()) !== 0) {
|
||||
$url = '//' . $group->getDomains()[0] . $url;
|
||||
}
|
||||
|
||||
/* Create the param string - {parameter} */
|
||||
$param1 = $this->paramModifiers[0] . '%s' . $this->paramModifiers[1];
|
||||
|
||||
@@ -167,14 +165,22 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
|
||||
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
|
||||
/* Add parameter to the correct position */
|
||||
$url = str_ireplace([sprintf($param1, $param), sprintf($param2, $param)], $value, $url);
|
||||
$url = str_ireplace([sprintf($param1, $param), sprintf($param2, $param)], (string)$value, $url);
|
||||
} else {
|
||||
/* Parameter aren't recognized and will be appended at the end of the url */
|
||||
$url .= $value . '/';
|
||||
}
|
||||
}
|
||||
|
||||
return rtrim('/' . ltrim($url, '/'), '/') . '/';
|
||||
$url = rtrim('/' . ltrim($url, '/'), '/') . '/';
|
||||
|
||||
$group = $this->getGroup();
|
||||
|
||||
if ($group !== null && count($group->getDomains()) !== 0 && SimpleRouter::request()->getHost() !== $group->getDomains()[0]) {
|
||||
$url = '//' . $group->getDomains()[0] . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,7 +201,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
|
||||
*/
|
||||
public function hasName(string $name): bool
|
||||
{
|
||||
return strtolower($this->name) === strtolower($name);
|
||||
return strtolower((string)$this->name) === strtolower($name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,31 +18,37 @@ abstract class Route implements IRoute
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $filterEmptyParams = true;
|
||||
protected bool $filterEmptyParams = true;
|
||||
|
||||
/**
|
||||
* If true the last parameter of the route will include ending trail/slash.
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $slashParameterEnabled = false;
|
||||
|
||||
/**
|
||||
* Default regular expression used for parsing parameters.
|
||||
* @var string|null
|
||||
*/
|
||||
protected $defaultParameterRegex;
|
||||
protected $paramModifiers = '{}';
|
||||
protected $paramOptionalSymbol = '?';
|
||||
protected $urlRegex = '/^%s\/?$/u';
|
||||
protected $group;
|
||||
protected $parent;
|
||||
protected ?string $defaultParameterRegex = null;
|
||||
protected string $paramModifiers = '{}';
|
||||
protected string $paramOptionalSymbol = '?';
|
||||
protected string $urlRegex = '/^%s\/?$/u';
|
||||
protected ?IGroupRoute $group = null;
|
||||
protected ?IRoute $parent = null;
|
||||
/**
|
||||
* @var string|callable|null
|
||||
*/
|
||||
protected $callback;
|
||||
protected $defaultNamespace;
|
||||
protected ?string $defaultNamespace = null;
|
||||
|
||||
/* Default options */
|
||||
protected $namespace;
|
||||
protected $requestMethods = [];
|
||||
protected $where = [];
|
||||
protected $parameters = [];
|
||||
protected $originalParameters = [];
|
||||
protected $middlewares = [];
|
||||
protected ?string $namespace = null;
|
||||
protected array $requestMethods = [];
|
||||
protected array $where = [];
|
||||
protected array $parameters = [];
|
||||
protected array $originalParameters = [];
|
||||
protected array $middlewares = [];
|
||||
|
||||
/**
|
||||
* Render route
|
||||
@@ -111,7 +117,7 @@ abstract class Route implements IRoute
|
||||
return $router->getClassLoader()->loadClassMethod($class, $method, $parameters);
|
||||
}
|
||||
|
||||
protected function parseParameters($route, $url, $parameterRegex = null): ?array
|
||||
protected function parseParameters($route, $url, Request $request, $parameterRegex = null): ?array
|
||||
{
|
||||
$regex = (strpos($route, $this->paramModifiers[0]) === false) ? null :
|
||||
sprintf
|
||||
@@ -123,8 +129,10 @@ abstract class Route implements IRoute
|
||||
);
|
||||
|
||||
// Ensures that host names/domains will work with parameters
|
||||
|
||||
if($route[0] == '{') $url = '/' . ltrim($url, '/');
|
||||
if ($route[0] === $this->paramModifiers[0]) {
|
||||
$url = '/' . ltrim($url, '/');
|
||||
}
|
||||
|
||||
$urlRegex = '';
|
||||
$parameters = [];
|
||||
|
||||
@@ -132,7 +140,7 @@ abstract class Route implements IRoute
|
||||
$urlRegex = preg_quote($route, '/');
|
||||
} else {
|
||||
|
||||
foreach (preg_split('/((\.?-?\/?){[^}]+})/', $route) as $key => $t) {
|
||||
foreach (preg_split('/((\.?-?\/?){[^' . $this->paramModifiers[1] . ']+' . $this->paramModifiers[1] . ')/', $route) as $key => $t) {
|
||||
|
||||
$regex = '';
|
||||
|
||||
@@ -154,6 +162,7 @@ abstract class Route implements IRoute
|
||||
}
|
||||
}
|
||||
|
||||
// Get name of last param
|
||||
if (trim($urlRegex) === '' || (bool)preg_match(sprintf($this->urlRegex, $urlRegex), $url, $matches) === false) {
|
||||
return null;
|
||||
}
|
||||
@@ -167,7 +176,8 @@ abstract class Route implements IRoute
|
||||
$lastParams = [];
|
||||
|
||||
/* Only take matched parameters with name */
|
||||
foreach ((array)$parameters[1] as $name) {
|
||||
$originalPath = $request->getUrl()->getOriginalPath();
|
||||
foreach ((array)$parameters[1] as $i => $name) {
|
||||
|
||||
// Ignore parent parameters
|
||||
if (isset($groupParameters[$name]) === true) {
|
||||
@@ -175,10 +185,16 @@ abstract class Route implements IRoute
|
||||
continue;
|
||||
}
|
||||
|
||||
// If last parameter and slash parameter is enabled, use slash according to original path (non sanitized version)
|
||||
$lastParameter = $this->paramModifiers[0] . $name . $this->paramModifiers[1] . '/';
|
||||
if ($this->slashParameterEnabled && ($i === count($parameters[1]) - 1) && (substr_compare($route, $lastParameter, -strlen($lastParameter)) === 0) && $originalPath[strlen($originalPath) - 1] === '/') {
|
||||
$matches[$name] .= '/';
|
||||
}
|
||||
|
||||
$values[$name] = (isset($matches[$name]) === true && $matches[$name] !== '') ? $matches[$name] : null;
|
||||
}
|
||||
|
||||
$values = array_merge($values, $lastParams);
|
||||
$values += $lastParams;
|
||||
}
|
||||
|
||||
$this->originalParameters = $values;
|
||||
@@ -387,6 +403,17 @@ abstract class Route implements IRoute
|
||||
return $this->namespace ?? $this->defaultNamespace;
|
||||
}
|
||||
|
||||
public function setSlashParameterEnabled(bool $enabled): self
|
||||
{
|
||||
$this->slashParameterEnabled = $enabled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSlashParameterEnabled(): bool
|
||||
{
|
||||
return $this->slashParameterEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export route settings to array so they can be merged with another route.
|
||||
*
|
||||
@@ -416,6 +443,10 @@ abstract class Route implements IRoute
|
||||
$values['defaultParameterRegex'] = $this->defaultParameterRegex;
|
||||
}
|
||||
|
||||
if ($this->slashParameterEnabled === true) {
|
||||
$values['includeSlash'] = $this->slashParameterEnabled;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
@@ -453,6 +484,10 @@ abstract class Route implements IRoute
|
||||
$this->setDefaultParameterRegex($settings['defaultParameterRegex']);
|
||||
}
|
||||
|
||||
if (isset($settings['includeSlash']) === true) {
|
||||
$this->setSlashParameterEnabled($settings['includeSlash']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\SimpleRouter;
|
||||
|
||||
class RouteController extends LoadableRoute implements IControllerRoute
|
||||
{
|
||||
protected $defaultMethod = 'index';
|
||||
protected $controller;
|
||||
protected $method;
|
||||
protected $names = [];
|
||||
protected string $defaultMethod = 'index';
|
||||
protected string $controller;
|
||||
protected ?string $method = null;
|
||||
protected array $names = [];
|
||||
|
||||
public function __construct($url, $controller)
|
||||
{
|
||||
@@ -77,13 +78,15 @@ class RouteController extends LoadableRoute implements IControllerRoute
|
||||
|
||||
$group = $this->getGroup();
|
||||
|
||||
if ($group !== null && count($group->getDomains()) !== 0) {
|
||||
$url .= '//' . $group->getDomains()[0];
|
||||
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower((string)$method) . implode('/', $parameters);
|
||||
|
||||
$url = '/' . trim($url, '/') . '/';
|
||||
|
||||
if ($group !== null && count($group->getDomains()) !== 0 && SimpleRouter::request()->getHost() !== $group->getDomains()[0]) {
|
||||
$url = '//' . $group->getDomains()[0] . $url;
|
||||
}
|
||||
|
||||
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower($method) . implode('/', $parameters);
|
||||
|
||||
return '/' . trim($url, '/') . '/';
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function matchRoute(string $url, Request $request): bool
|
||||
|
||||
@@ -7,12 +7,12 @@ use Pecee\SimpleRouter\Handlers\IExceptionHandler;
|
||||
|
||||
class RouteGroup extends Route implements IGroupRoute
|
||||
{
|
||||
protected $urlRegex = '/^%s\/?/u';
|
||||
protected $prefix;
|
||||
protected $name;
|
||||
protected $domains = [];
|
||||
protected $exceptionHandlers = [];
|
||||
protected $mergeExceptionHandlers = true;
|
||||
protected string $urlRegex = '/^%s\/?/u';
|
||||
protected ?string $prefix = null;
|
||||
protected ?string $name = null;
|
||||
protected array $domains = [];
|
||||
protected array $exceptionHandlers = [];
|
||||
protected bool $mergeExceptionHandlers = true;
|
||||
|
||||
/**
|
||||
* Method called to check if a domain matches
|
||||
@@ -22,7 +22,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
*/
|
||||
public function matchDomain(Request $request): bool
|
||||
{
|
||||
if ($this->domains === null || count($this->domains) === 0) {
|
||||
if (count($this->domains) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
return true;
|
||||
}
|
||||
|
||||
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
|
||||
$parameters = $this->parseParameters($domain, $request->getHost(), $request, '.*');
|
||||
|
||||
if ($parameters !== null && count($parameters) !== 0) {
|
||||
$this->parameters = $parameters;
|
||||
@@ -60,7 +60,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
|
||||
if ($this->prefix !== null) {
|
||||
/* Parse parameters from current route */
|
||||
$parameters = $this->parseParameters($this->prefix, $url);
|
||||
$parameters = $this->parseParameters($this->prefix, $url, $request);
|
||||
|
||||
/* If no custom regular expression or parameters was found on this route, we stop */
|
||||
if ($parameters === null) {
|
||||
@@ -74,7 +74,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
$parsedPrefix = $this->prefix;
|
||||
|
||||
foreach ($this->getParameters() as $parameter => $value) {
|
||||
$parsedPrefix = str_ireplace('{' . $parameter . '}', $value, $parsedPrefix);
|
||||
$parsedPrefix = str_ireplace('{' . $parameter . '}', (string)$value, (string)$parsedPrefix);
|
||||
}
|
||||
|
||||
/* Skip if prefix doesn't match */
|
||||
@@ -220,7 +220,7 @@ class RouteGroup extends Route implements IGroupRoute
|
||||
$this->setExceptionHandlers((array)$settings['exceptionHandler']);
|
||||
}
|
||||
|
||||
if ($merge === false && isset($settings['domain']) === true) {
|
||||
if (isset($settings['domain']) === true) {
|
||||
$this->setDomains((array)$settings['domain']);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,31 +3,32 @@
|
||||
namespace Pecee\SimpleRouter\Route;
|
||||
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\SimpleRouter\SimpleRouter;
|
||||
|
||||
class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
{
|
||||
protected $urls = [
|
||||
'index' => '',
|
||||
'create' => 'create',
|
||||
'store' => '',
|
||||
'show' => '',
|
||||
'edit' => 'edit',
|
||||
'update' => '',
|
||||
protected array $urls = [
|
||||
'index' => '',
|
||||
'create' => 'create',
|
||||
'store' => '',
|
||||
'show' => '',
|
||||
'edit' => 'edit',
|
||||
'update' => '',
|
||||
'destroy' => '',
|
||||
];
|
||||
|
||||
protected $methodNames = [
|
||||
'index' => 'index',
|
||||
'create' => 'create',
|
||||
'store' => 'store',
|
||||
'show' => 'show',
|
||||
'edit' => 'edit',
|
||||
'update' => 'update',
|
||||
protected array $methodNames = [
|
||||
'index' => 'index',
|
||||
'create' => 'create',
|
||||
'store' => 'store',
|
||||
'show' => 'show',
|
||||
'edit' => 'edit',
|
||||
'update' => 'update',
|
||||
'destroy' => 'destroy',
|
||||
];
|
||||
|
||||
protected $names = [];
|
||||
protected $controller;
|
||||
protected array $names = [];
|
||||
protected string $controller;
|
||||
|
||||
public function __construct($url, $controller)
|
||||
{
|
||||
@@ -69,11 +70,25 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
public function findUrl(?string $method = null, $parameters = null, ?string $name = null): string
|
||||
{
|
||||
$url = array_search($name, $this->names, true);
|
||||
if ($url !== false) {
|
||||
return rtrim($this->url . $this->urls[$url], '/') . '/';
|
||||
|
||||
$parametersUrl = '';
|
||||
|
||||
if ($parameters !== null && count($parameters) > 0) {
|
||||
$parametersUrl = join('/', $parameters) . '/';
|
||||
}
|
||||
|
||||
return $this->url;
|
||||
if ($url !== false) {
|
||||
return rtrim($this->url . $parametersUrl . $this->urls[$url], '/') . '/';
|
||||
}
|
||||
|
||||
$url = $this->url . $parametersUrl;
|
||||
|
||||
$group = $this->getGroup();
|
||||
if ($group !== null && count($group->getDomains()) !== 0 && SimpleRouter::request()->getHost() !== $group->getDomains()[0]) {
|
||||
$url = '//' . $group->getDomains()[0] . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
protected function call($method): bool
|
||||
@@ -99,14 +114,14 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
|
||||
|
||||
/* Parse parameters from current route */
|
||||
$this->parameters = $this->parseParameters($route, $url);
|
||||
$this->parameters = $this->parseParameters($route, $url, $request);
|
||||
|
||||
/* If no custom regular expression or parameters was found on this route, we stop */
|
||||
if ($regexMatch === null && $this->parameters === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$action = strtolower(trim($this->parameters['action']));
|
||||
$action = strtolower(trim((string)$this->parameters['action']));
|
||||
$id = $this->parameters['id'];
|
||||
|
||||
// Remove action parameter
|
||||
@@ -172,12 +187,12 @@ class RouteResource extends LoadableRoute implements IControllerRoute
|
||||
$this->name = $name;
|
||||
|
||||
$this->names = [
|
||||
'index' => $this->name . '.index',
|
||||
'create' => $this->name . '.create',
|
||||
'store' => $this->name . '.store',
|
||||
'show' => $this->name . '.show',
|
||||
'edit' => $this->name . '.edit',
|
||||
'update' => $this->name . '.update',
|
||||
'index' => $this->name . '.index',
|
||||
'create' => $this->name . '.create',
|
||||
'store' => $this->name . '.store',
|
||||
'show' => $this->name . '.show',
|
||||
'edit' => $this->name . '.edit',
|
||||
'update' => $this->name . '.update',
|
||||
'destroy' => $this->name . '.destroy',
|
||||
];
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class RouteUrl extends LoadableRoute
|
||||
}
|
||||
|
||||
/* Parse parameters from current route */
|
||||
$parameters = $this->parseParameters($this->url, $url);
|
||||
$parameters = $this->parseParameters($this->url, $url, $request);
|
||||
|
||||
/* If no custom regular expression or parameters was found on this route, we stop */
|
||||
if ($regexMatch === null && $parameters === null) {
|
||||
|
||||
@@ -28,56 +28,56 @@ class Router
|
||||
* Current request
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
protected Request $request;
|
||||
|
||||
/**
|
||||
* Defines if a route is currently being processed.
|
||||
* @var bool
|
||||
*/
|
||||
protected $isProcessingRoute;
|
||||
|
||||
protected bool $isProcessingRoute;
|
||||
|
||||
/**
|
||||
* Defines all data from current processing route.
|
||||
* @var ILoadableRoute
|
||||
*/
|
||||
protected $currentProcessingRoute;
|
||||
protected ILoadableRoute $currentProcessingRoute;
|
||||
|
||||
/**
|
||||
* All added routes
|
||||
* @var array
|
||||
*/
|
||||
protected $routes = [];
|
||||
protected array $routes = [];
|
||||
|
||||
/**
|
||||
* List of processed routes
|
||||
* @var array|ILoadableRoute[]
|
||||
*/
|
||||
protected $processedRoutes = [];
|
||||
protected array $processedRoutes = [];
|
||||
|
||||
/**
|
||||
* Stack of routes used to keep track of sub-routes added
|
||||
* when a route is being processed.
|
||||
* @var array
|
||||
*/
|
||||
protected $routeStack = [];
|
||||
protected array $routeStack = [];
|
||||
|
||||
/**
|
||||
* List of added bootmanagers
|
||||
* @var array
|
||||
*/
|
||||
protected $bootManagers = [];
|
||||
protected array $bootManagers = [];
|
||||
|
||||
/**
|
||||
* Csrf verifier class
|
||||
* @var BaseCsrfVerifier|null
|
||||
*/
|
||||
protected $csrfVerifier;
|
||||
protected ?BaseCsrfVerifier $csrfVerifier;
|
||||
|
||||
/**
|
||||
* Get exception handlers
|
||||
* @var array
|
||||
*/
|
||||
protected $exceptionHandlers = [];
|
||||
protected array $exceptionHandlers = [];
|
||||
|
||||
/**
|
||||
* List of loaded exception that has been loaded.
|
||||
@@ -85,44 +85,44 @@ class Router
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $loadedExceptionHandlers = [];
|
||||
protected array $loadedExceptionHandlers = [];
|
||||
|
||||
/**
|
||||
* Enable or disabled debugging
|
||||
* @var bool
|
||||
*/
|
||||
protected $debugEnabled = false;
|
||||
protected bool $debugEnabled = false;
|
||||
|
||||
/**
|
||||
* The start time used when debugging is enabled
|
||||
* @var float
|
||||
*/
|
||||
protected $debugStartTime;
|
||||
protected float $debugStartTime;
|
||||
|
||||
/**
|
||||
* List containing all debug messages
|
||||
* @var array
|
||||
*/
|
||||
protected $debugList = [];
|
||||
protected array $debugList = [];
|
||||
|
||||
/**
|
||||
* Contains any registered event-handler.
|
||||
* @var array
|
||||
*/
|
||||
protected $eventHandlers = [];
|
||||
protected array $eventHandlers = [];
|
||||
|
||||
/**
|
||||
* Class loader instance
|
||||
* @var IClassLoader
|
||||
*/
|
||||
protected $classLoader;
|
||||
protected IClassLoader $classLoader;
|
||||
|
||||
/**
|
||||
* When enabled the router will render all routes that matches.
|
||||
* When disabled the router will stop execution when first route is found.
|
||||
* @var bool
|
||||
*/
|
||||
protected $renderMultipleRoutes = true;
|
||||
protected bool $renderMultipleRoutes = false;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
@@ -166,7 +166,7 @@ class Router
|
||||
public function addRoute(IRoute $route): IRoute
|
||||
{
|
||||
$this->fireEvents(EventHandler::EVENT_ADD_ROUTE, [
|
||||
'route' => $route,
|
||||
'route' => $route,
|
||||
'isSubRoute' => $this->isProcessingRoute,
|
||||
]);
|
||||
|
||||
@@ -307,7 +307,7 @@ class Router
|
||||
$this->debug('Rendering bootmanager "%s"', $className);
|
||||
$this->fireEvents(EventHandler::EVENT_RENDER_BOOTMANAGER, [
|
||||
'bootmanagers' => $this->bootManagers,
|
||||
'bootmanager' => $manager,
|
||||
'bootmanager' => $manager,
|
||||
]);
|
||||
|
||||
/* Render bootmanager */
|
||||
@@ -342,8 +342,12 @@ class Router
|
||||
'csrfVerifier' => $this->csrfVerifier,
|
||||
]);
|
||||
|
||||
/* Verify csrf token for request */
|
||||
$this->csrfVerifier->handle($this->request);
|
||||
try {
|
||||
/* Verify csrf token for request */
|
||||
$this->csrfVerifier->handle($this->request);
|
||||
} catch (Exception $e) {
|
||||
return $this->handleException($e);
|
||||
}
|
||||
}
|
||||
|
||||
$output = $this->routeRequest();
|
||||
@@ -377,7 +381,7 @@ class Router
|
||||
foreach ($this->processedRoutes as $key => $route) {
|
||||
|
||||
$this->debug('Matching route "%s"', get_class($route));
|
||||
|
||||
|
||||
/* Add current processing route to constants */
|
||||
$this->currentProcessingRoute = $route;
|
||||
|
||||
@@ -401,7 +405,7 @@ class Router
|
||||
}
|
||||
|
||||
$this->fireEvents(EventHandler::EVENT_RENDER_MIDDLEWARES, [
|
||||
'route' => $route,
|
||||
'route' => $route,
|
||||
'middlewares' => $route->getMiddlewares(),
|
||||
]);
|
||||
|
||||
@@ -423,7 +427,7 @@ class Router
|
||||
$routeOutput = $route->renderRoute($this->request, $this);
|
||||
|
||||
if ($this->renderMultipleRoutes === true) {
|
||||
if ($routeOutput !== null) {
|
||||
if ($routeOutput !== '') {
|
||||
return $routeOutput;
|
||||
}
|
||||
|
||||
@@ -440,12 +444,12 @@ class Router
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
return $this->handleException($e);
|
||||
}
|
||||
|
||||
if ($methodNotAllowed === true) {
|
||||
$message = sprintf('Route "%s" or method "%s" not allowed.', $this->request->getUrl()->getPath(), $this->request->getMethod());
|
||||
$this->handleException(new NotFoundHttpException($message, 403));
|
||||
return $this->handleException(new NotFoundHttpException($message, 403));
|
||||
}
|
||||
|
||||
if (count($this->request->getLoadedRoutes()) === 0) {
|
||||
@@ -496,7 +500,7 @@ class Router
|
||||
$this->request->setHasPendingRewrite(false);
|
||||
|
||||
$this->fireEvents(EventHandler::EVENT_REWRITE, [
|
||||
'rewriteUrl' => $this->request->getRewriteUrl(),
|
||||
'rewriteUrl' => $this->request->getRewriteUrl(),
|
||||
'rewriteRoute' => $this->request->getRewriteRoute(),
|
||||
]);
|
||||
|
||||
@@ -517,7 +521,7 @@ class Router
|
||||
$this->debug('Starting exception handling for "%s"', get_class($e));
|
||||
|
||||
$this->fireEvents(EventHandler::EVENT_LOAD_EXCEPTIONS, [
|
||||
'exception' => $e,
|
||||
'exception' => $e,
|
||||
'exceptionHandlers' => $this->exceptionHandlers,
|
||||
]);
|
||||
|
||||
@@ -529,8 +533,8 @@ class Router
|
||||
}
|
||||
|
||||
$this->fireEvents(EventHandler::EVENT_RENDER_EXCEPTION, [
|
||||
'exception' => $e,
|
||||
'exceptionHandler' => $handler,
|
||||
'exception' => $e,
|
||||
'exceptionHandler' => $handler,
|
||||
'exceptionHandlers' => $this->exceptionHandlers,
|
||||
]);
|
||||
|
||||
@@ -552,7 +556,7 @@ class Router
|
||||
$this->debug('Exception handler contains rewrite, reloading routes');
|
||||
|
||||
$this->fireEvents(EventHandler::EVENT_REWRITE, [
|
||||
'rewriteUrl' => $this->request->getRewriteUrl(),
|
||||
'rewriteUrl' => $this->request->getRewriteUrl(),
|
||||
'rewriteRoute' => $this->request->getRewriteRoute(),
|
||||
]);
|
||||
|
||||
@@ -608,7 +612,7 @@ class Router
|
||||
if (strpos($name, '@') !== false) {
|
||||
[$controller, $method] = array_map('strtolower', explode('@', $name));
|
||||
|
||||
if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) {
|
||||
if ($controller === strtolower((string)$route->getClass()) && $method === strtolower((string)$route->getMethod())) {
|
||||
$this->debug('Found route "%s" by controller "%s" and method "%s"', $route->getUrl(), $controller, $method);
|
||||
|
||||
return $route;
|
||||
@@ -663,9 +667,9 @@ class Router
|
||||
$this->debug('Finding url', func_get_args());
|
||||
|
||||
$this->fireEvents(EventHandler::EVENT_GET_URL, [
|
||||
'name' => $name,
|
||||
'name' => $name,
|
||||
'parameters' => $parameters,
|
||||
'getParams' => $getParams,
|
||||
'getParams' => $getParams,
|
||||
]);
|
||||
|
||||
if ($name === '' && $parameters === '') {
|
||||
@@ -686,10 +690,7 @@ class Router
|
||||
|
||||
/* If nothing is defined and a route is loaded we use that */
|
||||
if ($name === null && $loadedRoute !== null) {
|
||||
return $this->request
|
||||
->getUrlCopy()
|
||||
->setPath($loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name))
|
||||
->setParams($getParams);
|
||||
return $this->request->getUrlCopy()->parse($loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name))->setParams($getParams);
|
||||
}
|
||||
|
||||
if ($name !== null) {
|
||||
@@ -697,10 +698,7 @@ class Router
|
||||
$route = $this->findRoute($name);
|
||||
|
||||
if ($route !== null) {
|
||||
return $this->request
|
||||
->getUrlCopy()
|
||||
->setPath($route->findUrl($route->getMethod(), $parameters, $name))
|
||||
->setParams($getParams);
|
||||
return $this->request->getUrlCopy()->parse($route->findUrl($route->getMethod(), $parameters, $name))->setParams($getParams);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,18 +713,12 @@ class Router
|
||||
|
||||
/* Check if the route contains the name/alias */
|
||||
if ($processedRoute->hasName($controller) === true) {
|
||||
return $this->request
|
||||
->getUrlCopy()
|
||||
->setPath($processedRoute->findUrl($method, $parameters, $name))
|
||||
->setParams($getParams);
|
||||
return $this->request->getUrlCopy()->parse($processedRoute->findUrl($method, $parameters, $name))->setParams($getParams);
|
||||
}
|
||||
|
||||
/* Check if the route controller is equal to the name */
|
||||
if ($processedRoute instanceof IControllerRoute && strtolower($processedRoute->getController()) === strtolower($controller)) {
|
||||
return $this->request
|
||||
->getUrlCopy()
|
||||
->setPath($processedRoute->findUrl($method, $parameters, $name))
|
||||
->setParams($getParams);
|
||||
return $this->request->getUrlCopy()->parse($processedRoute->findUrl($method, $parameters, $name))->setParams($getParams);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -736,10 +728,7 @@ class Router
|
||||
$url = trim(implode('/', array_merge((array)$name, (array)$parameters)), '/');
|
||||
$url = (($url === '') ? '/' : '/' . $url . '/');
|
||||
|
||||
return $this->request
|
||||
->getUrlCopy()
|
||||
->setPath($url)
|
||||
->setParams($getParams);
|
||||
return $this->request->getUrlCopy()->parse($url)->setParams($getParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -909,8 +898,8 @@ class Router
|
||||
$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),
|
||||
'time' => number_format(microtime(true) - $this->debugStartTime, 10),
|
||||
'trace' => end($trace),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -936,7 +925,7 @@ class Router
|
||||
{
|
||||
return $this->debugList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the current processing route details.
|
||||
*
|
||||
@@ -969,4 +958,4 @@ class Router
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -37,19 +37,19 @@ class SimpleRouter
|
||||
* Default namespace added to all routes
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $defaultNamespace;
|
||||
protected static ?string $defaultNamespace = null;
|
||||
|
||||
/**
|
||||
* The response object
|
||||
* @var Response
|
||||
* @var Response|null
|
||||
*/
|
||||
protected static $response;
|
||||
protected static ?Response $response = null;
|
||||
|
||||
/**
|
||||
* Router instance
|
||||
* @var Router
|
||||
*/
|
||||
protected static $router;
|
||||
protected static ?Router $router = null;
|
||||
|
||||
/**
|
||||
* Start routing
|
||||
@@ -493,7 +493,7 @@ class SimpleRouter
|
||||
* Prepends the default namespace to all new routes added.
|
||||
*
|
||||
* @param ILoadableRoute|IRoute $route
|
||||
* @return IRoute
|
||||
* @return IRoute|ILoadableRoute
|
||||
*/
|
||||
public static function addDefaultNamespace(IRoute $route): IRoute
|
||||
{
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
class DummyCsrfVerifier extends \Pecee\Http\Middleware\BaseCsrfVerifier {
|
||||
|
||||
protected $except = [
|
||||
protected array $except = [
|
||||
'/exclude-page',
|
||||
'/exclude-all/*',
|
||||
];
|
||||
|
||||
protected $include = [
|
||||
protected array $include = [
|
||||
'/exclude-all/include-page',
|
||||
];
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
class IpRestrictMiddleware extends \Pecee\Http\Middleware\IpRestrictAccess {
|
||||
|
||||
protected $ipBlacklist = [
|
||||
protected array $ipBlacklist = [
|
||||
'5.5.5.5',
|
||||
'8.8.*',
|
||||
];
|
||||
|
||||
protected $ipWhitelist = [
|
||||
protected array $ipWhitelist = [
|
||||
'8.8.2.2',
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class DummyLoadableRoute extends Pecee\SimpleRouter\Route\LoadableRoute {
|
||||
|
||||
public function matchRoute(string $url, Request $request): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
require_once 'Dummy/Route/DummyLoadableRoute.php';
|
||||
|
||||
class LoadableRouteTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testSetUrlUpdatesParameters()
|
||||
{
|
||||
$route = new DummyLoadableRoute();
|
||||
$this->assertEmpty($route->getParameters());
|
||||
|
||||
$route->setUrl('/');
|
||||
$this->assertEmpty($route->getParameters());
|
||||
|
||||
$expected = ['param' => null, 'optionalParam' => null];
|
||||
$route->setUrl('/{param}/{optionalParam?}');
|
||||
$this->assertEquals($expected, $route->getParameters());
|
||||
|
||||
$expected = ['otherParam' => null];
|
||||
$route->setUrl('/{otherParam}');
|
||||
$this->assertEquals($expected, $route->getParameters());
|
||||
|
||||
$expected = [];
|
||||
$route->setUrl('/');
|
||||
$this->assertEquals($expected, $route->getParameters());
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,22 @@ class RouterResourceTest extends \PHPUnit\Framework\TestCase
|
||||
$response = TestRouter::debugOutput('/resource/38', 'get');
|
||||
|
||||
$this->assertEquals('show 38', $response);
|
||||
}
|
||||
|
||||
public function testResourceUrls()
|
||||
{
|
||||
TestRouter::resource('/resource', 'ResourceController')->name('resource');
|
||||
|
||||
TestRouter::debugNoReset('/resource');
|
||||
|
||||
$this->assertEquals('/resource/3/create/', TestRouter::router()->getUrl('resource.create', ['id' => 3]));
|
||||
$this->assertEquals('/resource/5/edit/', TestRouter::router()->getUrl('resource.edit', ['id' => 5]));
|
||||
$this->assertEquals('/resource/6/', TestRouter::router()->getUrl('resource.update', ['id' => 6]));
|
||||
$this->assertEquals('/resource/9/', TestRouter::router()->getUrl('resource.destroy', ['id' => 9]));
|
||||
$this->assertEquals('/resource/12/', TestRouter::router()->getUrl('resource.delete', ['id' => 12]));
|
||||
$this->assertEquals('/resource/', TestRouter::router()->getUrl('resource'));
|
||||
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,23 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
|
||||
public function testLastParameterSlash()
|
||||
{
|
||||
TestRouter::get('/test/{param}', function ($param) {
|
||||
return $param;
|
||||
})->setSettings(['includeSlash' => true]);
|
||||
|
||||
// Test with ending /
|
||||
$output = TestRouter::debugOutputNoReset('/test/param/');
|
||||
$this->assertEquals($output, 'param/');
|
||||
|
||||
// Test without ending /
|
||||
$output = TestRouter::debugOutputNoReset('/test/param');
|
||||
$this->assertEquals($output, 'param');
|
||||
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
|
||||
public function testUnicodeCharacters()
|
||||
{
|
||||
// Test spanish characters
|
||||
@@ -77,11 +94,13 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
public function testSimilarUrls()
|
||||
{
|
||||
TestRouter::reset();
|
||||
// Match normal route on alias
|
||||
TestRouter::get('/url11', 'DummyController@method1');
|
||||
TestRouter::get('/url22', 'DummyController@method2');
|
||||
TestRouter::get('/url33', 'DummyController@method2')->name('match');
|
||||
|
||||
|
||||
TestRouter::debugNoReset('/url33', 'get');
|
||||
|
||||
$this->assertEquals(TestRouter::getUrl('match'), TestRouter::getUrl());
|
||||
@@ -167,7 +186,7 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
// Should match /?jackdaniels=true&cola=yeah
|
||||
$this->assertEquals('/?jackdaniels=true&cola=yeah', TestRouter::getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
|
||||
|
||||
TestRouter::router()->reset();
|
||||
TestRouter::reset();
|
||||
|
||||
}
|
||||
|
||||
@@ -191,7 +210,7 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
$results = '';
|
||||
|
||||
TestRouter::get('/tester/{param}', function ($param = null) use($results) {
|
||||
TestRouter::get('/tester/{param}', function ($param = null) use ($results) {
|
||||
return $results = $param;
|
||||
})->setMatch('/(.*)/i');
|
||||
|
||||
@@ -234,9 +253,9 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
TestRouter::debug('/');
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertCount(2, $result);
|
||||
}
|
||||
|
||||
|
||||
public function testDefaultNamespace()
|
||||
{
|
||||
TestRouter::setDefaultNamespace('\\Default\\Namespace');
|
||||
@@ -245,14 +264,14 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
TestRouter::group([
|
||||
'namespace' => 'Appended\Namespace',
|
||||
'prefix' => '/horses',
|
||||
'prefix' => '/horses',
|
||||
], function () {
|
||||
|
||||
TestRouter::get('/', 'DummyController@method1');
|
||||
|
||||
TestRouter::group([
|
||||
'namespace' => '\\New\\Namespace',
|
||||
'prefix' => '/race',
|
||||
'prefix' => '/race',
|
||||
], function () {
|
||||
|
||||
TestRouter::get('/', 'DummyController@method1');
|
||||
@@ -287,13 +306,14 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
TestRouter::router()->reset();
|
||||
}
|
||||
|
||||
public function testGroupPrefix() {
|
||||
public function testGroupPrefix()
|
||||
{
|
||||
|
||||
$result = false;
|
||||
|
||||
TestRouter::group(['prefix' => '/lang/{lang}'], function () use(&$result) {
|
||||
TestRouter::group(['prefix' => '/lang/{lang}'], function () use (&$result) {
|
||||
|
||||
TestRouter::get('/test', function() use(&$result) {
|
||||
TestRouter::get('/test', function () use (&$result) {
|
||||
$result = true;
|
||||
});
|
||||
});
|
||||
@@ -307,13 +327,13 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
$result = null;
|
||||
$expectedResult = 28;
|
||||
|
||||
TestRouter::group(['prefix' => '/lang/{lang}'], function () use(&$result) {
|
||||
TestRouter::group(['prefix' => '/lang/{lang}'], function () use (&$result) {
|
||||
|
||||
TestRouter::get('/horse/{horseType}', function($horseType) use(&$result) {
|
||||
TestRouter::get('/horse/{horseType}', function ($horseType) use (&$result) {
|
||||
$result = false;
|
||||
});
|
||||
|
||||
TestRouter::get('/user/{userId}', function($userId) use(&$result) {
|
||||
TestRouter::get('/user/{userId}', function ($userId) use (&$result) {
|
||||
$result = $userId;
|
||||
});
|
||||
});
|
||||
@@ -324,14 +344,15 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
}
|
||||
|
||||
public function testPassParameter() {
|
||||
public function testPassParameter()
|
||||
{
|
||||
|
||||
$result = false;
|
||||
$expectedLanguage = 'da';
|
||||
|
||||
TestRouter::group(['prefix' => '/lang/{lang}'], function ($language) use(&$result) {
|
||||
TestRouter::group(['prefix' => '/lang/{lang}'], function ($language) use (&$result) {
|
||||
|
||||
TestRouter::get('/test', function($language) use(&$result) {
|
||||
TestRouter::get('/test', function ($language) use (&$result) {
|
||||
$result = $language;
|
||||
});
|
||||
|
||||
@@ -343,15 +364,16 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
}
|
||||
|
||||
public function testPassParameterDeep() {
|
||||
public function testPassParameterDeep()
|
||||
{
|
||||
|
||||
$result = false;
|
||||
$expectedLanguage = 'da';
|
||||
|
||||
TestRouter::group(['prefix' => '/lang/{lang}'], function ($language) use(&$result) {
|
||||
TestRouter::group(['prefix' => '/lang/{lang}'], function ($language) use (&$result) {
|
||||
|
||||
TestRouter::group(['prefix' => '/admin'], function($language) use(&$result) {
|
||||
TestRouter::get('/test', function($language) use(&$result) {
|
||||
TestRouter::group(['prefix' => '/admin'], function ($language) use (&$result) {
|
||||
TestRouter::get('/test', function ($language) use (&$result) {
|
||||
$result = $language;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,11 +8,16 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter
|
||||
static::request()->setHost('testhost.com');
|
||||
}
|
||||
|
||||
public static function reset(): void
|
||||
{
|
||||
static::$router = null;
|
||||
}
|
||||
|
||||
public static function debugNoReset(string $testUrl, string $testMethod = 'get'): void
|
||||
{
|
||||
$request = static::request();
|
||||
|
||||
$request->setUrl((new \Pecee\Http\Url($testUrl))->setHost('local.unitTest'));
|
||||
$request->setUrl((new \Pecee\Http\Url($testUrl)));
|
||||
$request->setMethod($testMethod);
|
||||
|
||||
static::start();
|
||||
|
||||
Reference in New Issue
Block a user