Compare commits

..

24 Commits

Author SHA1 Message Date
Simon Sessingø 8477ea19d4 Merge pull request #645 from skipperbent/v5-development
Version 5.2.0.0
2023-04-02 03:20:01 +02:00
Simon Sessingø 77da37e00e Merge pull request #644 from skipperbent/v5-property-types
[!!!] Added type definitions to property types
2023-04-02 03:14:50 +02:00
sessingo 5946397c15 [!!!] Added type definitions to property types
- Request: optimized getIp method and reversed the order so proxy is always checked first.
2023-04-02 03:10:27 +02:00
Simon Sessingø 4121011ef2 Merge pull request #642 from skipperbent/v5-development
Version 5.1.1.0
2023-03-30 16:26:44 +02:00
Simon Sessingø dc3b1fe74e Merge pull request #641 from skipperbent/fix-session
[BUGFIX] Fixed SESSION not shutdown correctly when using redirect.
2023-03-30 16:21:04 +02:00
sessingo c622ef97b0 [BUGFIX] Fixed SESSION not shutdown correctly when using redirect.
- Disabled multiple route rendering by default.
2023-03-30 16:15:06 +02:00
Simon Sessingø 5dbfc3dbfe Merge pull request #639 from skipperbent/v5-development
Version 5.1.0.0
2023-03-25 03:19:13 +01:00
Simon Sessingø 8be42ca1a6 Merge pull request #638 from skipperbent/v5-feature-ending-slash
[FEATURE] Parameter ending trail/slash
2023-03-25 03:13:28 +01:00
sessingo 4a7360909c Fixed for PHP7 2023-03-25 02:24:19 +01:00
sessingo 74c52931e9 Ending trail/slash feature
- Feature: added support for slash in parameters (see readme).
- Route: Fixed hardcoded param modifier.
- Route: optimisations.
- Updated Readme.
2023-03-25 02:20:06 +01:00
Simon Sessingø 0aea8673d9 Merge pull request #632 from skipperbent/v5-development
Version 5.0.0.3
2023-02-13 14:06:24 +01:00
sessingo 5ab5087f8e Fixed CSRF-token not triggering exception handlers. 2023-02-13 13:59:49 +01:00
sessingo b7c1b52a57 Updated phpstan 2023-02-12 23:39:49 +01:00
Simon Sessingø 9c66a4dfd8 Merge pull request #630 from skipperbent/v5-development
Version 5.0.0.2
2023-02-11 17:34:48 +01:00
sessingo 941149d8d7 Fixed deprication warnings 2023-02-11 17:31:00 +01:00
sessingo 1764b67112 Updated composer.json 2023-02-11 17:25:54 +01:00
sessingo 3742998537 Updated git workflows 2023-02-11 17:22:40 +01:00
sessingo 20e00efbed Php8 deprication warning 2023-02-11 17:16:40 +01:00
sessingo 7dd176a771 Fixed strtolower php8 deprication warning 2023-02-10 22:47:45 +01:00
sessingo abda9d468b Fixed deprication message when using response()->json 2023-02-10 08:48:58 +01:00
sessingo 23a29ce5d1 Fixed php strtolower deprication warning 2023-02-10 06:10:29 +01:00
Simon Sessingø d5bf77cbd4 Merge pull request #628 from skipperbent/v5-development
Fixed offsetGet return type deprication warning
2023-02-09 03:34:48 +01:00
Simon Sessingø 1e9fa9c6a1 Merge pull request #627 from skipperbent/v4-development
Version 5.0.0.0
2023-02-09 03:07:54 +01:00
Simon Sessingø 0630569f56 Merge pull request #592 from skipperbent/master
fixed json_encode 2nd parameter int flag issue in response and issue with offsetGet return type incompatibility
2023-02-09 02:30:02 +01:00
31 changed files with 295 additions and 181 deletions
+2 -2
View File
@@ -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
View File
@@ -1,4 +1,5 @@
composer.lock
vendor/
.idea/
.phpunit.result.cache
.phpunit.result.cache
tests/tmp
+33 -9
View File
@@ -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:
@@ -1469,11 +1492,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
+10 -5
View File
@@ -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
}
}
}
+11 -11
View File
@@ -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;
}
+7 -7
View File
@@ -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.
+10 -2
View File
@@ -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;
@@ -17,18 +17,18 @@ class BaseCsrfVerifier implements IMiddleware
* For example: /admin/*
* @var array|null
*/
protected $except;
protected ?array $except = null;
/**
* Urls to include. Can be used to include urls from a certain path.
* @var array|null
*/
protected $include;
protected ?array $include = null;
/**
* @var ITokenProvider
*/
protected $tokenProvider;
protected ITokenProvider $tokenProvider;
/**
* BaseCsrfVerifier constructor.
@@ -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
{
+21 -19
View File
@@ -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.
@@ -133,7 +133,7 @@ class Request
if($url !== null){
$this->setUrl(new Url($url));
}else{
$this->setUrl(new Url(urldecode($this->getHeader('request-uri'))));
$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'];
$headers = [];
if($safeMode === false) {
$headers = array_merge($headers, [
$headers = [
'http-cf-connecting-ip',
'http-client-ip',
'http-x-forwarded-for',
]);
];
}
$headers[] = 'remote-addr';
return $this->getFirstHeader($headers);
}
@@ -364,7 +366,7 @@ 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');
}
/**
+3 -4
View File
@@ -7,7 +7,7 @@ use Pecee\Exceptions\InvalidArgumentException;
class Response
{
protected $request;
protected Request $request;
public function __construct(Request $request)
{
@@ -65,7 +65,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 +86,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.');
@@ -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.
+27 -11
View File
@@ -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.
@@ -73,6 +79,7 @@ class Url implements JsonSerializable
if (isset($data['path']) === true) {
$this->setPath($data['path']);
$this->originalPath = $data['path'];
}
$this->fragment = $data['fragment'] ?? null;
@@ -226,6 +233,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 +300,7 @@ class Url implements JsonSerializable
$params = [];
parse_str($queryString, $params);
if(count($params) > 0) {
if (count($params) > 0) {
return $this->setParams($params);
}
@@ -469,7 +485,7 @@ class Url implements JsonSerializable
{
$path = $this->path ?? '/';
if($includeParams === false) {
if ($includeParams === false) {
return $path;
}
@@ -27,7 +27,7 @@ class ClassLoader implements IClassLoader
* @param object $class
* @param string $method
* @param array $parameters
* @return object
* @return mixed
*/
public function loadClassMethod($class, string $method, array $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
@@ -12,17 +12,17 @@ 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
@@ -167,7 +167,7 @@ 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 . '/';
@@ -195,7 +195,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);
}
/**
+55 -20
View File
@@ -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;
}
@@ -6,10 +6,10 @@ use Pecee\Http\Request;
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)
{
@@ -81,7 +81,7 @@ class RouteController extends LoadableRoute implements IControllerRoute
$url .= '//' . $group->getDomains()[0];
}
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower($method) . implode('/', $parameters);
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower((string)$method) . implode('/', $parameters);
return '/' . trim($url, '/') . '/';
}
+10 -10
View File
@@ -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 */
@@ -6,7 +6,7 @@ use Pecee\Http\Request;
class RouteResource extends LoadableRoute implements IControllerRoute
{
protected $urls = [
protected array $urls = [
'index' => '',
'create' => 'create',
'store' => '',
@@ -16,7 +16,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute
'destroy' => '',
];
protected $methodNames = [
protected array $methodNames = [
'index' => 'index',
'create' => 'create',
'store' => 'store',
@@ -26,8 +26,8 @@ class RouteResource extends LoadableRoute implements IControllerRoute
'destroy' => 'destroy',
];
protected $names = [];
protected $controller;
protected array $names = [];
protected string $controller;
public function __construct($url, $controller)
{
@@ -99,14 +99,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
+1 -1
View File
@@ -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) {
+23 -19
View File
@@ -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.
@@ -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) {
$this->handleException($e);
}
}
$output = $this->routeRequest();
@@ -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;
+4 -4
View File
@@ -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
*/
protected static $response;
protected static Response $response;
/**
* 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',
];
+38 -18
View File
@@ -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
@@ -191,7 +208,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 +251,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 +262,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 +304,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 +325,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 +342,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 +362,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;
});
});