From f23d569757c79e491d775dcf3966ea927944eaeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Tue, 20 Mar 2018 03:38:55 +0100 Subject: [PATCH 01/25] Added support for PHP7 --- .codeclimate.yml | 22 +++ .scrutinizer.yml | 13 ++ .travis.yml | 13 ++ README.md | 14 +- composer.json | 5 +- helpers.php | 11 +- phpunit.xml | 24 ++++ src/Pecee/Controllers/IResourceController.php | 29 ++-- .../Handlers/CallbackExceptionHandler.php | 5 +- src/Pecee/Handlers/IExceptionHandler.php | 4 +- src/Pecee/Http/Input/IInputItem.php | 12 +- src/Pecee/Http/Input/InputFile.php | 60 ++++---- .../Input/{Input.php => InputHandler.php} | 44 +++--- src/Pecee/Http/Input/InputItem.php | 12 +- .../Http/Middleware/BaseCsrfVerifier.php | 24 ++-- src/Pecee/Http/Middleware/IMiddleware.php | 4 +- src/Pecee/Http/Request.php | 129 +++++++++++------- src/Pecee/Http/Response.php | 21 ++- .../Http/Security/CookieTokenProvider.php | 39 ++---- src/Pecee/Http/Security/ITokenProvider.php | 12 +- src/Pecee/Http/Url.php | 31 +++-- src/Pecee/SimpleRouter/IRouterBootManager.php | 4 +- .../SimpleRouter/Route/IControllerRoute.php | 20 +-- src/Pecee/SimpleRouter/Route/IGroupRoute.php | 18 +-- .../SimpleRouter/Route/ILoadableRoute.php | 17 +-- src/Pecee/SimpleRouter/Route/IRoute.php | 67 +++++---- .../SimpleRouter/Route/LoadableRoute.php | 34 ++--- src/Pecee/SimpleRouter/Route/Route.php | 96 ++++++------- .../SimpleRouter/Route/RouteController.php | 30 ++-- src/Pecee/SimpleRouter/Route/RouteGroup.php | 38 +++--- .../SimpleRouter/Route/RoutePartialGroup.php | 6 +- .../SimpleRouter/Route/RouteResource.php | 22 +-- src/Pecee/SimpleRouter/Route/RouteUrl.php | 4 +- src/Pecee/SimpleRouter/Router.php | 76 +++++------ src/Pecee/SimpleRouter/SimpleRouter.php | 84 +++++++----- test/Dummy/ResourceController.php | 39 ------ .../SimpleRouter}/Dummy/DummyController.php | 0 .../SimpleRouter}/Dummy/DummyMiddleware.php | 4 +- .../Exception}/ExceptionHandlerException.php | 1 + .../Exception}/MiddlewareLoadedException.php | 0 .../Dummy/Exception}/ResponseException.php | 0 .../Dummy/Handler/ExceptionHandler.php | 2 +- .../Dummy/Handler/ExceptionHandlerFirst.php | 3 +- .../Dummy/Handler/ExceptionHandlerSecond.php | 3 +- .../Dummy/Handler/ExceptionHandlerThird.php | 2 +- .../Dummy/Middleware}/RewriteMiddleware.php | 2 +- .../SimpleRouter/Dummy/ResourceController.php | 46 +++++++ .../Pecee/SimpleRouter}/GroupTest.php | 6 +- .../Pecee/SimpleRouter}/MiddlewareTest.php | 7 +- .../RouterCallbackExceptionHandlerTest.php | 9 +- .../SimpleRouter}/RouterControllerTest.php | 3 +- .../SimpleRouter}/RouterPartialGroupTest.php | 3 +- .../SimpleRouter}/RouterResourceTest.php | 3 +- .../Pecee/SimpleRouter}/RouterRewriteTest.php | 9 +- .../Pecee/SimpleRouter}/RouterRouteTest.php | 17 ++- .../Pecee/SimpleRouter}/RouterUrlTest.php | 3 +- {test/Helpers => tests}/TestRouter.php | 0 tests/bootstrap.php | 4 + 58 files changed, 678 insertions(+), 532 deletions(-) create mode 100644 .codeclimate.yml create mode 100644 .scrutinizer.yml create mode 100644 .travis.yml create mode 100644 phpunit.xml rename src/Pecee/Http/Input/{Input.php => InputHandler.php} (84%) delete mode 100644 test/Dummy/ResourceController.php rename {test => tests/Pecee/SimpleRouter}/Dummy/DummyController.php (100%) rename {test => tests/Pecee/SimpleRouter}/Dummy/DummyMiddleware.php (61%) rename {test/Dummy/Exceptions => tests/Pecee/SimpleRouter/Dummy/Exception}/ExceptionHandlerException.php (98%) rename {test/Dummy/Exceptions => tests/Pecee/SimpleRouter/Dummy/Exception}/MiddlewareLoadedException.php (100%) rename {test/Dummy/Exceptions => tests/Pecee/SimpleRouter/Dummy/Exception}/ResponseException.php (100%) rename {test => tests/Pecee/SimpleRouter}/Dummy/Handler/ExceptionHandler.php (89%) rename {test => tests/Pecee/SimpleRouter}/Dummy/Handler/ExceptionHandlerFirst.php (87%) rename {test => tests/Pecee/SimpleRouter}/Dummy/Handler/ExceptionHandlerSecond.php (86%) rename {test => tests/Pecee/SimpleRouter}/Dummy/Handler/ExceptionHandlerThird.php (92%) rename {test/Dummy/Middlewares => tests/Pecee/SimpleRouter/Dummy/Middleware}/RewriteMiddleware.php (79%) create mode 100644 tests/Pecee/SimpleRouter/Dummy/ResourceController.php rename {test => tests/Pecee/SimpleRouter}/GroupTest.php (94%) rename {test => tests/Pecee/SimpleRouter}/MiddlewareTest.php (83%) rename {test => tests/Pecee/SimpleRouter}/RouterCallbackExceptionHandlerTest.php (69%) rename {test => tests/Pecee/SimpleRouter}/RouterControllerTest.php (89%) rename {test => tests/Pecee/SimpleRouter}/RouterPartialGroupTest.php (86%) rename {test => tests/Pecee/SimpleRouter}/RouterResourceTest.php (94%) rename {test => tests/Pecee/SimpleRouter}/RouterRewriteTest.php (93%) rename {test => tests/Pecee/SimpleRouter}/RouterRouteTest.php (91%) rename {test => tests/Pecee/SimpleRouter}/RouterUrlTest.php (98%) rename {test/Helpers => tests}/TestRouter.php (100%) create mode 100644 tests/bootstrap.php diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000..467daf9 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,22 @@ +engines: + phpmd: + enabled: true + checks: + Design/TooManyPublicMethods: + enabled: true + Naming/ShortVariable: + enabled: true + CleanCode/StaticAccess: + enabled: true + Controversial/CamelCaseMethodName: + enabled: true + fixme: + enabled: true + duplication: + enabled: true + config: + languages: + - php: +ratings: + paths: + - src/** diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..0608e7a --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,13 @@ +build: + tests: + override: + - + command: './vendor/bin/phpunit --coverage-clover=coverage.clover' + coverage: + file: 'coverage.clover' + format: 'clover' +checks: + php: + code_rating: true + duplication: true + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..72c98f1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +sudo: false + +language: php + +php: + - 7.1 + +before_script: + - curl -sS http://getcomposer.org/installer | php + - php composer.phar install --prefer-source --no-interaction + +script: + - ./vendor/bin/phpunit diff --git a/README.md b/README.md index f8c2c00..5a333b6 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,8 @@ use Pecee\SimpleRouter\SimpleRouter as Router; * @param string|array|null $parameters * @param array|null $getParams * @return string + * @throws \InvalidArgumentException + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function url($name = null, $parameters = null, $getParams = null) { @@ -316,6 +318,7 @@ function url($name = null, $parameters = null, $getParams = null) /** * @return \Pecee\Http\Response + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function response() { @@ -324,6 +327,7 @@ function response() /** * @return \Pecee\Http\Request + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function request() { @@ -335,15 +339,16 @@ function request() * @param string|null $index Parameter index name * @param string|null $defaultValue Default return value * @param string|array|null $methods Default method - * @return \Pecee\Http\Input\Input|string + * @return \Pecee\Http\Input\InputHandler|string + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function input($index = null, $defaultValue = null, $methods = null) { if ($index !== null) { - return request()->getInput()->get($index, $defaultValue, $methods); + return request()->getInputHandler()->get($index, $defaultValue, $methods); } - return request()->getInput(); + return request()->getInputHandler(); } function redirect($url, $code = null) @@ -358,12 +363,13 @@ function redirect($url, $code = null) /** * Get current csrf-token * @return string|null + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function csrf_token() { $baseVerifier = Router::router()->getCsrfVerifier(); if ($baseVerifier !== null) { - return $baseVerifier->getToken(); + return $baseVerifier->getTokenProvider()->getToken(); } return null; diff --git a/composer.json b/composer.json index 2fee7b4..9c88dd6 100644 --- a/composer.json +++ b/composer.json @@ -27,10 +27,11 @@ } ], "require": { - "php": ">=5.4.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "4.7.7" + "phpunit/phpunit": "^6.0", + "mockery/mockery": "^1" }, "autoload": { "psr-4": { diff --git a/helpers.php b/helpers.php index 71ae2fb..2c4d86d 100644 --- a/helpers.php +++ b/helpers.php @@ -19,6 +19,7 @@ use Pecee\SimpleRouter\SimpleRouter as Router; * @param array|null $getParams * @return string * @throws \InvalidArgumentException + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function url($name = null, $parameters = null, $getParams = null) { @@ -27,6 +28,7 @@ function url($name = null, $parameters = null, $getParams = null) /** * @return \Pecee\Http\Response + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function response() { @@ -35,6 +37,7 @@ function response() /** * @return \Pecee\Http\Request + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function request() { @@ -46,15 +49,16 @@ function request() * @param string|null $index Parameter index name * @param string|null $defaultValue Default return value * @param string|array|null $methods Default method - * @return \Pecee\Http\Input\Input|string + * @return \Pecee\Http\Input\InputHandler|string + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function input($index = null, $defaultValue = null, $methods = null) { if ($index !== null) { - return request()->getInput()->get($index, $defaultValue, $methods); + return request()->getInputHandler()->get($index, $defaultValue, $methods); } - return request()->getInput(); + return request()->getInputHandler(); } function redirect($url, $code = null) @@ -69,6 +73,7 @@ function redirect($url, $code = null) /** * Get current csrf-token * @return string|null + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ function csrf_token() { diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..6e8f0c6 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,24 @@ + + + + + + tests/Pecee/SimpleRouter/ + + + + + src + + + diff --git a/src/Pecee/Controllers/IResourceController.php b/src/Pecee/Controllers/IResourceController.php index 502017f..5900467 100644 --- a/src/Pecee/Controllers/IResourceController.php +++ b/src/Pecee/Controllers/IResourceController.php @@ -1,47 +1,48 @@ callback, + \call_user_func($this->callback, $request, $error ); diff --git a/src/Pecee/Handlers/IExceptionHandler.php b/src/Pecee/Handlers/IExceptionHandler.php index d27237f..5cd21d8 100644 --- a/src/Pecee/Handlers/IExceptionHandler.php +++ b/src/Pecee/Handlers/IExceptionHandler.php @@ -1,4 +1,5 @@ index; } @@ -67,9 +67,9 @@ class InputFile implements IInputItem /** * Set input index * @param string $index - * @return static $this + * @return static */ - public function setIndex($index) + public function setIndex($index): IInputItem { $this->index = $index; @@ -79,7 +79,7 @@ class InputFile implements IInputItem /** * @return string */ - public function getSize() + public function getSize(): string { return $this->size; } @@ -87,9 +87,9 @@ class InputFile implements IInputItem /** * Set file size * @param int $size - * @return static $this + * @return static */ - public function setSize($size) + public function setSize($size): IInputItem { $this->size = $size; @@ -100,7 +100,7 @@ class InputFile implements IInputItem * Get mime-type of file * @return string */ - public function getMime() + public function getMime(): string { return $this->getType(); } @@ -108,7 +108,7 @@ class InputFile implements IInputItem /** * @return string */ - public function getType() + public function getType(): string { return $this->type; } @@ -116,9 +116,9 @@ class InputFile implements IInputItem /** * Set type * @param string $type - * @return static $this + * @return static */ - public function setType($type) + public function setType($type): IInputItem { $this->type = $type; @@ -130,7 +130,7 @@ class InputFile implements IInputItem * * @return string */ - public function getExtension() + public function getExtension(): string { return pathinfo($this->getFilename(), PATHINFO_EXTENSION); } @@ -140,7 +140,7 @@ class InputFile implements IInputItem * * @return string */ - public function getName() + public function getName(): string { return $this->name; } @@ -150,9 +150,9 @@ class InputFile implements IInputItem * Useful for adding validation etc. * * @param string $name - * @return static $this + * @return static */ - public function setName($name) + public function setName($name): IInputItem { $this->name = $name; @@ -163,9 +163,9 @@ class InputFile implements IInputItem * Set filename * * @param string $name - * @return static $this + * @return static */ - public function setFilename($name) + public function setFilename($name): IInputItem { $this->filename = $name; @@ -177,7 +177,7 @@ class InputFile implements IInputItem * * @return string mixed */ - public function getFilename() + public function getFilename(): string { return $this->filename; } @@ -188,7 +188,7 @@ class InputFile implements IInputItem * @param string $destination * @return bool */ - public function move($destination) + public function move($destination): bool { return move_uploaded_file($this->tmpName, $destination); } @@ -198,7 +198,7 @@ class InputFile implements IInputItem * * @return string */ - public function getContents() + public function getContents(): string { return file_get_contents($this->tmpName); } @@ -208,7 +208,7 @@ class InputFile implements IInputItem * * @return bool */ - public function hasError() + public function hasError(): bool { return ($this->getError() !== 0); } @@ -218,7 +218,7 @@ class InputFile implements IInputItem * * @return string */ - public function getError() + public function getError(): string { return $this->errors; } @@ -227,9 +227,9 @@ class InputFile implements IInputItem * Set error * * @param int $error - * @return static $this + * @return static */ - public function setError($error) + public function setError($error): IInputItem { $this->errors = (int)$error; @@ -239,7 +239,7 @@ class InputFile implements IInputItem /** * @return string */ - public function getTmpName() + public function getTmpName(): string { return $this->tmpName; } @@ -247,9 +247,9 @@ class InputFile implements IInputItem /** * Set file temp. name * @param string $name - * @return static $this + * @return static */ - public function setTmpName($name) + public function setTmpName($name): IInputItem { $this->tmpName = $name; @@ -261,7 +261,7 @@ class InputFile implements IInputItem return $this->getTmpName(); } - public function getValue() + public function getValue(): string { return $this->getFilename(); } @@ -270,14 +270,14 @@ class InputFile implements IInputItem * @param string $value * @return static */ - public function setValue($value) + public function setValue($value): IInputItem { $this->filename = $value; return $this; } - public function toArray() + public function toArray(): array { return [ 'tmp_name' => $this->tmpName, diff --git a/src/Pecee/Http/Input/Input.php b/src/Pecee/Http/Input/InputHandler.php similarity index 84% rename from src/Pecee/Http/Input/Input.php rename to src/Pecee/Http/Input/InputHandler.php index b40dfef..416a8e3 100644 --- a/src/Pecee/Http/Input/Input.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -5,7 +5,7 @@ namespace Pecee\Http\Input; use Pecee\Exceptions\InvalidArgumentException; use Pecee\Http\Request; -class Input +class InputHandler { /** * @var array @@ -42,26 +42,26 @@ class Input * Parse input values * */ - public function parseInputs() + public function parseInputs() : void { /* Parse get requests */ - if (count($_GET) !== 0) { + if (\count($_GET) !== 0) { $this->get = $this->handleGetPost($_GET); } /* Parse post requests */ $postVars = $_POST; - if (in_array($this->request->getMethod(), ['put', 'patch', 'delete'], false) === true) { + if (\in_array($this->request->getMethod(), ['put', 'patch', 'delete'], false) === true) { parse_str(file_get_contents('php://input'), $postVars); } - if (count($postVars) !== 0) { + if (\count($postVars) !== 0) { $this->post = $this->handleGetPost($postVars); } /* Parse get requests */ - if (count($_FILES) !== 0) { + if (\count($_FILES) !== 0) { $this->file = $this->parseFiles(); } } @@ -69,14 +69,14 @@ class Input /** * @return array */ - public function parseFiles() + public function parseFiles() : array { $list = []; foreach ((array)$_FILES as $key => $value) { // Handle array input - if (is_array($value['name']) === false) { + if (\is_array($value['name']) === false) { $values['index'] = $key; try { $list[$key] = InputFile::createFromArray($values + $value); @@ -101,7 +101,7 @@ class Input return $list; } - protected function rearrangeFiles(array $values, &$index, $original) + protected function rearrangeFiles(array $values, &$index, $original) : array { $originalIndex = $index[0]; @@ -111,7 +111,7 @@ class Input foreach ($values as $key => $value) { - if (is_array($original['name'][$key]) === false) { + if (\is_array($original['name'][$key]) === false) { try { @@ -152,14 +152,14 @@ class Input return $output; } - protected function handleGetPost(array $array) + protected function handleGetPost(array $array) : array { $list = []; foreach ($array as $key => $value) { // Handle array input - if (is_array($value) === false) { + if (\is_array($value) === false) { $list[$key] = new InputItem($key, $value); continue; } @@ -181,7 +181,7 @@ class Input */ public function findPost($index, $defaultValue = null) { - return isset($this->post[$index]) ? $this->post[$index] : $defaultValue; + return $this->post[$index] ?? $defaultValue; } /** @@ -193,7 +193,7 @@ class Input */ public function findFile($index, $defaultValue = null) { - return isset($this->file[$index]) ? $this->file[$index] : $defaultValue; + return $this->file[$index] ?? $defaultValue; } /** @@ -205,7 +205,7 @@ class Input */ public function findGet($index, $defaultValue = null) { - return isset($this->get[$index]) ? $this->get[$index] : $defaultValue; + return $this->get[$index] ?? $defaultValue; } /** @@ -218,25 +218,25 @@ class Input */ public function getObject($index, $defaultValue = null, $methods = null) { - if ($methods !== null && is_string($methods) === true) { + if ($methods !== null && \is_string($methods) === true) { $methods = [$methods]; } $element = null; - if ($methods === null || in_array('get', $methods, false) === true) { + if ($methods === null || \in_array('get', $methods, true) === true) { $element = $this->findGet($index); } - if (($element === null && $methods === null) || ($methods !== null && in_array('post', $methods, false) === true)) { + if (($element === null && $methods === null) || ($methods !== null && \in_array('post', $methods, true) === true)) { $element = $this->findPost($index); } - if (($element === null && $methods === null) || ($methods !== null && in_array('file', $methods, false) === true)) { + if (($element === null && $methods === null) || ($methods !== null && \in_array('file', $methods, true) === true)) { $element = $this->findFile($index); } - return ($element !== null) ? $element : $defaultValue; + return $element ?? $defaultValue; } /** @@ -264,7 +264,7 @@ class Input * @param string $index * @return bool */ - public function exists($index) + public function exists($index) : bool { return ($this->getObject($index) !== null); } @@ -274,7 +274,7 @@ class Input * @param array|null $filter Only take items in filter * @return array */ - public function all(array $filter = null) + public function all(array $filter = null) : array { $output = $_GET + $_POST; diff --git a/src/Pecee/Http/Input/InputItem.php b/src/Pecee/Http/Input/InputItem.php index 7ca4b93..de00aa7 100644 --- a/src/Pecee/Http/Input/InputItem.php +++ b/src/Pecee/Http/Input/InputItem.php @@ -20,12 +20,12 @@ class InputItem implements IInputItem /** * @return string */ - public function getIndex() + public function getIndex(): string { return $this->index; } - public function setIndex($index) + public function setIndex($index): IInputItem { $this->index = $index; @@ -35,7 +35,7 @@ class InputItem implements IInputItem /** * @return string */ - public function getName() + public function getName(): string { return $this->name; } @@ -45,7 +45,7 @@ class InputItem implements IInputItem * @param string $name * @return static $this */ - public function setName($name) + public function setName($name): IInputItem { $this->name = $name; @@ -55,7 +55,7 @@ class InputItem implements IInputItem /** * @return string */ - public function getValue() + public function getValue(): string { return $this->value; } @@ -65,7 +65,7 @@ class InputItem implements IInputItem * @param string $value * @return static $this */ - public function setValue($value) + public function setValue($value): IInputItem { $this->value = $value; diff --git a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php index 67e57dd..613058a 100644 --- a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php +++ b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php @@ -9,8 +9,8 @@ use Pecee\Http\Security\ITokenProvider; class BaseCsrfVerifier implements IMiddleware { - const POST_KEY = 'csrf-token'; - const HEADER_KEY = 'X-CSRF-TOKEN'; + public const POST_KEY = 'csrf-token'; + public const HEADER_KEY = 'X-CSRF-TOKEN'; protected $except; protected $tokenProvider; @@ -29,21 +29,21 @@ class BaseCsrfVerifier implements IMiddleware * @param Request $request * @return bool */ - protected function skip(Request $request) + protected function skip(Request $request): bool { - if ($this->except === null || count($this->except) === 0) { + if ($this->except === null || \count($this->except) === 0) { return false; } - $max = count($this->except) - 1; + $max = \count($this->except) - 1; for ($i = $max; $i >= 0; $i--) { $url = $this->except[$i]; $url = rtrim($url, '/'); - if ($url[strlen($url) - 1] === '*') { + if ($url[\strlen($url) - 1] === '*') { $url = rtrim($url, '*'); - $skip = (stripos($request->getUrl()->getOriginalUrl(), $url) === 0); + $skip = $request->getUrl()->contains($url); } else { $skip = ($url === $request->getUrl()->getOriginalUrl()); } @@ -62,12 +62,12 @@ class BaseCsrfVerifier implements IMiddleware * @param Request $request * @throws TokenMismatchException */ - public function handle(Request $request) + public function handle(Request $request): void { - if ($this->skip($request) === false && in_array($request->getMethod(), ['post', 'put', 'delete'], false) === true) { + if ($this->skip($request) === false && \in_array($request->getMethod(), ['post', 'put', 'delete'], true) === true) { - $token = $request->getInput()->get(static::POST_KEY, null, 'post'); + $token = $request->getInputHandler()->get(static::POST_KEY, null, 'post'); // If the token is not posted, check headers for valid x-csrf-token if ($token === null) { @@ -85,7 +85,7 @@ class BaseCsrfVerifier implements IMiddleware } - public function getTokenProvider() + public function getTokenProvider(): ITokenProvider { return $this->tokenProvider; } @@ -94,7 +94,7 @@ class BaseCsrfVerifier implements IMiddleware * Set token provider * @param ITokenProvider $provider */ - public function setTokenProvider(ITokenProvider $provider) + public function setTokenProvider(ITokenProvider $provider): void { $this->tokenProvider = $provider; } diff --git a/src/Pecee/Http/Middleware/IMiddleware.php b/src/Pecee/Http/Middleware/IMiddleware.php index 731144d..68daf8b 100644 --- a/src/Pecee/Http/Middleware/IMiddleware.php +++ b/src/Pecee/Http/Middleware/IMiddleware.php @@ -1,4 +1,5 @@ setUrl($this->getHeader('unencoded-url', $this->getHeader('request-uri'))); - $this->input = new Input($this); - $this->method = strtolower($this->input->get('_method', $this->getHeader('request-method'))); + $this->inputHandler = new InputHandler($this); + $this->method = strtolower($this->inputHandler->get('_method', $this->getHeader('request-method'))); } - protected function parseHeaders() + protected function parseHeaders(): void { $this->headers = []; @@ -56,7 +56,7 @@ class Request } - public function isSecure() + public function isSecure(): bool { return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443; } @@ -64,23 +64,23 @@ class Request /** * @return Url */ - public function getUrl() + public function getUrl(): Url { return $this->url; } /** - * @return string + * @return string|null */ - public function getHost() + public function getHost(): ?string { return $this->host; } /** - * @return string + * @return string|null */ - public function getMethod() + public function getMethod(): ?string { return $this->method; } @@ -89,7 +89,7 @@ class Request * Get http basic auth user * @return string|null */ - public function getUser() + public function getUser(): ?string { return $this->getHeader('php-auth-user'); } @@ -98,7 +98,7 @@ class Request * Get http basic auth password * @return string|null */ - public function getPassword() + public function getPassword(): ?string { return $this->getHeader('php-auth-pw'); } @@ -107,16 +107,16 @@ class Request * Get all headers * @return array */ - public function getHeaders() + public function getHeaders(): array { return $this->headers; } /** * Get id address - * @return string + * @return string|null */ - public function getIp() + public function getIp(): ?string { if ($this->getHeader('http-cf-connecting-ip') !== null) { return $this->getHeader('http-cf-connecting-ip'); @@ -133,27 +133,27 @@ class Request * Get remote address/ip * * @alias static::getIp - * @return string + * @return string|null */ - public function getRemoteAddr() + public function getRemoteAddr(): ?string { return $this->getIp(); } /** * Get referer - * @return string + * @return string|null */ - public function getReferer() + public function getReferer(): ?string { return $this->getHeader('http-referer'); } /** * Get user agent - * @return string + * @return string|null */ - public function getUserAgent() + public function getUserAgent(): ?string { return $this->getHeader('http-user-agent'); } @@ -166,18 +166,18 @@ class Request * * @return string|null */ - public function getHeader($name, $defaultValue = null) + public function getHeader($name, $defaultValue = null): ?string { - return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue; + return $this->headers[strtolower($name)] ?? $defaultValue; } /** * Get input class - * @return Input + * @return InputHandler */ - public function getInput() + public function getInputHandler(): InputHandler { - return $this->input; + return $this->inputHandler; } /** @@ -187,9 +187,9 @@ class Request * * @return bool */ - public function isFormatAccepted($format) + public function isFormatAccepted($format): bool { - return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) > -1); + return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) !== false); } /** @@ -197,7 +197,7 @@ class Request * * @return bool */ - public function isAjax() + public function isAjax(): bool { return (strtolower($this->getHeader('http-x-requested-with')) === 'xmlhttprequest'); } @@ -206,7 +206,7 @@ class Request * Get accept formats * @return array */ - public function getAcceptFormats() + public function getAcceptFormats(): array { return explode(',', $this->getHeader('http-accept')); } @@ -215,7 +215,7 @@ class Request * @param string|Url $url * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public function setUrl($url) + public function setUrl($url): void { $this->url = ($url instanceof Url) ? $url : new Url($url); } @@ -223,7 +223,7 @@ class Request /** * @param string $host */ - public function setHost($host) + public function setHost($host): void { $this->host = $host; } @@ -231,7 +231,7 @@ class Request /** * @param string $method */ - public function setMethod($method) + public function setMethod($method): void { $this->method = $method; } @@ -242,7 +242,7 @@ class Request * @param ILoadableRoute $route * @return static */ - public function setRewriteRoute(ILoadableRoute $route) + public function setRewriteRoute(ILoadableRoute $route): self { $this->hasRewrite = true; $this->rewriteRoute = SimpleRouter::addDefaultNamespace($route); @@ -255,7 +255,7 @@ class Request * * @return ILoadableRoute|null */ - public function getRewriteRoute() + public function getRewriteRoute(): ?ILoadableRoute { return $this->rewriteRoute; } @@ -263,9 +263,9 @@ class Request /** * Get rewrite url * - * @return string + * @return string|null */ - public function getRewriteUrl() + public function getRewriteUrl(): ?string { return $this->rewriteUrl; } @@ -276,7 +276,7 @@ class Request * @param string $rewriteUrl * @return static */ - public function setRewriteUrl($rewriteUrl) + public function setRewriteUrl(string $rewriteUrl): self { $this->hasRewrite = true; $this->rewriteUrl = rtrim($rewriteUrl, '/') . '/'; @@ -286,10 +286,10 @@ class Request /** * Set rewrite callback - * @param string $callback + * @param string|\Closure $callback * @return static */ - public function setRewriteCallback($callback) + public function setRewriteCallback($callback): self { $this->hasRewrite = true; @@ -300,37 +300,60 @@ class Request * Get loaded route * @return ILoadableRoute|null */ - public function getLoadedRoute() + public function getLoadedRoute(): ?ILoadableRoute { - return $this->loadedRoute; + return (\count($this->loadedRoutes) > 0) ? end($this->loadedRoutes) : null; } /** - * Set loaded route + * Get all loaded routes * - * @param ILoadableRoute $route + * @return array + */ + public function getLoadedRoutes(): array + { + return $this->loadedRoutes; + } + + /** + * Set loaded routes + * + * @param array $routes * @return static */ - public function setLoadedRoute(ILoadableRoute $route) + public function setLoadedRoutes(array $routes): self { - $this->loadedRoute = $route; + $this->loadedRoutes = $routes; return $this; } - public function hasRewrite() + /** + * Added loaded route + * + * @param ILoadableRoute $route + * @return static + */ + public function addLoadedRoute(ILoadableRoute $route): self + { + $this->loadedRoutes[] = $route; + + return $this; + } + + public function hasRewrite(): bool { return $this->hasRewrite; } - public function setHasRewrite($value) + public function setHasRewrite($value): self { $this->hasRewrite = $value; return $this; } - public function isRewrite($url) + public function isRewrite($url): bool { return ($this->rewriteUrl === $url); } @@ -347,7 +370,7 @@ class Request public function __get($name) { - return isset($this->data[$name]) ? $this->data[$name] : null; + return $this->data[$name] ?? null; } } \ No newline at end of file diff --git a/src/Pecee/Http/Response.php b/src/Pecee/Http/Response.php index f9f46f1..9920132 100644 --- a/src/Pecee/Http/Response.php +++ b/src/Pecee/Http/Response.php @@ -19,7 +19,7 @@ class Response * @param int $code * @return static */ - public function httpCode($code) + public function httpCode($code): self { http_response_code($code); @@ -32,7 +32,7 @@ class Response * @param string $url * @param int $httpCode */ - public function redirect($url, $httpCode = null) + public function redirect($url, $httpCode = null): void { if ($httpCode !== null) { $this->httpCode($httpCode); @@ -42,7 +42,7 @@ class Response exit(0); } - public function refresh() + public function refresh(): void { $this->redirect($this->request->getUrl()->getOriginalUrl()); } @@ -52,7 +52,7 @@ class Response * @param string $name * @return static */ - public function auth($name = '') + public function auth($name = ''): self { $this->headers([ 'WWW-Authenticate: Basic realm="' . $name . '"', @@ -62,7 +62,7 @@ class Response return $this; } - public function cache($eTag, $lastModified = 2592000) + public function cache($eTag, $lastModified = 2592000): self { $this->headers([ @@ -77,8 +77,7 @@ class Response if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModified)) { $this->header('HTTP/1.1 304 Not Modified'); - - exit(); + exit(0); } return $this; @@ -91,9 +90,9 @@ class Response * @param int $dept JSON debt. * @throws InvalidArgumentException */ - public function json($value, $options = null, $dept = 512) + public function json($value, $options = null, $dept = 512): void { - if (($value instanceof \JsonSerializable) === false && is_array($value) === false) { + 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.'); } @@ -107,7 +106,7 @@ class Response * @param string $value * @return static */ - public function header($value) + public function header($value): self { header($value); @@ -119,7 +118,7 @@ class Response * @param array $headers * @return static */ - public function headers(array $headers) + public function headers(array $headers): self { foreach ($headers as $header) { $this->header($header); diff --git a/src/Pecee/Http/Security/CookieTokenProvider.php b/src/Pecee/Http/Security/CookieTokenProvider.php index 209067f..a44f639 100644 --- a/src/Pecee/Http/Security/CookieTokenProvider.php +++ b/src/Pecee/Http/Security/CookieTokenProvider.php @@ -6,7 +6,7 @@ use Pecee\Http\Security\Exceptions\SecurityException; class CookieTokenProvider implements ITokenProvider { - const CSRF_KEY = 'CSRF-TOKEN'; + public const CSRF_KEY = 'CSRF-TOKEN'; protected $token; protected $cookieTimeoutMinutes = 120; @@ -30,24 +30,13 @@ class CookieTokenProvider implements ITokenProvider * @return string * @throws SecurityException */ - public function generateToken() + public function generateToken(): string { - if (function_exists('random_bytes') === true) { - try { - return bin2hex(random_bytes(32)); - } catch(\Exception $e) { - throw new SecurityException($e->getMessage(), (int)$e->getCode(), $e->getPrevious()); - } + try { + return bin2hex(random_bytes(32)); + } catch (\Exception $e) { + throw new SecurityException($e->getMessage(), (int)$e->getCode(), $e->getPrevious()); } - - $isSourceStrong = false; - - $random = openssl_random_pseudo_bytes(32, $isSourceStrong); - if ($isSourceStrong === false || $random === false) { - throw new SecurityException('IV generation failed'); - } - - return $random; } /** @@ -56,7 +45,7 @@ class CookieTokenProvider implements ITokenProvider * @param string $token * @return bool */ - public function validate($token) + public function validate($token): bool { if ($token !== null && $this->getToken() !== null) { return hash_equals($token, $this->getToken()); @@ -71,7 +60,7 @@ class CookieTokenProvider implements ITokenProvider * * @param string $token */ - public function setToken($token) + public function setToken($token): void { $this->token = $token; setcookie(static::CSRF_KEY, $token, time() + 60 * $this->cookieTimeoutMinutes, '/'); @@ -82,17 +71,17 @@ class CookieTokenProvider implements ITokenProvider * @param string|null $defaultValue * @return string|null */ - public function getToken($defaultValue = null) + public function getToken($defaultValue = null): ?string { $this->token = ($this->hasToken() === true) ? $_COOKIE[static::CSRF_KEY] : null; - return ($this->token !== null) ? $this->token : $defaultValue; + return $this->token ?? $defaultValue; } /** * Refresh existing token */ - public function refresh() + public function refresh(): void { if ($this->token !== null) { $this->setToken($this->token); @@ -103,7 +92,7 @@ class CookieTokenProvider implements ITokenProvider * Returns whether the csrf token has been defined * @return bool */ - public function hasToken() + public function hasToken(): bool { return isset($_COOKIE[static::CSRF_KEY]); } @@ -112,7 +101,7 @@ class CookieTokenProvider implements ITokenProvider * Get timeout for cookie in minutes * @return int */ - public function getCookieTimeoutMinutes() + public function getCookieTimeoutMinutes(): int { return $this->cookieTimeoutMinutes; } @@ -121,7 +110,7 @@ class CookieTokenProvider implements ITokenProvider * Set cookie timeout in minutes * @param $minutes */ - public function setCookieTimeoutMinutes($minutes) + public function setCookieTimeoutMinutes($minutes): void { $this->cookieTimeoutMinutes = $minutes; } diff --git a/src/Pecee/Http/Security/ITokenProvider.php b/src/Pecee/Http/Security/ITokenProvider.php index 6b1ee72..7b6307f 100644 --- a/src/Pecee/Http/Security/ITokenProvider.php +++ b/src/Pecee/Http/Security/ITokenProvider.php @@ -8,7 +8,7 @@ interface ITokenProvider /** * Refresh existing token */ - public function refresh(); + public function refresh(): void; /** * Validate valid CSRF token @@ -16,6 +16,14 @@ interface ITokenProvider * @param string $token * @return bool */ - public function validate($token); + public function validate($token): bool; + + /** + * Get token token + * + * @param string|null $defaultValue + * @return string|null + */ + public function getToken($defaultValue = null); } \ No newline at end of file diff --git a/src/Pecee/Http/Url.php b/src/Pecee/Http/Url.php index 6256c3e..e863a76 100644 --- a/src/Pecee/Http/Url.php +++ b/src/Pecee/Http/Url.php @@ -38,7 +38,7 @@ class Url * Check if url is using a secure protocol like https * @return bool */ - public function isSecure() + public function isSecure(): bool { return (strtolower($this->getScheme()) === 'https'); } @@ -47,7 +47,7 @@ class Url * Checks if url is relative * @return bool */ - public function isRelative() + public function isRelative(): bool { return ($this->getHost() === null); } @@ -56,7 +56,7 @@ class Url * Get url scheme * @return string|null */ - public function getScheme() + public function getScheme(): ?string { return $this->data['scheme']; } @@ -65,7 +65,7 @@ class Url * Get url host * @return string|null */ - public function getHost() + public function getHost(): ?string { return $this->data['host']; } @@ -74,7 +74,7 @@ class Url * Get url port * @return int|null */ - public function getPort() + public function getPort(): ?int { return ($this->data['port'] !== null) ? (int)$this->data['port'] : null; } @@ -83,7 +83,7 @@ class Url * Parse username from url * @return string|null */ - public function getUserName() + public function getUserName(): ?string { return $this->data['user']; } @@ -92,7 +92,7 @@ class Url * Parse password from url * @return string|null */ - public function getPassword() + public function getPassword(): ?string { return $this->data['pass']; } @@ -101,7 +101,7 @@ class Url * Get path from url * @return string */ - public function getPath() + public function getPath(): ?string { return $this->data['path']; } @@ -110,7 +110,7 @@ class Url * Get querystring from url * @return string|null */ - public function getQueryString() + public function getQueryString(): ?string { return $this->data['query']; } @@ -119,7 +119,7 @@ class Url * Get fragment from url (everything after #) * @return string|null */ - public function getFragment() + public function getFragment(): ?string { return $this->data['fragment']; } @@ -127,7 +127,7 @@ class Url /** * @return string */ - public function getOriginalUrl() + public function getOriginalUrl(): string { return $this->originalUrl; } @@ -139,7 +139,7 @@ class Url * @throws MalformedUrlException * @return array */ - public function parseUrl($url, $component = -1) + public function parseUrl($url, $component = -1): array { $encodedUrl = preg_replace_callback( '/[^:\/@?&=#]+/u', @@ -158,11 +158,16 @@ class Url return array_map('urldecode', $parts); } + public function contains($value): bool + { + return (stripos($this->getOriginalUrl(), $value) === false); + } + /** * Returns data array with information about the url * @return array */ - public function getData() + public function getData(): array { return $this->data; } diff --git a/src/Pecee/SimpleRouter/IRouterBootManager.php b/src/Pecee/SimpleRouter/IRouterBootManager.php index 08090ed..c3686ec 100644 --- a/src/Pecee/SimpleRouter/IRouterBootManager.php +++ b/src/Pecee/SimpleRouter/IRouterBootManager.php @@ -1,4 +1,5 @@ getMiddlewares() as $middleware) { - if (is_object($middleware) === false) { + if (\is_object($middleware) === false) { $middleware = $this->loadClass($middleware); } @@ -42,7 +42,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute } } - public function matchRegex(Request $request, $url) + public function matchRegex(Request $request, $url) : ?bool { /* Match on custom defined regular expression */ @@ -59,7 +59,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string $url * @return static */ - public function setUrl($url) + public function setUrl($url) : self { $this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/'; @@ -75,7 +75,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute return $this; } - public function getUrl() + public function getUrl() : string { return $this->url; } @@ -89,13 +89,13 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string|null $name * @return string */ - public function findUrl($method = null, $parameters = null, $name = null) + public function findUrl($method = null, $parameters = null, $name = null) : string { $url = $this->getUrl(); $group = $this->getGroup(); - if ($group !== null && count($group->getDomains()) !== 0) { + if ($group !== null && \count($group->getDomains()) !== 0) { $url = '//' . $group->getDomains()[0] . $url; } @@ -114,7 +114,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute foreach (array_keys($params) as $param) { - if ($parameters === '' || (is_array($parameters) === true && count($parameters) === 0)) { + if ($parameters === '' || (\is_array($parameters) === true && \count($parameters) === 0)) { $value = ''; } else { $p = (array)$parameters; @@ -144,7 +144,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * * @return string */ - public function getName() + public function getName() : string { return $this->name; } @@ -155,9 +155,9 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string $name * @return bool */ - public function hasName($name) + public function hasName($name) : bool { - return (strtolower($this->name) === strtolower($name)); + return strtolower($this->name) === strtolower($name); } /** @@ -166,7 +166,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string $regex * @return static */ - public function setMatch($regex) + public function setMatch($regex) : ILoadableRoute { $this->regex = $regex; @@ -178,7 +178,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * * @return string */ - public function getMatch() + public function getMatch() : string { return $this->regex; } @@ -191,7 +191,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string|array $name * @return static */ - public function name($name) + public function name($name) : ILoadableRoute { return $this->setName($name); } @@ -200,9 +200,9 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * Sets the router name, which makes it easier to obtain the url or router at a later point. * * @param string $name - * @return static $this + * @return static */ - public function setName($name) + public function setName($name) : ILoadableRoute { $this->name = $name; @@ -216,7 +216,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param bool $merge * @return static */ - public function setSettings(array $values, $merge = false) + public function setSettings(array $values, $merge = false): IRoute { if (isset($values['as']) === true) { diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 8ebd681..1cb70bc 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -8,15 +8,15 @@ use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; abstract class Route implements IRoute { - const PARAMETERS_REGEX_FORMAT = '%s([\w]+)(\%s?)%s'; - const PARAMETERS_DEFAULT_REGEX = '[\w]+'; + protected const PARAMETERS_REGEX_FORMAT = '%s([\w]+)(\%s?)%s'; + protected const PARAMETERS_DEFAULT_REGEX = '[\w]+'; - const REQUEST_TYPE_GET = 'get'; - const REQUEST_TYPE_POST = 'post'; - const REQUEST_TYPE_PUT = 'put'; - const REQUEST_TYPE_PATCH = 'patch'; - const REQUEST_TYPE_OPTIONS = 'options'; - const REQUEST_TYPE_DELETE = 'delete'; + public const REQUEST_TYPE_GET = 'get'; + public const REQUEST_TYPE_POST = 'post'; + public const REQUEST_TYPE_PUT = 'put'; + public const REQUEST_TYPE_PATCH = 'patch'; + public const REQUEST_TYPE_OPTIONS = 'options'; + public const REQUEST_TYPE_DELETE = 'delete'; public static $requestTypes = [ self::REQUEST_TYPE_GET, @@ -75,10 +75,10 @@ abstract class Route implements IRoute * Render route * * @param Request $request - * @return string|mixed + * @return string|null * @throws NotFoundHttpException */ - public function renderRoute(Request $request) + public function renderRoute(Request $request): ?string { $callback = $this->getCallback(); @@ -97,9 +97,9 @@ abstract class Route implements IRoute } /* Render callback function */ - if (is_callable($callback) === true) { + if (\is_callable($callback) === true) { /* When the callback is a function */ - return call_user_func_array($callback, $parameters); + return \call_user_func_array($callback, $parameters); } /* When the callback is a class + method */ @@ -116,7 +116,7 @@ abstract class Route implements IRoute throw new NotFoundHttpException(sprintf('Method "%s" does not exist in class "%s"', $method, $className), 404); } - return call_user_func_array([$class, $method], $parameters); + return \call_user_func_array([$class, $method], $parameters); } protected function parseParameters($route, $url, $parameterRegex = null) @@ -138,7 +138,7 @@ abstract class Route implements IRoute $regex = ''; - if ($key < count($parameters[1])) { + if ($key < \count($parameters[1])) { $name = $parameters[1][$key]; @@ -151,7 +151,7 @@ abstract class Route implements IRoute if ($parameterRegex !== null) { $regex = $parameterRegex; } else { - $regex = ($this->defaultParameterRegex === null) ? static::PARAMETERS_DEFAULT_REGEX : $this->defaultParameterRegex; + $regex = $this->defaultParameterRegex ?? static::PARAMETERS_DEFAULT_REGEX; } } @@ -189,9 +189,9 @@ abstract class Route implements IRoute * * @return string */ - public function getIdentifier() + public function getIdentifier(): string { - if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) { + if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) { return $this->callback; } @@ -202,9 +202,9 @@ abstract class Route implements IRoute * Set allowed request methods * * @param array $methods - * @return static $this + * @return static */ - public function setRequestMethods(array $methods) + public function setRequestMethods(array $methods): IRoute { $this->requestMethods = $methods; @@ -216,7 +216,7 @@ abstract class Route implements IRoute * * @return array */ - public function getRequestMethods() + public function getRequestMethods(): array { return $this->requestMethods; } @@ -224,7 +224,7 @@ abstract class Route implements IRoute /** * @return IRoute|null */ - public function getParent() + public function getParent(): ?IRoute { return $this->parent; } @@ -234,7 +234,7 @@ abstract class Route implements IRoute * * @return IGroupRoute|null */ - public function getGroup() + public function getGroup(): ?IGroupRoute { return $this->group; } @@ -243,9 +243,9 @@ abstract class Route implements IRoute * Set group * * @param IGroupRoute $group - * @return static $this + * @return static */ - public function setGroup(IGroupRoute $group) + public function setGroup(IGroupRoute $group): IRoute { $this->group = $group; @@ -259,9 +259,9 @@ abstract class Route implements IRoute * Set parent route * * @param IRoute $parent - * @return static $this + * @return static */ - public function setParent(IRoute $parent) + public function setParent(IRoute $parent): IRoute { $this->parent = $parent; @@ -274,7 +274,7 @@ abstract class Route implements IRoute * @param string $callback * @return static */ - public function setCallback($callback) + public function setCallback($callback): IRoute { $this->callback = $callback; @@ -282,16 +282,16 @@ abstract class Route implements IRoute } /** - * @return string + * @return string|callable */ public function getCallback() { return $this->callback; } - public function getMethod() + public function getMethod(): ?string { - if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) { + if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) { $tmp = explode('@', $this->callback); return $tmp[1]; @@ -302,7 +302,7 @@ abstract class Route implements IRoute public function getClass() { - if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) { + if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) { $tmp = explode('@', $this->callback); return $tmp[0]; @@ -311,7 +311,7 @@ abstract class Route implements IRoute return null; } - public function setMethod($method) + public function setMethod($method): IRoute { $this->callback = sprintf('%s@%s', $this->getClass(), $method); @@ -353,11 +353,11 @@ abstract class Route implements IRoute } /** - * @return string + * @return string|null */ - public function getNamespace() + public function getNamespace(): ?string { - return ($this->namespace === null) ? $this->defaultNamespace : $this->namespace; + return $this->namespace ?? $this->defaultNamespace; } /** @@ -365,7 +365,7 @@ abstract class Route implements IRoute * * @return array */ - public function toArray() + public function toArray(): array { $values = []; @@ -373,15 +373,15 @@ abstract class Route implements IRoute $values['namespace'] = $this->namespace; } - if (count($this->requestMethods) !== 0) { + if (\count($this->requestMethods) !== 0) { $values['method'] = $this->requestMethods; } - if (count($this->where) !== 0) { + if (\count($this->where) !== 0) { $values['where'] = $this->where; } - if (count($this->middlewares) !== 0) { + if (\count($this->middlewares) !== 0) { $values['middleware'] = $this->middlewares; } @@ -434,7 +434,7 @@ abstract class Route implements IRoute * * @return array */ - public function getWhere() + public function getWhere(): array { return $this->where; } @@ -470,12 +470,12 @@ abstract class Route implements IRoute * * @return array */ - public function getParameters() + public function getParameters(): array { /* Sort the parameters after the user-defined param order, if any */ $parameters = []; - if (count($this->originalParameters) !== 0) { + if (\count($this->originalParameters) !== 0) { $parameters = $this->originalParameters; } @@ -494,7 +494,7 @@ abstract class Route implements IRoute * If this is the first time setting parameters we store them so we * later can organize the array, in case somebody tried to sort the array. */ - if (count($parameters) !== 0 && count($this->originalParameters) === 0) { + if (\count($parameters) !== 0 && \count($this->originalParameters) === 0) { $this->originalParameters = $parameters; } @@ -523,7 +523,7 @@ abstract class Route implements IRoute * @param IMiddleware|string $middleware * @return static */ - public function addMiddleware($middleware) + public function addMiddleware($middleware): IRoute { $this->middlewares[] = $middleware; @@ -534,9 +534,9 @@ abstract class Route implements IRoute * Set middlewares array * * @param array $middlewares - * @return $this + * @return static */ - public function setMiddlewares(array $middlewares) + public function setMiddlewares(array $middlewares): IRoute { $this->middlewares = $middlewares; @@ -546,7 +546,7 @@ abstract class Route implements IRoute /** * @return array */ - public function getMiddlewares() + public function getMiddlewares(): array { return $this->middlewares; } @@ -570,7 +570,7 @@ abstract class Route implements IRoute * * @return string */ - public function getDefaultParameterRegex() + public function getDefaultParameterRegex(): string { return $this->defaultParameterRegex; } diff --git a/src/Pecee/SimpleRouter/Route/RouteController.php b/src/Pecee/SimpleRouter/Route/RouteController.php index 25cfe4f..898df28 100644 --- a/src/Pecee/SimpleRouter/Route/RouteController.php +++ b/src/Pecee/SimpleRouter/Route/RouteController.php @@ -24,7 +24,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string $name * @return bool */ - public function hasName($name) + public function hasName($name): bool { if ($this->name === null) { return false; @@ -35,7 +35,7 @@ class RouteController extends LoadableRoute implements IControllerRoute $method = substr($name, strrpos($name, '.') + 1); $newName = substr($name, 0, strrpos($name, '.')); - if (in_array($method, $this->names, false) === true && strtolower($this->name) === strtolower($newName)) { + if (\in_array($method, $this->names, true) === true && strtolower($this->name) === strtolower($newName)) { return true; } } @@ -49,7 +49,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string|null $name * @return string */ - public function findUrl($method = null, $parameters = null, $name = null) + public function findUrl($method = null, $parameters = null, $name = null): string { if (strpos($name, '.') !== false) { $found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, false); @@ -67,7 +67,7 @@ class RouteController extends LoadableRoute implements IControllerRoute foreach (static::$requestTypes as $requestType) { if (stripos($method, $requestType) === 0) { - $method = (string)substr($method, strlen($requestType)); + $method = (string)substr($method, \strlen($requestType)); break; } } @@ -77,7 +77,7 @@ class RouteController extends LoadableRoute implements IControllerRoute $group = $this->getGroup(); - if ($group !== null && count($group->getDomains()) !== 0) { + if ($group !== null && \count($group->getDomains()) !== 0) { $url .= '//' . $group->getDomains()[0]; } @@ -86,9 +86,9 @@ class RouteController extends LoadableRoute implements IControllerRoute return '/' . trim($url, '/') . '/'; } - public function matchRoute($url, Request $request) + public function matchRoute($url, Request $request): bool { - if($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { + if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { return false; } @@ -102,12 +102,12 @@ class RouteController extends LoadableRoute implements IControllerRoute $strippedUrl = trim(str_ireplace($this->url, '/', $url), '/'); $path = explode('/', $strippedUrl); - if (count($path) !== 0) { + if (\count($path) !== 0) { $method = (isset($path[0]) === false || trim($path[0]) === '') ? $this->defaultMethod : $path[0]; $this->method = $request->getMethod() . ucfirst($method); - $this->parameters = array_slice($path, 1); + $this->parameters = \array_slice($path, 1); // Set callback $this->setCallback($this->controller . '@' . $this->method); @@ -123,7 +123,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * * @return string */ - public function getController() + public function getController(): string { return $this->controller; } @@ -134,7 +134,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string $controller * @return static */ - public function setController($controller) + public function setController($controller): IControllerRoute { $this->controller = $controller; @@ -144,9 +144,9 @@ class RouteController extends LoadableRoute implements IControllerRoute /** * Return active method * - * @return string + * @return string|null */ - public function getMethod() + public function getMethod(): ?string { return $this->method; } @@ -157,7 +157,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string $method * @return static */ - public function setMethod($method) + public function setMethod($method): IRoute { $this->method = $method; @@ -171,7 +171,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param bool $merge * @return static */ - public function setSettings(array $values, $merge = false) + public function setSettings(array $values, $merge = false): IRoute { if (isset($values['names']) === true) { $this->names = $values['names']; diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index 4e3bde1..e2506d7 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -18,9 +18,9 @@ class RouteGroup extends Route implements IGroupRoute * @param Request $request * @return bool */ - public function matchDomain(Request $request) + public function matchDomain(Request $request): bool { - if ($this->domains === null || count($this->domains) === 0) { + if ($this->domains === null || \count($this->domains) === 0) { return true; } @@ -28,7 +28,7 @@ class RouteGroup extends Route implements IGroupRoute $parameters = $this->parseParameters($domain, $request->getHost(), '.*'); - if ($parameters !== null && count($parameters) !== 0) { + if ($parameters !== null && \count($parameters) !== 0) { $this->parameters = $parameters; @@ -46,9 +46,9 @@ class RouteGroup extends Route implements IGroupRoute * @param Request $request * @return bool */ - public function matchRoute($url, Request $request) + public function matchRoute($url, Request $request): bool { - if($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { + if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { return false; } @@ -64,9 +64,9 @@ class RouteGroup extends Route implements IGroupRoute * Add exception handler * * @param IExceptionHandler|string $handler - * @return static $this + * @return static */ - public function addExceptionHandler($handler) + public function addExceptionHandler($handler): IGroupRoute { $this->exceptionHandlers[] = $handler; @@ -77,9 +77,9 @@ class RouteGroup extends Route implements IGroupRoute * Set exception-handlers for group * * @param array $handlers - * @return static $this + * @return static */ - public function setExceptionHandlers(array $handlers) + public function setExceptionHandlers(array $handlers): IGroupRoute { $this->exceptionHandlers = $handlers; @@ -91,7 +91,7 @@ class RouteGroup extends Route implements IGroupRoute * * @return array */ - public function getExceptionHandlers() + public function getExceptionHandlers(): array { return $this->exceptionHandlers; } @@ -101,7 +101,7 @@ class RouteGroup extends Route implements IGroupRoute * * @return array */ - public function getDomains() + public function getDomains(): array { return $this->domains; } @@ -110,9 +110,9 @@ class RouteGroup extends Route implements IGroupRoute * Set allowed domains for group. * * @param array $domains - * @return $this + * @return static */ - public function setDomains(array $domains) + public function setDomains(array $domains): IGroupRoute { $this->domains = $domains; @@ -123,7 +123,7 @@ class RouteGroup extends Route implements IGroupRoute * @param string $prefix * @return static */ - public function setPrefix($prefix) + public function setPrefix($prefix): IGroupRoute { $this->prefix = '/' . trim($prefix, '/'); @@ -133,9 +133,9 @@ class RouteGroup extends Route implements IGroupRoute /** * Set prefix that child-routes will inherit. * - * @return string + * @return string|null */ - public function getPrefix() + public function getPrefix(): ?string { return $this->prefix; } @@ -147,7 +147,7 @@ class RouteGroup extends Route implements IGroupRoute * @param bool $merge * @return static */ - public function setSettings(array $values, $merge = false) + public function setSettings(array $values, $merge = false): IRoute { if (isset($values['prefix']) === true) { @@ -183,7 +183,7 @@ class RouteGroup extends Route implements IGroupRoute * * @return array */ - public function toArray() + public function toArray(): array { $values = []; @@ -195,7 +195,7 @@ class RouteGroup extends Route implements IGroupRoute $values['as'] = $this->name; } - if (count($this->parameters) !== 0) { + if (\count($this->parameters) !== 0) { $values['parameters'] = $this->parameters; } diff --git a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php index 51b71e0..9656554 100644 --- a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php +++ b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php @@ -15,12 +15,12 @@ class RoutePartialGroup extends RouteGroup implements IPartialGroupRoute * @param Request $request * @return bool */ - public function matchRoute($url, Request $request) + public function matchRoute($url, Request $request): bool { - if($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { + if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { return false; } - + if ($this->prefix !== null) { /* Parse parameters from current route */ $parameters = $this->parseParameters($this->prefix, $url); diff --git a/src/Pecee/SimpleRouter/Route/RouteResource.php b/src/Pecee/SimpleRouter/Route/RouteResource.php index 108e51a..7d454b4 100644 --- a/src/Pecee/SimpleRouter/Route/RouteResource.php +++ b/src/Pecee/SimpleRouter/Route/RouteResource.php @@ -42,7 +42,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute * @param string $name * @return bool */ - public function hasName($name) + public function hasName($name): bool { if ($this->name === null) { return false; @@ -60,7 +60,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute return (strtolower($this->name) === strtolower($name)); } - public function findUrl($method = null, $parameters = null, $name = null) + public function findUrl($method = null, $parameters = null, $name = null): string { $url = array_search($name, $this->names, false); if ($url !== false) { @@ -77,9 +77,9 @@ class RouteResource extends LoadableRoute implements IControllerRoute return true; } - public function matchRoute($url, Request $request) + public function matchRoute($url, Request $request): bool { - if($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { + if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { return false; } @@ -114,7 +114,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute } // Update - if ($id !== null && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], false) === true) { + if ($id !== null && \in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], true) === true) { return $this->call($this->methodNames['update']); } @@ -145,7 +145,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute /** * @return string */ - public function getController() + public function getController(): string { return $this->controller; } @@ -154,14 +154,14 @@ class RouteResource extends LoadableRoute implements IControllerRoute * @param string $controller * @return static */ - public function setController($controller) + public function setController($controller): IControllerRoute { $this->controller = $controller; return $this; } - public function setName($name) + public function setName($name): ILoadableRoute { $this->name = $name; @@ -194,9 +194,9 @@ class RouteResource extends LoadableRoute implements IControllerRoute /** * Get method names * - * @return array $this + * @return array */ - public function getMethodNames() + public function getMethodNames(): array { return $this->methodNames; } @@ -208,7 +208,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute * @param bool $merge * @return static */ - public function setSettings(array $values, $merge = false) + public function setSettings(array $values, $merge = false): IRoute { if (isset($values['names']) === true) { $this->names = $values['names']; diff --git a/src/Pecee/SimpleRouter/Route/RouteUrl.php b/src/Pecee/SimpleRouter/Route/RouteUrl.php index 19eb137..cdfcb60 100644 --- a/src/Pecee/SimpleRouter/Route/RouteUrl.php +++ b/src/Pecee/SimpleRouter/Route/RouteUrl.php @@ -12,9 +12,9 @@ class RouteUrl extends LoadableRoute $this->setCallback($callback); } - public function matchRoute($url, Request $request) + public function matchRoute($url, Request $request): bool { - if($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { + if ($this->getGroup() !== null && $this->getGroup()->matchRoute($url, $request) === false) { return false; } diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index a34e350..c4d8f22 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -78,7 +78,7 @@ class Router /** * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public function reset() + public function reset() : void { $this->processingRoute = false; $this->request = new Request(); @@ -94,7 +94,7 @@ class Router * @param IRoute $route * @return IRoute */ - public function addRoute(IRoute $route) + public function addRoute(IRoute $route) : IRoute { /* * If a route is currently being processed, that means that the route being added are rendered from the parent @@ -115,13 +115,13 @@ class Router * @param IRoute $route * @throws NotFoundHttpException */ - protected function renderAndProcess(IRoute $route) { + protected function renderAndProcess(IRoute $route) : void { $this->processingRoute = true; $route->renderRoute($this->request); $this->processingRoute = false; - if (count($this->routeStack) !== 0) { + if (\count($this->routeStack) !== 0) { /* Pop and grab the routes added when executing group callback earlier */ $stack = $this->routeStack; @@ -139,7 +139,7 @@ class Router * @param IGroupRoute|null $group * @throws NotFoundHttpException */ - protected function processRoutes(array $routes, IGroupRoute $group = null) + protected function processRoutes(array $routes, IGroupRoute $group = null) : void { // Loop through each route-request $exceptionHandlers = []; @@ -149,7 +149,7 @@ class Router return; } - $url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUrl()->getPath(); + $url = $this->request->getRewriteUrl() ?? $this->request->getUrl()->getPath(); /* @var $route IRoute */ foreach ($routes as $route) { @@ -165,7 +165,7 @@ class Router if ($route->matchRoute($url, $this->request) === true) { /* Add exception handlers */ - if (count($route->getExceptionHandlers()) !== 0) { + if (\count($route->getExceptionHandlers()) !== 0) { /** @noinspection AdditionOperationOnArraysInspection */ $exceptionHandlers += $route->getExceptionHandlers(); } @@ -199,7 +199,7 @@ class Router * @throws NotFoundHttpException * @return void */ - public function loadRoutes() + public function loadRoutes() : void { /* Initialize boot-managers */ /* @var $manager IRouterBootManager */ @@ -215,11 +215,11 @@ class Router * Routes the request * * @param bool $rewrite - * @return string|mixed + * @return string|null * @throws HttpException * @throws \Exception */ - public function routeRequest($rewrite = false) + public function routeRequest($rewrite = false) : ?string { $routeNotAllowed = false; @@ -237,7 +237,7 @@ class Router $this->request->setHasRewrite(false); } - $url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUrl()->getPath(); + $url = $this->request->getRewriteUrl() ?? $this->request->getUrl()->getPath(); /* @var $route ILoadableRoute */ foreach ($this->processedRoutes as $key => $route) { @@ -246,7 +246,7 @@ class Router if ($route->matchRoute($url, $this->request) === true) { /* Check if request method matches */ - if (count($route->getRequestMethods()) !== 0 && in_array($this->request->getMethod(), $route->getRequestMethods(), false) === false) { + if (\count($route->getRequestMethods()) !== 0 && \in_array($this->request->getMethod(), $route->getRequestMethods(), true) === false) { $routeNotAllowed = true; continue; } @@ -262,7 +262,7 @@ class Router /* Render route */ $routeNotAllowed = false; - $this->request->setLoadedRoute($route); + $this->request->addLoadedRoute($route); $output = $route->renderRoute($this->request); @@ -287,7 +287,7 @@ class Router $this->handleException(new HttpException($message, 403)); } - if ($this->request->getLoadedRoute() === null) { + if (\count($this->request->getLoadedRoutes()) === 0) { $rewriteUrl = $this->request->getRewriteUrl(); @@ -303,7 +303,7 @@ class Router return null; } - protected function hasRewrite($url) + protected function hasRewrite($url) : bool { /* If the request has changed */ @@ -332,14 +332,14 @@ class Router * @param \Exception $e * @throws HttpException * @throws \Exception - * @return string + * @return string|null */ - protected function handleException(\Exception $e) + protected function handleException(\Exception $e) : ?string { /* @var $handler IExceptionHandler */ foreach ($this->exceptionHandlers as $key => $handler) { - if (is_object($handler) === false) { + if (\is_object($handler) === false) { $handler = new $handler(); } @@ -366,9 +366,9 @@ class Router throw $e; } - public function arrayToParams(array $getParams = [], $includeEmpty = true) + public function arrayToParams(array $getParams = [], $includeEmpty = true) : string { - if (count($getParams) !== 0) { + if (\count($getParams) !== 0) { if ($includeEmpty === false) { $getParams = array_filter($getParams, function ($item) { @@ -388,13 +388,13 @@ class Router * @param string $name * @return ILoadableRoute|null */ - public function findRoute($name) + public function findRoute($name) : ?ILoadableRoute { /* @var $route ILoadableRoute */ foreach ($this->processedRoutes as $route) { /* Check if the name matches with a name on the route. Should match either router alias or controller alias. */ - if ($route->hasName($name)) { + if ($route->hasName($name) === true) { return $route; } @@ -404,8 +404,8 @@ class Router } /* Using @ is most definitely a controller@method or alias@method */ - if (is_string($name) === true && strpos($name, '@') !== false) { - list($controller, $method) = array_map('strtolower', explode('@', $name)); + if (\is_string($name) === true && strpos($name, '@') !== false) { + [$controller, $method] = array_map('strtolower', explode('@', $name)); if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) { return $route; @@ -413,7 +413,7 @@ class Router } /* Check if callback matches (if it's not a function) */ - if (is_string($name) === true && is_string($route->getCallback()) && strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && is_callable($route->getCallback()) === false) { + if (\is_string($name) === true && \is_string($route->getCallback()) && strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && \is_callable($route->getCallback()) === false) { /* Check if the entire callback is matching */ if (strpos($route->getCallback(), $name) === 0 || strtolower($route->getCallback()) === strtolower($name)) { @@ -448,9 +448,9 @@ class Router * @throws InvalidArgumentException * @return string */ - public function getUrl($name = null, $parameters = null, $getParams = null) + public function getUrl($name = null, $parameters = null, $getParams = null) : string { - if ($getParams !== null && is_array($getParams) === false) { + if ($getParams !== null && \is_array($getParams) === false) { throw new InvalidArgumentException('Invalid type for getParams. Must be array or null'); } @@ -485,8 +485,8 @@ class Router } /* Using @ is most definitely a controller@method or alias@method */ - if (is_string($name) === true && strpos($name, '@') !== false) { - list($controller, $method) = explode('@', $name); + if (\is_string($name) === true && strpos($name, '@') !== false) { + [$controller, $method] = explode('@', $name); /* Loop through all the routes to see if we can find a match */ @@ -516,7 +516,7 @@ class Router * Get bootmanagers * @return array */ - public function getBootManagers() + public function getBootManagers() : array { return $this->bootManagers; } @@ -525,7 +525,7 @@ class Router * Set bootmanagers * @param array $bootManagers */ - public function setBootManagers(array $bootManagers) + public function setBootManagers(array $bootManagers) : void { $this->bootManagers = $bootManagers; } @@ -534,7 +534,7 @@ class Router * Add bootmanager * @param IRouterBootManager $bootManager */ - public function addBootManager(IRouterBootManager $bootManager) + public function addBootManager(IRouterBootManager $bootManager) : void { $this->bootManagers[] = $bootManager; } @@ -544,7 +544,7 @@ class Router * * @return array */ - public function getProcessedRoutes() + public function getProcessedRoutes() : array { return $this->processedRoutes; } @@ -552,7 +552,7 @@ class Router /** * @return array */ - public function getRoutes() + public function getRoutes() : array { return $this->routes; } @@ -561,9 +561,9 @@ class Router * Set routes * * @param array $routes - * @return static $this + * @return static */ - public function setRoutes(array $routes) + public function setRoutes(array $routes) : self { $this->routes = $routes; @@ -575,7 +575,7 @@ class Router * * @return Request */ - public function getRequest() + public function getRequest() : Request { return $this->request; } @@ -584,7 +584,7 @@ class Router * Get csrf verifier class * @return BaseCsrfVerifier */ - public function getCsrfVerifier() + public function getCsrfVerifier() : BaseCsrfVerifier { return $this->csrfVerifier; } diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 81b6014..8f64e70 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -13,12 +13,15 @@ namespace Pecee\SimpleRouter; use Pecee\Exceptions\InvalidArgumentException; use Pecee\Handlers\CallbackExceptionHandler; use Pecee\Http\Middleware\BaseCsrfVerifier; +use Pecee\Http\Request; use Pecee\Http\Response; use Pecee\SimpleRouter\Exceptions\HttpException; +use Pecee\SimpleRouter\Route\IGroupRoute; +use Pecee\SimpleRouter\Route\IPartialGroupRoute; use Pecee\SimpleRouter\Route\IRoute; -use Pecee\SimpleRouter\Route\RoutePartialGroup; use Pecee\SimpleRouter\Route\RouteController; use Pecee\SimpleRouter\Route\RouteGroup; +use Pecee\SimpleRouter\Route\RoutePartialGroup; use Pecee\SimpleRouter\Route\RouteResource; use Pecee\SimpleRouter\Route\RouteUrl; @@ -26,7 +29,7 @@ class SimpleRouter { /** * Default namespace added to all routes - * @var string + * @var string|null */ protected static $defaultNamespace; @@ -47,17 +50,36 @@ class SimpleRouter * @throws HttpException * @throws \Exception */ - public static function start() + public static function start(): void { echo static::router()->routeRequest(); } + /** + * Get debug info array + * + * @return array + * @throws \Pecee\Http\Exceptions\MalformedUrlException + */ + public static function debugInfo(): array + { + return [ + 'url' => static::request()->getUrl(), + 'method' => static::request()->getMethod(), + 'host' => static::request()->getHost(), + 'loaded_routes' => static::request()->getLoadedRoutes(), + 'all_routes' => static::router()->getRoutes(), + 'boot_managers' => static::router()->getBootManagers(), + 'csrf_verifier' => static::router()->getCsrfVerifier(), + ]; + } + /** * Set default namespace which will be prepended to all routes. * * @param string $defaultNamespace */ - public static function setDefaultNamespace($defaultNamespace) + public static function setDefaultNamespace(string $defaultNamespace): void { static::$defaultNamespace = $defaultNamespace; } @@ -68,7 +90,7 @@ class SimpleRouter * @param BaseCsrfVerifier $baseCsrfVerifier * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) + public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier): void { static::router()->setCsrfVerifier($baseCsrfVerifier); } @@ -80,7 +102,7 @@ class SimpleRouter * @param IRouterBootManager $bootManager * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function addBootManager(IRouterBootManager $bootManager) + public static function addBootManager(IRouterBootManager $bootManager): void { static::router()->addBootManager($bootManager); } @@ -95,7 +117,7 @@ class SimpleRouter * @return RouteUrl * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function get($url, $callback, array $settings = null) + public static function get(string $url, $callback, array $settings = null): IRoute { return static::match(['get'], $url, $callback, $settings); } @@ -109,7 +131,7 @@ class SimpleRouter * @return RouteUrl * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function post($url, $callback, array $settings = null) + public static function post(string $url, $callback, array $settings = null): IRoute { return static::match(['post'], $url, $callback, $settings); } @@ -123,7 +145,7 @@ class SimpleRouter * @return RouteUrl * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function put($url, $callback, array $settings = null) + public static function put(string $url, $callback, array $settings = null): IRoute { return static::match(['put'], $url, $callback, $settings); } @@ -137,7 +159,7 @@ class SimpleRouter * @return RouteUrl * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function patch($url, $callback, array $settings = null) + public static function patch(string $url, $callback, array $settings = null): IRoute { return static::match(['patch'], $url, $callback, $settings); } @@ -151,7 +173,7 @@ class SimpleRouter * @return RouteUrl * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function options($url, $callback, array $settings = null) + public static function options(string $url, $callback, array $settings = null): IRoute { return static::match(['options'], $url, $callback, $settings); } @@ -165,7 +187,7 @@ class SimpleRouter * @return RouteUrl * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function delete($url, $callback, array $settings = null) + public static function delete(string $url, $callback, array $settings = null): IRoute { return static::match(['delete'], $url, $callback, $settings); } @@ -179,9 +201,9 @@ class SimpleRouter * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws InvalidArgumentException */ - public static function group(array $settings = [], \Closure $callback) + public static function group(array $settings = [], \Closure $callback): IGroupRoute { - if (is_callable($callback) === false) { + if (\is_callable($callback) === false) { throw new InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); } @@ -205,9 +227,9 @@ class SimpleRouter * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws InvalidArgumentException */ - public static function partialGroup($url, \Closure $callback, array $settings = []) + public static function partialGroup(string $url, \Closure $callback, array $settings = []): IPartialGroupRoute { - if (is_callable($callback) === false) { + if (\is_callable($callback) === false) { throw new InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); } @@ -232,7 +254,7 @@ class SimpleRouter * @return RouteUrl * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function basic($url, $callback, array $settings = null) + public static function basic(string $url, $callback, array $settings = null): IRoute { return static::match(['get', 'post'], $url, $callback, $settings); } @@ -248,7 +270,7 @@ class SimpleRouter * @return RouteUrl * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function form($url, $callback, array $settings = null) + public static function form(string $url, $callback, array $settings = null): IRoute { return static::match(['get', 'post'], $url, $callback, $settings); } @@ -263,7 +285,7 @@ class SimpleRouter * @return RouteUrl|IRoute * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function match(array $requestMethods, $url, $callback, array $settings = null) + public static function match(array $requestMethods, string $url, $callback, array $settings = null) { $route = new RouteUrl($url, $callback); $route->setRequestMethods($requestMethods); @@ -287,7 +309,7 @@ class SimpleRouter * @return RouteUrl|IRoute * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function all($url, $callback, array $settings = null) + public static function all(string $url, $callback, array $settings = null) { $route = new RouteUrl($url, $callback); $route = static::addDefaultNamespace($route); @@ -310,7 +332,7 @@ class SimpleRouter * @return RouteController|IRoute * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function controller($url, $controller, array $settings = null) + public static function controller(string $url, $controller, array $settings = null) { $route = new RouteController($url, $controller); $route = static::addDefaultNamespace($route); @@ -333,7 +355,7 @@ class SimpleRouter * @return RouteResource|IRoute * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function resource($url, $controller, array $settings = null) + public static function resource(string $url, $controller, array $settings = null) { $route = new RouteResource($url, $controller); $route = static::addDefaultNamespace($route); @@ -354,7 +376,7 @@ class SimpleRouter * @return CallbackExceptionHandler $callbackHandler * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function error(\Closure $callback) + public static function error(\Closure $callback): CallbackExceptionHandler { $routes = static::router()->getRoutes(); @@ -389,7 +411,7 @@ class SimpleRouter * @return string * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function getUrl($name = null, $parameters = null, $getParams = null) + public static function getUrl(?string $name = null, $parameters = null, $getParams = null): string { return static::router()->getUrl($name, $parameters, $getParams); } @@ -400,7 +422,7 @@ class SimpleRouter * @return \Pecee\Http\Request * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function request() + public static function request(): Request { return static::router()->getRequest(); } @@ -411,7 +433,7 @@ class SimpleRouter * @return Response * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function response() + public static function response(): Response { if (static::$response === null) { static::$response = new Response(static::request()); @@ -426,7 +448,7 @@ class SimpleRouter * @return Router * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function router() + public static function router(): Router { if (static::$router === null) { static::$router = new Router(); @@ -441,14 +463,14 @@ class SimpleRouter * @param IRoute $route * @return IRoute */ - public static function addDefaultNamespace(IRoute $route) + public static function addDefaultNamespace(IRoute $route): IRoute { if (static::$defaultNamespace !== null) { $callback = $route->getCallback(); /* Only add default namespace on relative callbacks */ - if ($callback === null || (is_string($callback) === true && $callback[0] !== '\\')) { + if ($callback === null || (\is_string($callback) === true && $callback[0] !== '\\')) { $namespace = static::$defaultNamespace; @@ -468,9 +490,9 @@ class SimpleRouter /** * Get default namespace - * @return string + * @return string|null */ - public static function getDefaultNamespace() + public static function getDefaultNamespace(): ?string { return static::$defaultNamespace; } diff --git a/test/Dummy/ResourceController.php b/test/Dummy/ResourceController.php deleted file mode 100644 index 8536563..0000000 --- a/test/Dummy/ResourceController.php +++ /dev/null @@ -1,39 +0,0 @@ -getMessage(); } diff --git a/test/Dummy/Handler/ExceptionHandlerFirst.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerFirst.php similarity index 87% rename from test/Dummy/Handler/ExceptionHandlerFirst.php rename to tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerFirst.php index 8a5f807..561ebde 100644 --- a/test/Dummy/Handler/ExceptionHandlerFirst.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerFirst.php @@ -2,13 +2,12 @@ class ExceptionHandlerFirst implements \Pecee\Handlers\IExceptionHandler { - public function handleError(\Pecee\Http\Request $request, \Exception $error) + public function handleError(\Pecee\Http\Request $request, \Exception $error) : void { global $stack; $stack[] = static::class; $request->setUrl('/'); - return $request; } } \ No newline at end of file diff --git a/test/Dummy/Handler/ExceptionHandlerSecond.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php similarity index 86% rename from test/Dummy/Handler/ExceptionHandlerSecond.php rename to tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php index d629a03..2360047 100644 --- a/test/Dummy/Handler/ExceptionHandlerSecond.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php @@ -2,13 +2,12 @@ class ExceptionHandlerSecond implements \Pecee\Handlers\IExceptionHandler { - public function handleError(\Pecee\Http\Request $request, \Exception $error) + public function handleError(\Pecee\Http\Request $request, \Exception $error) : void { global $stack; $stack[] = static::class; $request->setUrl('/'); - return $request; } } \ No newline at end of file diff --git a/test/Dummy/Handler/ExceptionHandlerThird.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php similarity index 92% rename from test/Dummy/Handler/ExceptionHandlerThird.php rename to tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php index 15923ec..d295157 100644 --- a/test/Dummy/Handler/ExceptionHandlerThird.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php @@ -2,7 +2,7 @@ class ExceptionHandlerThird implements \Pecee\Handlers\IExceptionHandler { - public function handleError(\Pecee\Http\Request $request, \Exception $error) + public function handleError(\Pecee\Http\Request $request, \Exception $error) : void { global $stack; $stack[] = static::class; diff --git a/test/Dummy/Middlewares/RewriteMiddleware.php b/tests/Pecee/SimpleRouter/Dummy/Middleware/RewriteMiddleware.php similarity index 79% rename from test/Dummy/Middlewares/RewriteMiddleware.php rename to tests/Pecee/SimpleRouter/Dummy/Middleware/RewriteMiddleware.php index 7bd1fef..f107bbd 100644 --- a/test/Dummy/Middlewares/RewriteMiddleware.php +++ b/tests/Pecee/SimpleRouter/Dummy/Middleware/RewriteMiddleware.php @@ -5,7 +5,7 @@ use Pecee\Http\Request; class RewriteMiddleware implements IMiddleware { - public function handle(Request $request) { + public function handle(Request $request) : void { $request->setRewriteCallback(function() { return 'ok'; diff --git a/tests/Pecee/SimpleRouter/Dummy/ResourceController.php b/tests/Pecee/SimpleRouter/Dummy/ResourceController.php new file mode 100644 index 0000000..0a70e6f --- /dev/null +++ b/tests/Pecee/SimpleRouter/Dummy/ResourceController.php @@ -0,0 +1,46 @@ +assertTrue(true); } public function testMultipleRoutes() @@ -61,6 +61,8 @@ class GroupTest extends PHPUnit_Framework_TestCase }); TestRouter::debug('/my/match', 'get'); + + $this->assertTrue(true); } public function testUrls() diff --git a/test/MiddlewareTest.php b/tests/Pecee/SimpleRouter/MiddlewareTest.php similarity index 83% rename from test/MiddlewareTest.php rename to tests/Pecee/SimpleRouter/MiddlewareTest.php index d3c4dba..0183c59 100644 --- a/test/MiddlewareTest.php +++ b/tests/Pecee/SimpleRouter/MiddlewareTest.php @@ -3,13 +3,12 @@ require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyController.php'; require_once 'Dummy/Handler/ExceptionHandler.php'; -require_once 'Helpers/TestRouter.php'; -class MiddlewareTest extends PHPUnit_Framework_TestCase +class MiddlewareTest extends \PHPUnit\Framework\TestCase { public function testMiddlewareFound() { - $this->setExpectedException(MiddlewareLoadedException::class); + $this->expectException(MiddlewareLoadedException::class); TestRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () { TestRouter::get('/my/test/url', 'DummyController@method1', ['middleware' => 'DummyMiddleware']); @@ -29,6 +28,8 @@ class MiddlewareTest extends PHPUnit_Framework_TestCase TestRouter::get('/my/test/url', 'DummyController@method1'); TestRouter::debug('/my/test/url', 'get'); + + $this->assertTrue(true); } } \ No newline at end of file diff --git a/test/RouterCallbackExceptionHandlerTest.php b/tests/Pecee/SimpleRouter/RouterCallbackExceptionHandlerTest.php similarity index 69% rename from test/RouterCallbackExceptionHandlerTest.php rename to tests/Pecee/SimpleRouter/RouterCallbackExceptionHandlerTest.php index de21d2a..bf6514f 100644 --- a/test/RouterCallbackExceptionHandlerTest.php +++ b/tests/Pecee/SimpleRouter/RouterCallbackExceptionHandlerTest.php @@ -2,15 +2,14 @@ require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyController.php'; -require_once 'Dummy/Exceptions/ExceptionHandlerException.php'; -require_once 'Helpers/TestRouter.php'; +require_once 'Dummy/Exception/ExceptionHandlerException.php'; -class RouterCallbackExceptionHandlerTest extends PHPUnit_Framework_TestCase +class RouterCallbackExceptionHandlerTest extends \PHPUnit\Framework\TestCase { public function testCallbackExceptionHandler() { - $this->setExpectedException(ExceptionHandlerException::class); + $this->expectException(ExceptionHandlerException::class); // Match normal route on alias TestRouter::get('/my-new-url', 'DummyController@method2'); @@ -22,6 +21,8 @@ class RouterCallbackExceptionHandlerTest extends PHPUnit_Framework_TestCase TestRouter::debugNoReset('/404-url', 'get'); TestRouter::router()->reset(); + + $this->assertTrue(true); } } \ No newline at end of file diff --git a/test/RouterControllerTest.php b/tests/Pecee/SimpleRouter/RouterControllerTest.php similarity index 89% rename from test/RouterControllerTest.php rename to tests/Pecee/SimpleRouter/RouterControllerTest.php index 3c39bc2..fff6566 100644 --- a/test/RouterControllerTest.php +++ b/tests/Pecee/SimpleRouter/RouterControllerTest.php @@ -1,9 +1,8 @@ setExpectedException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class); + $this->expectException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class); TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) { diff --git a/test/RouterRouteTest.php b/tests/Pecee/SimpleRouter/RouterRouteTest.php similarity index 91% rename from test/RouterRouteTest.php rename to tests/Pecee/SimpleRouter/RouterRouteTest.php index c503b28..63d69f9 100644 --- a/test/RouterRouteTest.php +++ b/tests/Pecee/SimpleRouter/RouterRouteTest.php @@ -2,10 +2,9 @@ require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyController.php'; -require_once 'Dummy/Exceptions/ExceptionHandlerException.php'; -require_once 'Helpers/TestRouter.php'; +require_once 'Dummy/Exception/ExceptionHandlerException.php'; -class RouterRouteTest extends PHPUnit_Framework_TestCase +class RouterRouteTest extends \PHPUnit\Framework\TestCase { protected $result = false; @@ -27,7 +26,7 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase public function testNotFound() { - $this->setExpectedException('\Pecee\SimpleRouter\Exceptions\NotFoundHttpException'); + $this->expectException('\Pecee\SimpleRouter\Exceptions\NotFoundHttpException'); TestRouter::get('/non-existing-path', 'DummyController@method1'); TestRouter::debug('/test-param1-param2', 'post'); } @@ -36,24 +35,32 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase { TestRouter::get('/my/test/url', 'DummyController@method1'); TestRouter::debug('/my/test/url', 'get'); + + $this->assertTrue(true); } public function testPost() { TestRouter::post('/my/test/url', 'DummyController@method1'); TestRouter::debug('/my/test/url', 'post'); + + $this->assertTrue(true); } public function testPut() { TestRouter::put('/my/test/url', 'DummyController@method1'); TestRouter::debug('/my/test/url', 'put'); + + $this->assertTrue(true); } public function testDelete() { TestRouter::delete('/my/test/url', 'DummyController@method1'); TestRouter::debug('/my/test/url', 'delete'); + + $this->assertTrue(true); } public function testMethodNotAllowed() @@ -123,6 +130,8 @@ class RouterRouteTest extends PHPUnit_Framework_TestCase { TestRouter::get('/my/{path}', 'DummyController@method1')->where(['path' => '[a-zA-Z\-]+']); TestRouter::debug('/my/custom-path', 'get'); + + $this->assertTrue(true); } public function testParameterDefaultValue() { diff --git a/test/RouterUrlTest.php b/tests/Pecee/SimpleRouter/RouterUrlTest.php similarity index 98% rename from test/RouterUrlTest.php rename to tests/Pecee/SimpleRouter/RouterUrlTest.php index f7d986d..5c6d0e7 100644 --- a/test/RouterUrlTest.php +++ b/tests/Pecee/SimpleRouter/RouterUrlTest.php @@ -3,9 +3,8 @@ require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyController.php'; require_once 'Dummy/Handler/ExceptionHandler.php'; -require_once 'Helpers/TestRouter.php'; -class RouterUrlTest extends PHPUnit_Framework_TestCase +class RouterUrlTest extends \PHPUnit\Framework\TestCase { public function testIssue253() diff --git a/test/Helpers/TestRouter.php b/tests/TestRouter.php similarity index 100% rename from test/Helpers/TestRouter.php rename to tests/TestRouter.php diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..22f8cd2 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,4 @@ + Date: Mon, 26 Mar 2018 23:43:27 +0200 Subject: [PATCH 02/25] Development - Better php7 support. - Added easier way to debug router. - Improvements and bugfixes. - Updated documentation. --- README.md | 401 +++++++++++++++++- .../Exceptions/InvalidArgumentException.php | 4 +- src/Pecee/Http/Input/IInputItem.php | 6 +- src/Pecee/Http/Input/InputFile.php | 14 +- src/Pecee/Http/Input/InputHandler.php | 26 +- src/Pecee/Http/Input/InputItem.php | 12 +- .../Exceptions/TokenMismatchException.php | 1 + src/Pecee/Http/Request.php | 47 +- src/Pecee/Http/Response.php | 18 +- .../Http/Security/CookieTokenProvider.php | 12 +- .../Security/Exceptions/SecurityException.php | 4 +- src/Pecee/Http/Security/ITokenProvider.php | 4 +- src/Pecee/Http/Url.php | 39 +- .../SimpleRouter/Exceptions/HttpException.php | 1 + .../Exceptions/NotFoundHttpException.php | 1 + .../SimpleRouter/Route/IControllerRoute.php | 2 +- src/Pecee/SimpleRouter/Route/IGroupRoute.php | 6 +- .../SimpleRouter/Route/ILoadableRoute.php | 27 +- src/Pecee/SimpleRouter/Route/IRoute.php | 33 +- .../SimpleRouter/Route/LoadableRoute.php | 32 +- src/Pecee/SimpleRouter/Route/Route.php | 47 +- .../SimpleRouter/Route/RouteController.php | 8 +- src/Pecee/SimpleRouter/Route/RouteGroup.php | 2 +- .../SimpleRouter/Route/RouteResource.php | 8 +- src/Pecee/SimpleRouter/Router.php | 247 ++++++++--- src/Pecee/SimpleRouter/SimpleRouter.php | 54 ++- tests/Pecee/SimpleRouter/RouterUrlTest.php | 15 +- tests/debug.php | 9 + 28 files changed, 847 insertions(+), 233 deletions(-) create mode 100644 tests/debug.php diff --git a/README.md b/README.md index 5a333b6..18b4e2a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Simple PHP router -Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expandability in mind. + +Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind. **Please note that this documentation is currently work-in-progress. Feel free to contribute.** @@ -11,7 +12,6 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Notes](#notes-1) - [Requirements](#requirements) - [Feedback and development](#feedback-and-development) - - [Issues guidelines](#issues-guidelines) - [Contribution development guidelines](#contribution-development-guidelines) - [Features](#features) - [Installation](#installation) @@ -20,6 +20,14 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Setting up IIS](#setting-up-iis) - [Configuration](#configuration) - [Helper functions](#helper-functions) +- [Help and support](#help-and-support) + - [How to debug](#how-to-debug) + - [Creating unit-tests](#creating-unit-tests) + - [Debug information](#debug-information) + - [Benchmark and log-info](#benchmark-and-log-info) + - [Reporting a new issue](#reporting-a-new-issue) + - [Procedure for reporting a new issue](#procedure-for-reporting-a-new-issue) + - [Issue template](#issue-template) - [Routes](#routes) - [Basic routing](#basic-routing) - [Available methods](#available-methods) @@ -118,18 +126,14 @@ You can find the demo-project here: [https://github.com/skipperbent/simple-route ### Feedback and development -If you are missing a feature, experience problems or have ideas or feedback that you want us to hear, please feel free to create an issue. +If the library is missing a feature that you need in your project or if you have feedback, we'd love to hear from you. +Feel free to leave us feedback by [creating a new issue](https://github.com/skipperbent/simple-php-router/issues/new). -###### Issues guidelines +**Experiencing an issue?** -- Please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue. -For example: if you are experiencing issues, you should provide the necessary steps to reproduce the error within your description. +Please refer to our [Help and support](#help-and-support) section in the documentation before reporting a new issue. -- We love to hear out any ideas or feedback to the library. - -[Create a new issue here](https://github.com/skipperbent/simple-php-router/issues/new) - -###### Contribution development guidelines +##### Contribution development guidelines - Please try to follow the PSR-2 codestyle guidelines. @@ -206,7 +210,7 @@ Below is an example of an working `web.config` file used by simple-php-router. Simply create a new `web.config` file in your projects `public` directory and paste the contents below in your newly created file. This will redirect all requests to your `index.php` file (see Configuration section below). If the `web.config` file already exists, add the `` section inside the `` branch. -``` +```xml @@ -378,6 +382,379 @@ function csrf_token() --- +# Help and support + +This section will go into details on how to debug the router and answer some of the commonly asked questions- and issues. + +## How to debug + +This section will show you how to write unit-tests for the router, view useful debugging information and answer some of the frequently asked questions. + +It will also covers how to report any issue you might encounter. + +### Creating unit-tests + +The easiest and fastest way to debug any issues with the router, is to create a unit-test that represents the issue you are experiencing. + +Unit-tests use a special `TestRouter` class, which simulates a request-method and requested url of a browser. + +The `TestRouter` class can return the output directly or render a route silently. + +```php +public function testUnicodeCharacters() +{ + // Add route containing two optional paramters with special spanish characters like "í". + TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']); + + // Start the routing and simulate the url "/cursos/listado/especialidad/cirugía local". + TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'GET'); + + // Verify that the url for the loaded route matches the expected route. + $this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl()); + + // Start the routing and simulate the url "/test/Dermatología" using "GET" as request-method. + TestRouter::debugNoReset('/test/Dermatología', 'GET'); + + // Another route containing one parameter with special spanish characters like "í". + TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']); + + // Get all parameters parsed by the loaded route. + $parameters = TestRouter::request()->getLoadedRoute()->getParameters(); + + // Check that the parameter named "param" matches the exspected value. + $this->assertEquals('Dermatología', $parameters['param']); + + // Add route testing danish special characters like "ø". + TestRouter::get('/category/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']); + + // Start the routing and simulate the url "/kategory/økse" using "GET" as request-method. + TestRouter::debugNoReset('/category/økse', 'GET'); + + // Validate that the URL of the loaded-route matches the expected url. + $this->assertEquals('/category/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl()); + + // Reset the router, so other tests wont inherit settings or the routes we've added. + TestRouter::router()->reset(); +} +``` + +#### Using the TestRouter helper + +Depending on your test, you can use the methods below when rendering routes in your unit-tests. + + +| Method | Description | +| ------------- |-------------| +| ```TestRouter::debug($url, $method)``` | Will render the route without returning anything. Exceptions will be thrown and the router will be reset automatically. | +| ```TestRouter::debugOutput($url, $method)``` | Will render the route and return any value that the route might output. Manual reset required by calling `TestRouter::router()->reset()`. | +| ```TestRouter::debugNoReset($url, $method);``` | Will render the route without resetting the router. Useful if you need to get loaded route, parameters etc. from the router. Manual reset required by calling `TestRouter::router()->reset()`. | + +### Debug information + +The library can output debug-information, which contains information like loaded routes, the parsed request-url etc. It also contains info which are important when reporting a new issue like PHP-version, library version, server-variables, router debug log etc. + +You can activate the debug-information by calling the alternative start-method. + +The example below will start the routing an return array with debugging-information + +**Example:** + +```php +$debugInfo = SimpleRouter::startDebug(); +echo sprintf('
%s
', var_export($debugInfo)); +exit; +``` + +**The example above will provide you with an output containing:** + +| Key | Description | +| ------------- |------------- | +| `url` | The parsed request-uri. This url should match the url in the browser.| +| `method` | The browsers request method (example: `GET`, `POST`, `PUT`, `PATCH`, `DELETE` etc).| +| `host` | The website host (example: `domain.com`).| +| `loaded_routes` | List of all the routes that matched the `url` and that has been rendered/loaded. | +| `all_routes` | All available routes | +| `boot_managers` | All available BootManagers | +| `csrf_verifier` | CsrfVerifier class | +| `log` | List of debug messages/log from the router. | +| `router_output` | The rendered callback output from the router. | +| `library_version` | The version of simple-php-router you are using. | +| `php_version` | The version of PHP you are using. | +| `server_params` | List of all `$_SERVER` variables/headers. | + +#### Benchmark and logging + +You can activate benchmark debugging/logging by calling `setDebugEnabled` method on the `Router` instance. + +You have to enable debugging BEFORE starting the routing. + +**Example:** + +```php +SimpleRouter::router()->setDebugEnabled(true); +SimpleRouter::start(); +``` + +When the routing is complete, you can get the debug-log by calling the `getDebugLog()` on the `Router` instance. This will return an `array` of log-messages each containing execution time, trace info and debug-message. + +**Example:** + +```php +$messages = SimpleRouter::router()->getDebugLog(); +``` + +## Reporting a new issue + +**Before reporting your issue, make sure that the issue you are experiencing aren't already answered in the [Common errors](#common-errors) section or by searching the [closed issues](https://github.com/skipperbent/simple-php-router/issues?q=is%3Aissue+is%3Aclosed) page on GitHub.** + +To avoid confusion and to help you resolve your issue as quickly as possible, you should provide a detailed explanation of the problem you are experiencing. + +### Procedure for reporting a new issue + +1. Go to [this page](https://github.com/skipperbent/simple-php-router/issues/new) to create a new issue. +2. Add a title that describes your problems in as few words as possible. +3. Copy and paste the template below in the description of your issue and replace each step with your own information. If the step is not relevant for your issue you can delete it. + +### Issue template + +Copy and paste the template below into the description of your new issue and replace it with your own information. + +You can check the [Debug information](#debug-information) section to see how to generate the debug-info. + +
+### Description
+
+The library fails to render the route `/user/æsel` which contains one parameter using a custom regular expression for matching special foreign characters. Routes without special characters like `/user/tom` renders correctly.
+
+### Steps to reproduce the error
+
+1. Add the following route:
+
+```php
+SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
+```
+
+2. Navigate to `/user/æsel` in browser.
+
+3. `NotFoundHttpException` is thrown by library.
+
+### Route and/or callback for failing route
+
+*Route:*
+
+```php
+SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
+```
+
+*Callback:*
+
+```php
+public function show($username) {
+    return sprintf('Username is: %s', $username);
+}
+```
+
+### Debug info
+
+```php
+array (
+  'url' => 
+  Pecee\Http\Url::__set_state(array(
+     'originalUrl' => NULL,
+     'data' => 
+    array (
+      'scheme' => NULL,
+      'host' => NULL,
+      'port' => NULL,
+      'user' => NULL,
+      'pass' => NULL,
+      'path' => NULL,
+      'query' => NULL,
+      'fragment' => NULL,
+    ),
+  )),
+  'method' => '',
+  'host' => NULL,
+  'loaded_routes' => 
+  array (
+  ),
+  'all_routes' => 
+  array (
+    0 => 
+    Pecee\SimpleRouter\Route\RouteUrl::__set_state(array(
+       'url' => '/user/{name}/',
+       'name' => NULL,
+       'regex' => NULL,
+       'filterEmptyParams' => true,
+       'defaultParameterRegex' => NULL,
+       'paramModifiers' => '{}',
+       'paramOptionalSymbol' => '?',
+       'urlRegex' => '/^%s\\/?$/u',
+       'group' => NULL,
+       'parent' => NULL,
+       'callback' => 'UserController@show',
+       'defaultNamespace' => NULL,
+       'namespace' => NULL,
+       'requestMethods' => 
+      array (
+        0 => 'get',
+      ),
+       'where' => 
+      array (
+        'name' => '[\\w]+',
+      ),
+       'parameters' => 
+      array (
+        'name' => NULL,
+      ),
+       'originalParameters' => 
+      array (
+      ),
+       'middlewares' => 
+      array (
+      ),
+    )),
+  ),
+  'boot_managers' => 
+  array (
+  ),
+  'csrf_verifier' => NULL,
+  'log' => 
+  array (
+    0 => 
+    array (
+      'message' => 'Started routing request (rewrite: no)',
+      'time' => '0.0000069141',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
+        'line' => 57,
+        'function' => 'routeRequest',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    1 => 
+    array (
+      'message' => 'Loading routes',
+      'time' => '0.0036418438',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 273,
+        'function' => 'loadRoutes',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    2 => 
+    array (
+      'message' => 'Processing routes',
+      'time' => '0.0069010258',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 251,
+        'function' => 'processRoutes',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    3 => 
+    array (
+      'message' => 'Processing route "Pecee\\SimpleRouter\\Route\\RouteUrl"',
+      'time' => '0.0099139214',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 251,
+        'function' => 'processRoutes',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    4 => 
+    array (
+      'message' => 'Finished loading routes',
+      'time' => '0.0130679607',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 273,
+        'function' => 'loadRoutes',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    5 => 
+    array (
+      'message' => 'Matching route "Pecee\\SimpleRouter\\Route\\RouteUrl"',
+      'time' => '0.0160858631',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
+        'line' => 57,
+        'function' => 'routeRequest',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    6 => 
+    array (
+      'message' => 'Route not found: "/"',
+      'time' => '0.0193598270',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
+        'line' => 57,
+        'function' => 'routeRequest',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    7 => 
+    array (
+      'message' => 'Starting exception handling for "Pecee\\SimpleRouter\\Exceptions\\NotFoundHttpException"',
+      'time' => '0.0229449272',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 345,
+        'function' => 'handleException',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    8 => 
+    array (
+      'message' => 'Finished exception handling - exception not handled, throwing',
+      'time' => '0.0258929729',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 345,
+        'function' => 'handleException',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+  ),
+  'router_output' => NULL,
+  'library_version' => false,
+  'php_version' => '7.2.0',
+  'server_params' => 
+  array (),
+)
+```
+
+
+ +Remember that a more detailed issue- description and debug-info might suck to write, but it will help others understand- and resolve your issue without asking for the information. + +**Note:** please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue. Providing the necessary steps to reproduce the error within your description, adding useful debugging info etc. will help others quickly resolve the issue you are reporting. + +--- + # Routes Remember the ```routes.php``` file you required in your ```index.php```? This file be where you place all your custom rules for routing. diff --git a/src/Pecee/Exceptions/InvalidArgumentException.php b/src/Pecee/Exceptions/InvalidArgumentException.php index 1edcca6..95f070a 100644 --- a/src/Pecee/Exceptions/InvalidArgumentException.php +++ b/src/Pecee/Exceptions/InvalidArgumentException.php @@ -1,6 +1,8 @@ index = $index; @@ -48,7 +48,7 @@ class InputFile implements IInputItem ]; return (new static($values['index'])) - ->setSize($values['size']) + ->setSize((int)$values['size']) ->setError($values['error']) ->setType($values['type']) ->setTmpName($values['tmp_name']) @@ -69,7 +69,7 @@ class InputFile implements IInputItem * @param string $index * @return static */ - public function setIndex($index): IInputItem + public function setIndex(string $index): IInputItem { $this->index = $index; @@ -89,7 +89,7 @@ class InputFile implements IInputItem * @param int $size * @return static */ - public function setSize($size): IInputItem + public function setSize(int $size): IInputItem { $this->size = $size; @@ -118,7 +118,7 @@ class InputFile implements IInputItem * @param string $type * @return static */ - public function setType($type): IInputItem + public function setType(string $type): IInputItem { $this->type = $type; @@ -152,7 +152,7 @@ class InputFile implements IInputItem * @param string $name * @return static */ - public function setName($name): IInputItem + public function setName(string $name): IInputItem { $this->name = $name; @@ -270,7 +270,7 @@ class InputFile implements IInputItem * @param string $value * @return static */ - public function setValue($value): IInputItem + public function setValue(string $value): IInputItem { $this->filename = $value; diff --git a/src/Pecee/Http/Input/InputHandler.php b/src/Pecee/Http/Input/InputHandler.php index 416a8e3..6fee12a 100644 --- a/src/Pecee/Http/Input/InputHandler.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -42,7 +42,7 @@ class InputHandler * Parse input values * */ - public function parseInputs() : void + public function parseInputs(): void { /* Parse get requests */ if (\count($_GET) !== 0) { @@ -69,7 +69,7 @@ class InputHandler /** * @return array */ - public function parseFiles() : array + public function parseFiles(): array { $list = []; @@ -80,7 +80,7 @@ class InputHandler $values['index'] = $key; try { $list[$key] = InputFile::createFromArray($values + $value); - } catch(InvalidArgumentException $e ){ + } catch (InvalidArgumentException $e) { } continue; @@ -101,7 +101,7 @@ class InputHandler return $list; } - protected function rearrangeFiles(array $values, &$index, $original) : array + protected function rearrangeFiles(array $values, &$index, $original): array { $originalIndex = $index[0]; @@ -132,7 +132,7 @@ class InputHandler $output[$key] = $file; continue; - } catch(InvalidArgumentException $e) { + } catch (InvalidArgumentException $e) { } } @@ -152,7 +152,7 @@ class InputHandler return $output; } - protected function handleGetPost(array $array) : array + protected function handleGetPost(array $array): array { $list = []; @@ -179,7 +179,7 @@ class InputHandler * @param string|null $defaultValue * @return InputItem|string */ - public function findPost($index, $defaultValue = null) + public function findPost(string $index, ?string $defaultValue = null) { return $this->post[$index] ?? $defaultValue; } @@ -191,7 +191,7 @@ class InputHandler * @param string|null $defaultValue * @return InputFile|string */ - public function findFile($index, $defaultValue = null) + public function findFile(string $index, ?string $defaultValue = null) { return $this->file[$index] ?? $defaultValue; } @@ -203,7 +203,7 @@ class InputHandler * @param string|null $defaultValue * @return InputItem|string */ - public function findGet($index, $defaultValue = null) + public function findGet(string $index, ?string $defaultValue = null) { return $this->get[$index] ?? $defaultValue; } @@ -216,7 +216,7 @@ class InputHandler * @param array|string|null $methods * @return IInputItem|string */ - public function getObject($index, $defaultValue = null, $methods = null) + public function getObject(string $index, ?string $defaultValue = null, $methods = null) { if ($methods !== null && \is_string($methods) === true) { $methods = [$methods]; @@ -247,7 +247,7 @@ class InputHandler * @param array|string|null $methods * @return InputItem|string */ - public function get($index, $defaultValue = null, $methods = null) + public function get(string $index, ?string $defaultValue = null, $methods = null) { $input = $this->getObject($index, $defaultValue, $methods); @@ -264,7 +264,7 @@ class InputHandler * @param string $index * @return bool */ - public function exists($index) : bool + public function exists(string $index): bool { return ($this->getObject($index) !== null); } @@ -274,7 +274,7 @@ class InputHandler * @param array|null $filter Only take items in filter * @return array */ - public function all(array $filter = null) : array + public function all(array $filter = null): array { $output = $_GET + $_POST; diff --git a/src/Pecee/Http/Input/InputItem.php b/src/Pecee/Http/Input/InputItem.php index de00aa7..c3f8129 100644 --- a/src/Pecee/Http/Input/InputItem.php +++ b/src/Pecee/Http/Input/InputItem.php @@ -8,7 +8,7 @@ class InputItem implements IInputItem public $name; public $value; - public function __construct($index, $value = null) + public function __construct(string $index, ?string $value = null) { $this->index = $index; $this->value = $value; @@ -25,7 +25,7 @@ class InputItem implements IInputItem return $this->index; } - public function setIndex($index): IInputItem + public function setIndex(string $index): IInputItem { $this->index = $index; @@ -43,9 +43,9 @@ class InputItem implements IInputItem /** * Set input name * @param string $name - * @return static $this + * @return static */ - public function setName($name): IInputItem + public function setName(string $name): IInputItem { $this->name = $name; @@ -63,9 +63,9 @@ class InputItem implements IInputItem /** * Set input value * @param string $value - * @return static $this + * @return static */ - public function setValue($value): IInputItem + public function setValue(string $value): IInputItem { $this->value = $value; diff --git a/src/Pecee/Http/Middleware/Exceptions/TokenMismatchException.php b/src/Pecee/Http/Middleware/Exceptions/TokenMismatchException.php index 8f21c9e..7e07c7a 100644 --- a/src/Pecee/Http/Middleware/Exceptions/TokenMismatchException.php +++ b/src/Pecee/Http/Middleware/Exceptions/TokenMismatchException.php @@ -1,4 +1,5 @@ parseHeaders(); + foreach ($_SERVER as $key => $value) { + $this->headers[strtolower($key)] = $value; + $this->headers[strtolower(str_replace('_', '-', $key))] = $value; + } + $this->setHost($this->getHeader('http-host')); // Check if special IIS header exist, otherwise use default. @@ -45,17 +49,6 @@ class Request $this->method = strtolower($this->inputHandler->get('_method', $this->getHeader('request-method'))); } - protected function parseHeaders(): void - { - $this->headers = []; - - foreach ($_SERVER as $key => $value) { - $this->headers[strtolower($key)] = $value; - $this->headers[strtolower(str_replace('_', '-', $key))] = $value; - } - - } - public function isSecure(): bool { return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443; @@ -221,9 +214,9 @@ class Request } /** - * @param string $host + * @param string|null $host */ - public function setHost($host): void + public function setHost(?string $host): void { $this->host = $host; } @@ -231,7 +224,7 @@ class Request /** * @param string $method */ - public function setMethod($method): void + public function setMethod(string $method): void { $this->method = $method; } @@ -341,26 +334,32 @@ class Request return $this; } + /** + * Returns true if the request contains a rewrite + * + * @return bool + */ public function hasRewrite(): bool { return $this->hasRewrite; } - public function setHasRewrite($value): self + /** + * Defines if the current request contains a rewrite. + * + * @param bool $boolean + * @return Request + */ + public function setHasRewrite(bool $boolean): self { - $this->hasRewrite = $value; + $this->hasRewrite = $boolean; return $this; } - public function isRewrite($url): bool - { - return ($this->rewriteUrl === $url); - } - public function __isset($name) { - return array_key_exists($name, $this->data); + return array_key_exists($name, $this->data) === true; } public function __set($name, $value = null) diff --git a/src/Pecee/Http/Response.php b/src/Pecee/Http/Response.php index 9920132..2e199f5 100644 --- a/src/Pecee/Http/Response.php +++ b/src/Pecee/Http/Response.php @@ -19,7 +19,7 @@ class Response * @param int $code * @return static */ - public function httpCode($code): self + public function httpCode(int $code): self { http_response_code($code); @@ -32,7 +32,7 @@ class Response * @param string $url * @param int $httpCode */ - public function redirect($url, $httpCode = null): void + public function redirect(string $url, ?int $httpCode = null): void { if ($httpCode !== null) { $this->httpCode($httpCode); @@ -52,7 +52,7 @@ class Response * @param string $name * @return static */ - public function auth($name = ''): self + public function auth(string $name = ''): self { $this->headers([ 'WWW-Authenticate: Basic realm="' . $name . '"', @@ -62,19 +62,19 @@ class Response return $this; } - public function cache($eTag, $lastModified = 2592000): self + public function cache(string $eTag, int $lastModifiedTime = 2592000): self { $this->headers([ 'Cache-Control: public', - 'Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT', - 'Etag: ' . $eTag, + sprintf('Last-Modified: %s GMT', gmdate('D, d M Y H:i:s', $lastModifiedTime)), + sprintf('Etag: %s', $eTag), ]); $httpModified = $this->request->getHeader('http-if-modified-since'); $httpIfNoneMatch = $this->request->getHeader('http-if-none-match'); - if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModified)) { + if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModifiedTime)) { $this->header('HTTP/1.1 304 Not Modified'); exit(0); @@ -90,7 +90,7 @@ class Response * @param int $dept JSON debt. * @throws InvalidArgumentException */ - public function json($value, $options = null, $dept = 512): void + public function json($value, ?int $options = null, 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.'); @@ -106,7 +106,7 @@ class Response * @param string $value * @return static */ - public function header($value): self + public function header(string $value): self { header($value); diff --git a/src/Pecee/Http/Security/CookieTokenProvider.php b/src/Pecee/Http/Security/CookieTokenProvider.php index a44f639..bbe5ccb 100644 --- a/src/Pecee/Http/Security/CookieTokenProvider.php +++ b/src/Pecee/Http/Security/CookieTokenProvider.php @@ -45,9 +45,9 @@ class CookieTokenProvider implements ITokenProvider * @param string $token * @return bool */ - public function validate($token): bool + public function validate(string $token): bool { - if ($token !== null && $this->getToken() !== null) { + if ($this->getToken() !== null) { return hash_equals($token, $this->getToken()); } @@ -60,7 +60,7 @@ class CookieTokenProvider implements ITokenProvider * * @param string $token */ - public function setToken($token): void + public function setToken(string $token): void { $this->token = $token; setcookie(static::CSRF_KEY, $token, time() + 60 * $this->cookieTimeoutMinutes, '/'); @@ -71,7 +71,7 @@ class CookieTokenProvider implements ITokenProvider * @param string|null $defaultValue * @return string|null */ - public function getToken($defaultValue = null): ?string + public function getToken(?string $defaultValue = null): ?string { $this->token = ($this->hasToken() === true) ? $_COOKIE[static::CSRF_KEY] : null; @@ -108,9 +108,9 @@ class CookieTokenProvider implements ITokenProvider /** * Set cookie timeout in minutes - * @param $minutes + * @param int $minutes */ - public function setCookieTimeoutMinutes($minutes): void + public function setCookieTimeoutMinutes(int $minutes): void { $this->cookieTimeoutMinutes = $minutes; } diff --git a/src/Pecee/Http/Security/Exceptions/SecurityException.php b/src/Pecee/Http/Security/Exceptions/SecurityException.php index 6b63bfb..b7d2dc2 100644 --- a/src/Pecee/Http/Security/Exceptions/SecurityException.php +++ b/src/Pecee/Http/Security/Exceptions/SecurityException.php @@ -1,6 +1,8 @@ originalUrl = $url; - $this->data = $this->parseUrl($url) + $this->data; + if ($url !== null) { + $this->data = $this->parseUrl($url) + $this->data; - if (isset($this->data['path']) === true && $this->data['path'] !== '/') { - $this->data['path'] = rtrim($this->data['path'], '/') . '/'; + if (isset($this->data['path']) === true && $this->data['path'] !== '/') { + $this->data['path'] = rtrim($this->data['path'], '/') . '/'; + } } - } /** @@ -103,7 +104,7 @@ class Url */ public function getPath(): ?string { - return $this->data['path']; + return $this->data['path'] ?? '/'; } /** @@ -139,7 +140,7 @@ class Url * @throws MalformedUrlException * @return array */ - public function parseUrl($url, $component = -1): array + public function parseUrl(string $url, int $component = -1): array { $encodedUrl = preg_replace_callback( '/[^:\/@?&=#]+/u', @@ -158,9 +159,29 @@ class Url return array_map('urldecode', $parts); } - public function contains($value): bool + /** + * Get position of value. + * Returns -1 on failure. + * + * @param string $value + * @return int + */ + public function indexOf(string $value): int { - return (stripos($this->getOriginalUrl(), $value) === false); + $index = stripos($this->getOriginalUrl(), $value); + + return ($index === false) ? -1 : $index; + } + + /** + * Check if url contains value. + * + * @param string $value + * @return bool + */ + public function contains(string $value): bool + { + return (stripos($this->getOriginalUrl(), $value) !== false); } /** diff --git a/src/Pecee/SimpleRouter/Exceptions/HttpException.php b/src/Pecee/SimpleRouter/Exceptions/HttpException.php index e3ded14..e03c4a7 100644 --- a/src/Pecee/SimpleRouter/Exceptions/HttpException.php +++ b/src/Pecee/SimpleRouter/Exceptions/HttpException.php @@ -1,4 +1,5 @@ debug('Loading middlewares'); + foreach ($this->getMiddlewares() as $middleware) { if (\is_object($middleware) === false) { @@ -38,11 +42,15 @@ abstract class LoadableRoute extends Route implements ILoadableRoute throw new HttpException($middleware . ' must be inherit the IMiddleware interface'); } + $router->debug('Loading middleware "%s"', \get_class($middleware)); $middleware->handle($request); + $router->debug('Finished loading middleware'); } + + $router->debug('Finished loading middlewares'); } - public function matchRegex(Request $request, $url) : ?bool + public function matchRegex(Request $request, $url): ?bool { /* Match on custom defined regular expression */ @@ -59,7 +67,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string $url * @return static */ - public function setUrl($url) : self + public function setUrl(string $url): ILoadableRoute { $this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/'; @@ -75,7 +83,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute return $this; } - public function getUrl() : string + public function getUrl(): string { return $this->url; } @@ -89,7 +97,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string|null $name * @return string */ - public function findUrl($method = null, $parameters = null, $name = null) : string + public function findUrl($method = null, $parameters = null, $name = null): string { $url = $this->getUrl(); @@ -144,7 +152,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * * @return string */ - public function getName() : string + public function getName(): string { return $this->name; } @@ -155,7 +163,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string $name * @return bool */ - public function hasName($name) : bool + public function hasName(string $name): bool { return strtolower($this->name) === strtolower($name); } @@ -166,7 +174,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string $regex * @return static */ - public function setMatch($regex) : ILoadableRoute + public function setMatch($regex): ILoadableRoute { $this->regex = $regex; @@ -178,7 +186,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * * @return string */ - public function getMatch() : string + public function getMatch(): string { return $this->regex; } @@ -191,7 +199,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string|array $name * @return static */ - public function name($name) : ILoadableRoute + public function name($name): ILoadableRoute { return $this->setName($name); } @@ -202,7 +210,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param string $name * @return static */ - public function setName($name) : ILoadableRoute + public function setName(string $name): ILoadableRoute { $this->name = $name; @@ -216,7 +224,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * @param bool $merge * @return static */ - public function setSettings(array $values, $merge = false): IRoute + public function setSettings(array $values, bool $merge = false): IRoute { if (isset($values['as']) === true) { diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 1cb70bc..12d6aff 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -5,6 +5,7 @@ namespace Pecee\SimpleRouter\Route; use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Request; use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; +use Pecee\SimpleRouter\Router; abstract class Route implements IRoute { @@ -59,10 +60,10 @@ abstract class Route implements IRoute /** * Load class by name * @param string $name - * @return mixed + * @return object * @throws NotFoundHttpException */ - protected function loadClass($name) + protected function loadClass($name): object { if (class_exists($name) === false) { throw new NotFoundHttpException(sprintf('Class "%s" does not exist', $name), 404); @@ -75,21 +76,25 @@ abstract class Route implements IRoute * Render route * * @param Request $request + * @param Router $router * @return string|null * @throws NotFoundHttpException */ - public function renderRoute(Request $request): ?string + public function renderRoute(Request $request, Router $router): ?string { + $router->debug('Starting rendering route'); + $callback = $this->getCallback(); if ($callback === null) { return null; } + $router->debug('Parsing parameters'); $parameters = $this->getParameters(); + $router->debug('Finished parsing parameters'); /* Filter parameters with null-value */ - if ($this->filterEmptyParams === true) { $parameters = array_filter($parameters, function ($var) { return ($var !== null); @@ -98,7 +103,10 @@ abstract class Route implements IRoute /* Render callback function */ if (\is_callable($callback) === true) { + $router->debug('Executing callback'); + /* When the callback is a function */ + return \call_user_func_array($callback, $parameters); } @@ -109,6 +117,7 @@ abstract class Route implements IRoute $className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0]; + $router->debug('Loading class %s', $className); $class = $this->loadClass($className); $method = $controller[1]; @@ -116,6 +125,8 @@ abstract class Route implements IRoute throw new NotFoundHttpException(sprintf('Method "%s" does not exist in class "%s"', $method, $className), 404); } + $router->debug('Executing callback'); + return \call_user_func_array([$class, $method], $parameters); } @@ -300,7 +311,7 @@ abstract class Route implements IRoute return null; } - public function getClass() + public function getClass(): ?string { if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) { $tmp = explode('@', $this->callback); @@ -311,14 +322,14 @@ abstract class Route implements IRoute return null; } - public function setMethod($method): IRoute + public function setMethod(string $method): IRoute { $this->callback = sprintf('%s@%s', $this->getClass(), $method); return $this; } - public function setClass($class) + public function setClass(string $class): IRoute { $this->callback = sprintf('%s@%s', $class, $this->getMethod()); @@ -327,9 +338,9 @@ abstract class Route implements IRoute /** * @param string $namespace - * @return static $this + * @return static */ - public function setNamespace($namespace) + public function setNamespace(string $namespace): IRoute { $this->namespace = $namespace; @@ -338,16 +349,16 @@ abstract class Route implements IRoute /** * @param string $namespace - * @return static $this + * @return static */ - public function setDefaultNamespace($namespace) + public function setDefaultNamespace($namespace): IRoute { $this->defaultNamespace = $namespace; return $this; } - public function getDefaultNamespace() + public function getDefaultNamespace(): ?string { return $this->defaultNamespace; } @@ -397,9 +408,9 @@ abstract class Route implements IRoute * * @param array $values * @param bool $merge - * @return static $this + * @return static */ - public function setSettings(array $values, $merge = false) + public function setSettings(array $values, bool $merge = false): IRoute { if ($this->namespace === null && isset($values['namespace']) === true) { $this->setNamespace($values['namespace']); @@ -445,7 +456,7 @@ abstract class Route implements IRoute * @param array $options * @return static */ - public function setWhere(array $options) + public function setWhere(array $options): IRoute { $this->where = $options; @@ -486,9 +497,9 @@ abstract class Route implements IRoute * Get parameters * * @param array $parameters - * @return static $this + * @return static */ - public function setParameters(array $parameters) + public function setParameters(array $parameters): IRoute { /* * If this is the first time setting parameters we store them so we @@ -556,7 +567,7 @@ abstract class Route implements IRoute * This is used when no custom parameter regex is found. * * @param string $regex - * @return static $this + * @return static */ public function setDefaultParameterRegex($regex) { diff --git a/src/Pecee/SimpleRouter/Route/RouteController.php b/src/Pecee/SimpleRouter/Route/RouteController.php index 898df28..b15a402 100644 --- a/src/Pecee/SimpleRouter/Route/RouteController.php +++ b/src/Pecee/SimpleRouter/Route/RouteController.php @@ -24,7 +24,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string $name * @return bool */ - public function hasName($name): bool + public function hasName(string $name): bool { if ($this->name === null) { return false; @@ -134,7 +134,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string $controller * @return static */ - public function setController($controller): IControllerRoute + public function setController(string $controller): IControllerRoute { $this->controller = $controller; @@ -157,7 +157,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string $method * @return static */ - public function setMethod($method): IRoute + public function setMethod(string $method): IRoute { $this->method = $method; @@ -171,7 +171,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param bool $merge * @return static */ - public function setSettings(array $values, $merge = false): IRoute + public function setSettings(array $values, bool $merge = false): IRoute { if (isset($values['names']) === true) { $this->names = $values['names']; diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index e2506d7..8c88e6b 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -147,7 +147,7 @@ class RouteGroup extends Route implements IGroupRoute * @param bool $merge * @return static */ - public function setSettings(array $values, $merge = false): IRoute + public function setSettings(array $values, bool $merge = false): IRoute { if (isset($values['prefix']) === true) { diff --git a/src/Pecee/SimpleRouter/Route/RouteResource.php b/src/Pecee/SimpleRouter/Route/RouteResource.php index 7d454b4..13fe2e7 100644 --- a/src/Pecee/SimpleRouter/Route/RouteResource.php +++ b/src/Pecee/SimpleRouter/Route/RouteResource.php @@ -42,7 +42,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute * @param string $name * @return bool */ - public function hasName($name): bool + public function hasName(string $name): bool { if ($this->name === null) { return false; @@ -154,14 +154,14 @@ class RouteResource extends LoadableRoute implements IControllerRoute * @param string $controller * @return static */ - public function setController($controller): IControllerRoute + public function setController(string $controller): IControllerRoute { $this->controller = $controller; return $this; } - public function setName($name): ILoadableRoute + public function setName(string $name): ILoadableRoute { $this->name = $name; @@ -208,7 +208,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute * @param bool $merge * @return static */ - public function setSettings(array $values, $merge = false): IRoute + public function setSettings(array $values, bool $merge = false): IRoute { if (isset($values['names']) === true) { $this->names = $values['names']; diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index c4d8f22..4a96f7a 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -66,6 +66,32 @@ class Router */ protected $exceptionHandlers; + /** + * List of loaded exception that has been loaded. + * Used to ensure that exception-handlers aren't loaded twice when rewriting route. + * + * @var array + */ + protected $loadedExceptionHandlers; + + /** + * Enable or disabled debugging + * @var bool + */ + protected $debugEnabled = false; + + /** + * The start time used when debugging is enabled + * @var float + */ + protected $debugStartTime; + + /** + * List containing all debug messages + * @var array + */ + protected $debugList = []; + /** * Router constructor. * @throws \Pecee\Http\Exceptions\MalformedUrlException @@ -78,7 +104,7 @@ class Router /** * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public function reset() : void + public function reset(): void { $this->processingRoute = false; $this->request = new Request(); @@ -87,6 +113,7 @@ class Router $this->routeStack = []; $this->processedRoutes = []; $this->exceptionHandlers = []; + $this->loadedExceptionHandlers = []; } /** @@ -94,7 +121,7 @@ class Router * @param IRoute $route * @return IRoute */ - public function addRoute(IRoute $route) : IRoute + public function addRoute(IRoute $route): IRoute { /* * If a route is currently being processed, that means that the route being added are rendered from the parent @@ -102,10 +129,12 @@ class Router */ if ($this->processingRoute === true) { $this->routeStack[] = $route; + return $route; } $this->routes[] = $route; + return $route; } @@ -115,10 +144,11 @@ class Router * @param IRoute $route * @throws NotFoundHttpException */ - protected function renderAndProcess(IRoute $route) : void { + protected function renderAndProcess(IRoute $route): void + { $this->processingRoute = true; - $route->renderRoute($this->request); + $route->renderRoute($this->request, $this); $this->processingRoute = false; if (\count($this->routeStack) !== 0) { @@ -139,13 +169,17 @@ class Router * @param IGroupRoute|null $group * @throws NotFoundHttpException */ - protected function processRoutes(array $routes, IGroupRoute $group = null) : void + protected function processRoutes(array $routes, ?IGroupRoute $group = null): void { + $this->debug('Processing routes'); + // Loop through each route-request $exceptionHandlers = []; // Stop processing routes if no valid route is found. - if($this->request->getRewriteRoute() === null && $this->request->getUrl() === null) { + if ($this->request->getRewriteRoute() === null && $this->request->getUrl() === null) { + $this->debug('Halted route-processing as no valid route was found'); + return; } @@ -154,6 +188,8 @@ class Router /* @var $route IRoute */ foreach ($routes as $route) { + $this->debug('Processing route "%s"', \get_class($route)); + if ($group !== null) { /* Add the parent group */ $route->setGroup($group); @@ -199,16 +235,22 @@ class Router * @throws NotFoundHttpException * @return void */ - public function loadRoutes() : void + public function loadRoutes(): void { + $this->debug('Loading routes'); + /* Initialize boot-managers */ /* @var $manager IRouterBootManager */ foreach ($this->bootManagers as $manager) { + $this->debug('Rendering bootmanager %s', \get_class($manager)); $manager->boot($this->request); + $this->debug('Finished rendering bootmanager'); } /* Loop through each route-request */ $this->processRoutes($this->routes); + + $this->debug('Finished loading routes'); } /** @@ -219,9 +261,11 @@ class Router * @throws HttpException * @throws \Exception */ - public function routeRequest($rewrite = false) : ?string + public function routeRequest(bool $rewrite = false): ?string { - $routeNotAllowed = false; + $this->debug('Started routing request (rewrite: %s)', $rewrite === true ? 'yes' : 'no'); + + $methodNotAllowed = false; try { @@ -233,8 +277,6 @@ class Router /* Verify csrf token for request */ $this->csrfVerifier->handle($this->request); } - } else { - $this->request->setHasRewrite(false); } $url = $this->request->getRewriteUrl() ?? $this->request->getUrl()->getPath(); @@ -242,38 +284,39 @@ class Router /* @var $route ILoadableRoute */ foreach ($this->processedRoutes as $key => $route) { + $this->debug('Matching route "%s"', \get_class($route)); + /* If the route matches */ if ($route->matchRoute($url, $this->request) === true) { /* Check if request method matches */ if (\count($route->getRequestMethods()) !== 0 && \in_array($this->request->getMethod(), $route->getRequestMethods(), true) === false) { - $routeNotAllowed = true; + $this->debug('Method "%s" not allowed', $this->request->getMethod()); + $methodNotAllowed = true; continue; } - $route->loadMiddleware($this->request); + $route->loadMiddleware($this->request, $this); - if ($this->hasRewrite($url) === true) { - unset($this->processedRoutes[$key]); - - return $this->routeRequest(true); + $output = $this->handleRouteRewrite($key, $url); + if ($output !== null) { + return $output; } /* Render route */ - $routeNotAllowed = false; + $methodNotAllowed = false; $this->request->addLoadedRoute($route); - $output = $route->renderRoute($this->request); + $output = $route->renderRoute($this->request, $this); if ($output !== null) { return $output; } - if ($this->hasRewrite($url) === true) { - unset($this->processedRoutes[$key]); - - return $this->routeRequest(true); + $output = $this->handleRouteRewrite($key, $url); + if ($output !== null) { + return $output; } } } @@ -282,7 +325,7 @@ class Router $this->handleException($e); } - if ($routeNotAllowed === true) { + if ($methodNotAllowed === true) { $message = sprintf('Route "%s" or method "%s" not allowed.', $this->request->getUrl()->getPath(), $this->request->getMethod()); $this->handleException(new HttpException($message, 403)); } @@ -297,35 +340,45 @@ class Router $message = sprintf('Route not found: "%s"', $this->request->getUrl()->getPath()); } - $this->handleException(new NotFoundHttpException($message, 404)); + $this->debug($message); + + return $this->handleException(new NotFoundHttpException($message, 404)); } return null; } - protected function hasRewrite($url) : bool + /** + * Handle route-rewrite + * + * @param string $key + * @param string $url + * @return string|null + * @throws HttpException + * @throws \Exception + */ + protected function handleRouteRewrite($key, string $url): ?string { - /* If the request has changed */ - if ($this->request->hasRewrite() === true) { - - if ($this->request->getRewriteRoute() !== null) { - /* Render rewrite-route */ - $this->processedRoutes[] = $this->request->getRewriteRoute(); - - return true; - } - - if ($this->request->isRewrite($url) === false) { - - /* Render rewrite-url */ - $this->processedRoutes = array_values($this->processedRoutes); - - return true; - } + if ($this->request->hasRewrite() === false) { + return null; } - return false; + $route = $this->request->getRewriteRoute(); + + if ($route !== null) { + /* Add rewrite route */ + $this->processedRoutes[] = $route; + } + + if ($this->request->getRewriteUrl() !== $url) { + unset($this->processedRoutes[$key]); + $this->request->setHasRewrite(false); + + return $this->routeRequest(true); + } + + return null; } /** @@ -334,8 +387,10 @@ class Router * @throws \Exception * @return string|null */ - protected function handleException(\Exception $e) : ?string + protected function handleException(\Exception $e): ?string { + $this->debug('Starting exception handling for "%s"', \get_class($e)); + /* @var $handler IExceptionHandler */ foreach ($this->exceptionHandlers as $key => $handler) { @@ -343,17 +398,22 @@ class Router $handler = new $handler(); } + $this->debug('Processing exception-handler "%s"', \get_class($handler)); + if (($handler instanceof IExceptionHandler) === false) { throw new HttpException('Exception handler must implement the IExceptionHandler interface.', 500); } try { + $this->debug('Start rendering exception handler'); $handler->handleError($this->request, $e); + $this->debug('Finished rendering exception-handler'); - if ($this->request->hasRewrite() === true) { - unset($this->exceptionHandlers[$key]); - $this->exceptionHandlers = array_values($this->exceptionHandlers); + if (isset($this->loadedExceptionHandlers[$key]) === false && $this->request->hasRewrite() === true) { + $this->loadedExceptionHandlers[$key] = $handler; + + $this->debug('Exception handler contains rewrite, reloading routes'); return $this->routeRequest(true); } @@ -361,12 +421,15 @@ class Router } catch (\Exception $e) { } + + $this->debug('Finished processing'); } + $this->debug('Finished exception handling - exception not handled, throwing'); throw $e; } - public function arrayToParams(array $getParams = [], $includeEmpty = true) : string + public function arrayToParams(array $getParams = [], bool $includeEmpty = true): string { if (\count($getParams) !== 0) { @@ -388,18 +451,25 @@ class Router * @param string $name * @return ILoadableRoute|null */ - public function findRoute($name) : ?ILoadableRoute + public function findRoute(string $name): ?ILoadableRoute { + + $this->debug('Finding route by name "%s"', $name); + /* @var $route ILoadableRoute */ foreach ($this->processedRoutes as $route) { /* Check if the name matches with a name on the route. Should match either router alias or controller alias. */ if ($route->hasName($name) === true) { + $this->debug('Found route "%s" by name "%s"', $route->getUrl(), $name); + return $route; } /* Direct match to controller */ if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($name)) { + $this->debug('Found route "%s" by controller "%s"', $route->getUrl(), $name); + return $route; } @@ -408,6 +478,8 @@ class Router [$controller, $method] = array_map('strtolower', explode('@', $name)); if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) { + $this->debug('Found route "%s" by controller "%s" and method "%s"', $route->getUrl(), $controller, $method); + return $route; } } @@ -417,16 +489,22 @@ class Router /* Check if the entire callback is matching */ if (strpos($route->getCallback(), $name) === 0 || strtolower($route->getCallback()) === strtolower($name)) { + $this->debug('Found route "%s" by callback "%s"', $route->getUrl(), $name); + return $route; } /* Check if the class part of the callback matches (class@method) */ if (strtolower($name) === strtolower($route->getClass())) { + $this->debug('Found route "%s" by class "%s"', $route->getUrl(), $name); + return $route; } } } + $this->debug('Route not found'); + return null; } @@ -448,8 +526,10 @@ class Router * @throws InvalidArgumentException * @return string */ - public function getUrl($name = null, $parameters = null, $getParams = null) : string + public function getUrl(?string $name = null, $parameters = null, $getParams = null): string { + $this->debug('Finding url', \func_get_args()); + if ($getParams !== null && \is_array($getParams) === false) { throw new InvalidArgumentException('Invalid type for getParams. Must be array or null'); } @@ -513,28 +593,28 @@ class Router } /** - * Get bootmanagers + * Get BootManagers * @return array */ - public function getBootManagers() : array + public function getBootManagers(): array { return $this->bootManagers; } /** - * Set bootmanagers + * Set BootManagers * @param array $bootManagers */ - public function setBootManagers(array $bootManagers) : void + public function setBootManagers(array $bootManagers): void { $this->bootManagers = $bootManagers; } /** - * Add bootmanager + * Add BootManager * @param IRouterBootManager $bootManager */ - public function addBootManager(IRouterBootManager $bootManager) : void + public function addBootManager(IRouterBootManager $bootManager): void { $this->bootManagers[] = $bootManager; } @@ -544,7 +624,7 @@ class Router * * @return array */ - public function getProcessedRoutes() : array + public function getProcessedRoutes(): array { return $this->processedRoutes; } @@ -552,7 +632,7 @@ class Router /** * @return array */ - public function getRoutes() : array + public function getRoutes(): array { return $this->routes; } @@ -563,7 +643,7 @@ class Router * @param array $routes * @return static */ - public function setRoutes(array $routes) : self + public function setRoutes(array $routes): self { $this->routes = $routes; @@ -575,7 +655,7 @@ class Router * * @return Request */ - public function getRequest() : Request + public function getRequest(): Request { return $this->request; } @@ -584,7 +664,7 @@ class Router * Get csrf verifier class * @return BaseCsrfVerifier */ - public function getCsrfVerifier() : BaseCsrfVerifier + public function getCsrfVerifier(): ?BaseCsrfVerifier { return $this->csrfVerifier; } @@ -602,4 +682,47 @@ class Router return $this; } + /** + * Add new debug message + * @param string $message + * @param array $args + */ + public function debug(string $message, ...$args): void + { + if ($this->debugEnabled === false) { + return; + } + + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + $this->debugList[] = [ + 'message' => vsprintf($message, $args), + 'time' => number_format(microtime(true) - $this->debugStartTime, 10), + 'trace' => end($trace), + ]; + } + + /** + * Enable or disables debugging + * + * @param bool $boolean + */ + public function setDebugEnabled(bool $boolean): void + { + if ($boolean === true) { + $this->debugStartTime = microtime(true); + } + + $this->debugEnabled = $boolean; + } + + /** + * Get the list containing all debug messages. + * + * @return array + */ + public function getDebugLog(): array + { + return $this->debugList; + } + } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 8f64e70..ea0410f 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -46,6 +46,8 @@ class SimpleRouter protected static $router; /** + * Start routing + * * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws HttpException * @throws \Exception @@ -56,21 +58,55 @@ class SimpleRouter } /** - * Get debug info array + * Start the routing an return array with debugging-information * * @return array * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public static function debugInfo(): array + public static function startDebug(): array { + $routerOutput = null; + + try { + ob_start(); + static::router()->setDebugEnabled(true); + static::start(); + $routerOutput = ob_get_contents(); + ob_end_clean(); + } catch (\Exception $e) { + + } + + // Try to parse library version + $composerFile = \dirname(__DIR__, 3) . '/composer.lock'; + $version = false; + + if (is_file($composerFile) === true) { + $composerInfo = json_decode(file_get_contents($composerFile), true); + + if (isset($composerInfo['packages']) === true && \is_array($composerInfo['packages']) === true) { + foreach ($composerInfo['packages'] as $package) { + if (isset($package['name']) === true && strtolower($package['name']) === 'pecee/simple-router') { + $version = $package['version']; + break; + } + } + } + } + return [ - 'url' => static::request()->getUrl(), - 'method' => static::request()->getMethod(), - 'host' => static::request()->getHost(), - 'loaded_routes' => static::request()->getLoadedRoutes(), - 'all_routes' => static::router()->getRoutes(), - 'boot_managers' => static::router()->getBootManagers(), - 'csrf_verifier' => static::router()->getCsrfVerifier(), + 'url' => static::request()->getUrl(), + 'method' => static::request()->getMethod(), + 'host' => static::request()->getHost(), + 'loaded_routes' => static::request()->getLoadedRoutes(), + 'all_routes' => static::router()->getRoutes(), + 'boot_managers' => static::router()->getBootManagers(), + 'csrf_verifier' => static::router()->getCsrfVerifier(), + 'log' => static::router()->getDebugLog(), + 'router_output' => $routerOutput, + 'library_version' => $version, + 'php_version' => PHP_VERSION, + 'server_params' => static::request()->getHeaders(), ]; } diff --git a/tests/Pecee/SimpleRouter/RouterUrlTest.php b/tests/Pecee/SimpleRouter/RouterUrlTest.php index 5c6d0e7..78f9e1a 100644 --- a/tests/Pecee/SimpleRouter/RouterUrlTest.php +++ b/tests/Pecee/SimpleRouter/RouterUrlTest.php @@ -31,21 +31,22 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase { // Test spanish characters TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']); + TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']); TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'get'); + $this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl()); + TestRouter::debugNoReset('/test/Dermatología'); + $parameters = TestRouter::request()->getLoadedRoute()->getParameters(); + + $this->assertEquals('Dermatología', $parameters['param']); + // Test danish characters TestRouter::get('/kategori/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']); TestRouter::debugNoReset('/kategori/økse', 'get'); + $this->assertEquals('/kategori/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl()); - TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']); - TestRouter::debugNoReset('/test/Dermatología'); - - $parameters = TestRouter::request()->getLoadedRoute()->getParameters(); - - $this->assertEquals('Dermatología', $parameters['param']); - TestRouter::router()->reset(); } diff --git a/tests/debug.php b/tests/debug.php new file mode 100644 index 0000000..a582af8 --- /dev/null +++ b/tests/debug.php @@ -0,0 +1,9 @@ +where(['name' => '[\w]+']); +$debugInfo = SimpleRouter::startDebug(); +echo sprintf('
%s
', var_export($debugInfo, true)); +exit; \ No newline at end of file From ef4582dbe028fd2a3a37186bcea672b168d1c6a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Tue, 27 Mar 2018 01:11:44 +0200 Subject: [PATCH 03/25] Fixed wrong return-type in loadClass. --- src/Pecee/SimpleRouter/Route/Route.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 12d6aff..2f349c3 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -60,10 +60,10 @@ abstract class Route implements IRoute /** * Load class by name * @param string $name - * @return object + * @return mixed * @throws NotFoundHttpException */ - protected function loadClass($name): object + protected function loadClass($name) { if (class_exists($name) === false) { throw new NotFoundHttpException(sprintf('Class "%s" does not exist', $name), 404); From aa56d45f9c5f409c23c0792af4eda8d5173eb865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 10:13:23 +0200 Subject: [PATCH 04/25] Updated documentation --- README.md | 778 +++++++++++++++++++++++++++--------------------------- 1 file changed, 392 insertions(+), 386 deletions(-) diff --git a/README.md b/README.md index 18b4e2a..8034920 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,7 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Setting up IIS](#setting-up-iis) - [Configuration](#configuration) - [Helper functions](#helper-functions) -- [Help and support](#help-and-support) - - [How to debug](#how-to-debug) - - [Creating unit-tests](#creating-unit-tests) - - [Debug information](#debug-information) - - [Benchmark and log-info](#benchmark-and-log-info) - - [Reporting a new issue](#reporting-a-new-issue) - - [Procedure for reporting a new issue](#procedure-for-reporting-a-new-issue) - - [Issue template](#issue-template) + - [Routes](#routes) - [Basic routing](#basic-routing) - [Available methods](#available-methods) @@ -58,6 +51,7 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Middlewares](#middlewares) - [Example](#example) + - [ExceptionHandlers](#exceptionhandlers) - [Handling 404, 403 and other errors](#handling-404-403-and-other-errors) - [Using custom exception handlers](#using-custom-exception-handlers) @@ -85,6 +79,16 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Parameters](#parameters) - [Extending](#extending) +- [Help and support](#help-and-support) + - [Common issues and fixes](#common-issues-and-fixes) + - [Debugging](#debugging) + - [Creating unit-tests](#creating-unit-tests) + - [Debug information](#debug-information) + - [Benchmark and log-info](#benchmark-and-log-info) + - [Reporting a new issue](#reporting-a-new-issue) + - [Procedure for reporting a new issue](#procedure-for-reporting-a-new-issue) + - [Issue template](#issue-template) + - [Credits](#credits) - [Sites](#sites) - [License](#license) @@ -382,379 +386,6 @@ function csrf_token() --- -# Help and support - -This section will go into details on how to debug the router and answer some of the commonly asked questions- and issues. - -## How to debug - -This section will show you how to write unit-tests for the router, view useful debugging information and answer some of the frequently asked questions. - -It will also covers how to report any issue you might encounter. - -### Creating unit-tests - -The easiest and fastest way to debug any issues with the router, is to create a unit-test that represents the issue you are experiencing. - -Unit-tests use a special `TestRouter` class, which simulates a request-method and requested url of a browser. - -The `TestRouter` class can return the output directly or render a route silently. - -```php -public function testUnicodeCharacters() -{ - // Add route containing two optional paramters with special spanish characters like "í". - TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']); - - // Start the routing and simulate the url "/cursos/listado/especialidad/cirugía local". - TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'GET'); - - // Verify that the url for the loaded route matches the expected route. - $this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl()); - - // Start the routing and simulate the url "/test/Dermatología" using "GET" as request-method. - TestRouter::debugNoReset('/test/Dermatología', 'GET'); - - // Another route containing one parameter with special spanish characters like "í". - TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']); - - // Get all parameters parsed by the loaded route. - $parameters = TestRouter::request()->getLoadedRoute()->getParameters(); - - // Check that the parameter named "param" matches the exspected value. - $this->assertEquals('Dermatología', $parameters['param']); - - // Add route testing danish special characters like "ø". - TestRouter::get('/category/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']); - - // Start the routing and simulate the url "/kategory/økse" using "GET" as request-method. - TestRouter::debugNoReset('/category/økse', 'GET'); - - // Validate that the URL of the loaded-route matches the expected url. - $this->assertEquals('/category/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl()); - - // Reset the router, so other tests wont inherit settings or the routes we've added. - TestRouter::router()->reset(); -} -``` - -#### Using the TestRouter helper - -Depending on your test, you can use the methods below when rendering routes in your unit-tests. - - -| Method | Description | -| ------------- |-------------| -| ```TestRouter::debug($url, $method)``` | Will render the route without returning anything. Exceptions will be thrown and the router will be reset automatically. | -| ```TestRouter::debugOutput($url, $method)``` | Will render the route and return any value that the route might output. Manual reset required by calling `TestRouter::router()->reset()`. | -| ```TestRouter::debugNoReset($url, $method);``` | Will render the route without resetting the router. Useful if you need to get loaded route, parameters etc. from the router. Manual reset required by calling `TestRouter::router()->reset()`. | - -### Debug information - -The library can output debug-information, which contains information like loaded routes, the parsed request-url etc. It also contains info which are important when reporting a new issue like PHP-version, library version, server-variables, router debug log etc. - -You can activate the debug-information by calling the alternative start-method. - -The example below will start the routing an return array with debugging-information - -**Example:** - -```php -$debugInfo = SimpleRouter::startDebug(); -echo sprintf('
%s
', var_export($debugInfo)); -exit; -``` - -**The example above will provide you with an output containing:** - -| Key | Description | -| ------------- |------------- | -| `url` | The parsed request-uri. This url should match the url in the browser.| -| `method` | The browsers request method (example: `GET`, `POST`, `PUT`, `PATCH`, `DELETE` etc).| -| `host` | The website host (example: `domain.com`).| -| `loaded_routes` | List of all the routes that matched the `url` and that has been rendered/loaded. | -| `all_routes` | All available routes | -| `boot_managers` | All available BootManagers | -| `csrf_verifier` | CsrfVerifier class | -| `log` | List of debug messages/log from the router. | -| `router_output` | The rendered callback output from the router. | -| `library_version` | The version of simple-php-router you are using. | -| `php_version` | The version of PHP you are using. | -| `server_params` | List of all `$_SERVER` variables/headers. | - -#### Benchmark and logging - -You can activate benchmark debugging/logging by calling `setDebugEnabled` method on the `Router` instance. - -You have to enable debugging BEFORE starting the routing. - -**Example:** - -```php -SimpleRouter::router()->setDebugEnabled(true); -SimpleRouter::start(); -``` - -When the routing is complete, you can get the debug-log by calling the `getDebugLog()` on the `Router` instance. This will return an `array` of log-messages each containing execution time, trace info and debug-message. - -**Example:** - -```php -$messages = SimpleRouter::router()->getDebugLog(); -``` - -## Reporting a new issue - -**Before reporting your issue, make sure that the issue you are experiencing aren't already answered in the [Common errors](#common-errors) section or by searching the [closed issues](https://github.com/skipperbent/simple-php-router/issues?q=is%3Aissue+is%3Aclosed) page on GitHub.** - -To avoid confusion and to help you resolve your issue as quickly as possible, you should provide a detailed explanation of the problem you are experiencing. - -### Procedure for reporting a new issue - -1. Go to [this page](https://github.com/skipperbent/simple-php-router/issues/new) to create a new issue. -2. Add a title that describes your problems in as few words as possible. -3. Copy and paste the template below in the description of your issue and replace each step with your own information. If the step is not relevant for your issue you can delete it. - -### Issue template - -Copy and paste the template below into the description of your new issue and replace it with your own information. - -You can check the [Debug information](#debug-information) section to see how to generate the debug-info. - -
-### Description
-
-The library fails to render the route `/user/æsel` which contains one parameter using a custom regular expression for matching special foreign characters. Routes without special characters like `/user/tom` renders correctly.
-
-### Steps to reproduce the error
-
-1. Add the following route:
-
-```php
-SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
-```
-
-2. Navigate to `/user/æsel` in browser.
-
-3. `NotFoundHttpException` is thrown by library.
-
-### Route and/or callback for failing route
-
-*Route:*
-
-```php
-SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
-```
-
-*Callback:*
-
-```php
-public function show($username) {
-    return sprintf('Username is: %s', $username);
-}
-```
-
-### Debug info
-
-```php
-array (
-  'url' => 
-  Pecee\Http\Url::__set_state(array(
-     'originalUrl' => NULL,
-     'data' => 
-    array (
-      'scheme' => NULL,
-      'host' => NULL,
-      'port' => NULL,
-      'user' => NULL,
-      'pass' => NULL,
-      'path' => NULL,
-      'query' => NULL,
-      'fragment' => NULL,
-    ),
-  )),
-  'method' => '',
-  'host' => NULL,
-  'loaded_routes' => 
-  array (
-  ),
-  'all_routes' => 
-  array (
-    0 => 
-    Pecee\SimpleRouter\Route\RouteUrl::__set_state(array(
-       'url' => '/user/{name}/',
-       'name' => NULL,
-       'regex' => NULL,
-       'filterEmptyParams' => true,
-       'defaultParameterRegex' => NULL,
-       'paramModifiers' => '{}',
-       'paramOptionalSymbol' => '?',
-       'urlRegex' => '/^%s\\/?$/u',
-       'group' => NULL,
-       'parent' => NULL,
-       'callback' => 'UserController@show',
-       'defaultNamespace' => NULL,
-       'namespace' => NULL,
-       'requestMethods' => 
-      array (
-        0 => 'get',
-      ),
-       'where' => 
-      array (
-        'name' => '[\\w]+',
-      ),
-       'parameters' => 
-      array (
-        'name' => NULL,
-      ),
-       'originalParameters' => 
-      array (
-      ),
-       'middlewares' => 
-      array (
-      ),
-    )),
-  ),
-  'boot_managers' => 
-  array (
-  ),
-  'csrf_verifier' => NULL,
-  'log' => 
-  array (
-    0 => 
-    array (
-      'message' => 'Started routing request (rewrite: no)',
-      'time' => '0.0000069141',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
-        'line' => 57,
-        'function' => 'routeRequest',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-    1 => 
-    array (
-      'message' => 'Loading routes',
-      'time' => '0.0036418438',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
-        'line' => 273,
-        'function' => 'loadRoutes',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-    2 => 
-    array (
-      'message' => 'Processing routes',
-      'time' => '0.0069010258',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
-        'line' => 251,
-        'function' => 'processRoutes',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-    3 => 
-    array (
-      'message' => 'Processing route "Pecee\\SimpleRouter\\Route\\RouteUrl"',
-      'time' => '0.0099139214',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
-        'line' => 251,
-        'function' => 'processRoutes',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-    4 => 
-    array (
-      'message' => 'Finished loading routes',
-      'time' => '0.0130679607',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
-        'line' => 273,
-        'function' => 'loadRoutes',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-    5 => 
-    array (
-      'message' => 'Matching route "Pecee\\SimpleRouter\\Route\\RouteUrl"',
-      'time' => '0.0160858631',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
-        'line' => 57,
-        'function' => 'routeRequest',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-    6 => 
-    array (
-      'message' => 'Route not found: "/"',
-      'time' => '0.0193598270',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
-        'line' => 57,
-        'function' => 'routeRequest',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-    7 => 
-    array (
-      'message' => 'Starting exception handling for "Pecee\\SimpleRouter\\Exceptions\\NotFoundHttpException"',
-      'time' => '0.0229449272',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
-        'line' => 345,
-        'function' => 'handleException',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-    8 => 
-    array (
-      'message' => 'Finished exception handling - exception not handled, throwing',
-      'time' => '0.0258929729',
-      'trace' => 
-      array (
-        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
-        'line' => 345,
-        'function' => 'handleException',
-        'class' => 'Pecee\\SimpleRouter\\Router',
-        'type' => '->',
-      ),
-    ),
-  ),
-  'router_output' => NULL,
-  'library_version' => false,
-  'php_version' => '7.2.0',
-  'server_params' => 
-  array (),
-)
-```
-
-
- -Remember that a more detailed issue- description and debug-info might suck to write, but it will help others understand- and resolve your issue without asking for the information. - -**Note:** please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue. Providing the necessary steps to reproduce the error within your description, adding useful debugging info etc. will help others quickly resolve the issue you are reporting. - ---- - # Routes Remember the ```routes.php``` file you required in your ```index.php```? This file be where you place all your custom rules for routing. @@ -1226,7 +857,6 @@ class CustomMiddleware implements Middleware { // If authentication failed, redirect request to user-login page. if($request->user === null) { $request->setRewriteUrl(url('user.login')); - return $request; } } @@ -1276,7 +906,7 @@ class CustomExceptionHandler implements IExceptionHandler /* You can use the exception handler to format errors depending on the request and type. */ - if (stripos($request->getUrl()->getPath(), '/api') !== false) { + if ($request->getUrl()->contains('/api')) { response()->json([ 'error' => $error->getMessage(), @@ -1289,10 +919,9 @@ class CustomExceptionHandler implements IExceptionHandler if($error instanceof NotFoundHttpException) { // Render custom 404-page - $request->setRewriteCallback('Demo\Controllers\PageController@notFound'); - return $request; - + return; + } throw $error; @@ -1641,6 +1270,383 @@ class Router extends SimpleRouter { --- +# Help and support + +This section will go into details on how to debug the router and answer some of the commonly asked questions- and issues. + +## Common issues and fixes + +This section will go over common issues and how to resolve them. + +## Debugging + +This section will show you how to write unit-tests for the router, view useful debugging information and answer some of the frequently asked questions. + +It will also covers how to report any issue you might encounter. + +### Creating unit-tests + +The easiest and fastest way to debug any issues with the router, is to create a unit-test that represents the issue you are experiencing. + +Unit-tests use a special `TestRouter` class, which simulates a request-method and requested url of a browser. + +The `TestRouter` class can return the output directly or render a route silently. + +```php +public function testUnicodeCharacters() +{ + // Add route containing two optional paramters with special spanish characters like "í". + TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']); + + // Start the routing and simulate the url "/cursos/listado/especialidad/cirugía local". + TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'GET'); + + // Verify that the url for the loaded route matches the expected route. + $this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl()); + + // Start the routing and simulate the url "/test/Dermatología" using "GET" as request-method. + TestRouter::debugNoReset('/test/Dermatología', 'GET'); + + // Another route containing one parameter with special spanish characters like "í". + TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']); + + // Get all parameters parsed by the loaded route. + $parameters = TestRouter::request()->getLoadedRoute()->getParameters(); + + // Check that the parameter named "param" matches the exspected value. + $this->assertEquals('Dermatología', $parameters['param']); + + // Add route testing danish special characters like "ø". + TestRouter::get('/category/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']); + + // Start the routing and simulate the url "/kategory/økse" using "GET" as request-method. + TestRouter::debugNoReset('/category/økse', 'GET'); + + // Validate that the URL of the loaded-route matches the expected url. + $this->assertEquals('/category/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl()); + + // Reset the router, so other tests wont inherit settings or the routes we've added. + TestRouter::router()->reset(); +} +``` + +#### Using the TestRouter helper + +Depending on your test, you can use the methods below when rendering routes in your unit-tests. + + +| Method | Description | +| ------------- |-------------| +| ```TestRouter::debug($url, $method)``` | Will render the route without returning anything. Exceptions will be thrown and the router will be reset automatically. | +| ```TestRouter::debugOutput($url, $method)``` | Will render the route and return any value that the route might output. Manual reset required by calling `TestRouter::router()->reset()`. | +| ```TestRouter::debugNoReset($url, $method);``` | Will render the route without resetting the router. Useful if you need to get loaded route, parameters etc. from the router. Manual reset required by calling `TestRouter::router()->reset()`. | + +### Debug information + +The library can output debug-information, which contains information like loaded routes, the parsed request-url etc. It also contains info which are important when reporting a new issue like PHP-version, library version, server-variables, router debug log etc. + +You can activate the debug-information by calling the alternative start-method. + +The example below will start the routing an return array with debugging-information + +**Example:** + +```php +$debugInfo = SimpleRouter::startDebug(); +echo sprintf('
%s
', var_export($debugInfo)); +exit; +``` + +**The example above will provide you with an output containing:** + +| Key | Description | +| ------------- |------------- | +| `url` | The parsed request-uri. This url should match the url in the browser.| +| `method` | The browsers request method (example: `GET`, `POST`, `PUT`, `PATCH`, `DELETE` etc).| +| `host` | The website host (example: `domain.com`).| +| `loaded_routes` | List of all the routes that matched the `url` and that has been rendered/loaded. | +| `all_routes` | All available routes | +| `boot_managers` | All available BootManagers | +| `csrf_verifier` | CsrfVerifier class | +| `log` | List of debug messages/log from the router. | +| `router_output` | The rendered callback output from the router. | +| `library_version` | The version of simple-php-router you are using. | +| `php_version` | The version of PHP you are using. | +| `server_params` | List of all `$_SERVER` variables/headers. | + +#### Benchmark and logging + +You can activate benchmark debugging/logging by calling `setDebugEnabled` method on the `Router` instance. + +You have to enable debugging BEFORE starting the routing. + +**Example:** + +```php +SimpleRouter::router()->setDebugEnabled(true); +SimpleRouter::start(); +``` + +When the routing is complete, you can get the debug-log by calling the `getDebugLog()` on the `Router` instance. This will return an `array` of log-messages each containing execution time, trace info and debug-message. + +**Example:** + +```php +$messages = SimpleRouter::router()->getDebugLog(); +``` + +## Reporting a new issue + +**Before reporting your issue, make sure that the issue you are experiencing aren't already answered in the [Common errors](#common-errors) section or by searching the [closed issues](https://github.com/skipperbent/simple-php-router/issues?q=is%3Aissue+is%3Aclosed) page on GitHub.** + +To avoid confusion and to help you resolve your issue as quickly as possible, you should provide a detailed explanation of the problem you are experiencing. + +### Procedure for reporting a new issue + +1. Go to [this page](https://github.com/skipperbent/simple-php-router/issues/new) to create a new issue. +2. Add a title that describes your problems in as few words as possible. +3. Copy and paste the template below in the description of your issue and replace each step with your own information. If the step is not relevant for your issue you can delete it. + +### Issue template + +Copy and paste the template below into the description of your new issue and replace it with your own information. + +You can check the [Debug information](#debug-information) section to see how to generate the debug-info. + +
+### Description
+
+The library fails to render the route `/user/æsel` which contains one parameter using a custom regular expression for matching special foreign characters. Routes without special characters like `/user/tom` renders correctly.
+
+### Steps to reproduce the error
+
+1. Add the following route:
+
+```php
+SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
+```
+
+2. Navigate to `/user/æsel` in browser.
+
+3. `NotFoundHttpException` is thrown by library.
+
+### Route and/or callback for failing route
+
+*Route:*
+
+```php
+SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
+```
+
+*Callback:*
+
+```php
+public function show($username) {
+    return sprintf('Username is: %s', $username);
+}
+```
+
+### Debug info
+
+```php
+array (
+  'url' => 
+  Pecee\Http\Url::__set_state(array(
+     'originalUrl' => NULL,
+     'data' => 
+    array (
+      'scheme' => NULL,
+      'host' => NULL,
+      'port' => NULL,
+      'user' => NULL,
+      'pass' => NULL,
+      'path' => NULL,
+      'query' => NULL,
+      'fragment' => NULL,
+    ),
+  )),
+  'method' => '',
+  'host' => NULL,
+  'loaded_routes' => 
+  array (
+  ),
+  'all_routes' => 
+  array (
+    0 => 
+    Pecee\SimpleRouter\Route\RouteUrl::__set_state(array(
+       'url' => '/user/{name}/',
+       'name' => NULL,
+       'regex' => NULL,
+       'filterEmptyParams' => true,
+       'defaultParameterRegex' => NULL,
+       'paramModifiers' => '{}',
+       'paramOptionalSymbol' => '?',
+       'urlRegex' => '/^%s\\/?$/u',
+       'group' => NULL,
+       'parent' => NULL,
+       'callback' => 'UserController@show',
+       'defaultNamespace' => NULL,
+       'namespace' => NULL,
+       'requestMethods' => 
+      array (
+        0 => 'get',
+      ),
+       'where' => 
+      array (
+        'name' => '[\\w]+',
+      ),
+       'parameters' => 
+      array (
+        'name' => NULL,
+      ),
+       'originalParameters' => 
+      array (
+      ),
+       'middlewares' => 
+      array (
+      ),
+    )),
+  ),
+  'boot_managers' => 
+  array (
+  ),
+  'csrf_verifier' => NULL,
+  'log' => 
+  array (
+    0 => 
+    array (
+      'message' => 'Started routing request (rewrite: no)',
+      'time' => '0.0000069141',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
+        'line' => 57,
+        'function' => 'routeRequest',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    1 => 
+    array (
+      'message' => 'Loading routes',
+      'time' => '0.0036418438',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 273,
+        'function' => 'loadRoutes',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    2 => 
+    array (
+      'message' => 'Processing routes',
+      'time' => '0.0069010258',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 251,
+        'function' => 'processRoutes',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    3 => 
+    array (
+      'message' => 'Processing route "Pecee\\SimpleRouter\\Route\\RouteUrl"',
+      'time' => '0.0099139214',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 251,
+        'function' => 'processRoutes',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    4 => 
+    array (
+      'message' => 'Finished loading routes',
+      'time' => '0.0130679607',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 273,
+        'function' => 'loadRoutes',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    5 => 
+    array (
+      'message' => 'Matching route "Pecee\\SimpleRouter\\Route\\RouteUrl"',
+      'time' => '0.0160858631',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
+        'line' => 57,
+        'function' => 'routeRequest',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    6 => 
+    array (
+      'message' => 'Route not found: "/"',
+      'time' => '0.0193598270',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php',
+        'line' => 57,
+        'function' => 'routeRequest',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    7 => 
+    array (
+      'message' => 'Starting exception handling for "Pecee\\SimpleRouter\\Exceptions\\NotFoundHttpException"',
+      'time' => '0.0229449272',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 345,
+        'function' => 'handleException',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+    8 => 
+    array (
+      'message' => 'Finished exception handling - exception not handled, throwing',
+      'time' => '0.0258929729',
+      'trace' => 
+      array (
+        'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php',
+        'line' => 345,
+        'function' => 'handleException',
+        'class' => 'Pecee\\SimpleRouter\\Router',
+        'type' => '->',
+      ),
+    ),
+  ),
+  'router_output' => NULL,
+  'library_version' => false,
+  'php_version' => '7.2.0',
+  'server_params' => 
+  array (),
+)
+```
+
+
+ +Remember that a more detailed issue- description and debug-info might suck to write, but it will help others understand- and resolve your issue without asking for the information. + +**Note:** please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue. Providing the necessary steps to reproduce the error within your description, adding useful debugging info etc. will help others quickly resolve the issue you are reporting. + +--- + # Credits ## Sites From a9c03f927120ffa302fed6591ac6d077ec1cb675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 18:17:42 +0200 Subject: [PATCH 05/25] Development - Updated `helpers.php` and helpers example in documentation. - MalformedUrlException is now handled properly by Router to avoid phpStorm syntax highlights in routes. - Added `getUrlCopy` to `Request` class, used to clone the current route (to keep domain etc.) - `setUrl` in `Request` are now strict and requires `Url` object and no longer accepts strings. - Renamed `hasRewrite` property to `hasPendingRewrite` in `Request` class. - Renamed `hasRewrite` and `setHasRewrite` methods to `hasPendingRewrite` and `setHasPendingRewrite` in `Request` class. - Added better usage of `Url` class. When calling `url` you can now use the methods on the `Url` class to filter params, get relative/absolute url etc. See documentation for more info. - Renamed `get` method to `getValue` in `InputHandler` class. - Renamed `getObject` to `get` and removed `$defaultValue` argument in `InputHandler` class. - Optimized `InputHandler` class. - Fixed issue with `$token` not being proper string in `BaseCsrfVerifier` when token is not found. - Added php.ini configuration settings to `setcookie` in `CookieTokenProvider` for improved security. - Added `$router` parameter to `boot` method in `IRouterBootManager` which allows for further manipulation of the router within the bootmanager. - Renamed `$processingRoute` property to `$isProcessingRoute` in `Router` class. - Fixed `reset` method not resetting CSRF-verifier in `Router` class. - Moved `arrayToParams` helper-method from `Router` to `Url` class. - Began to add Event-functionality to router. - Added `addEventHandler` method to `SimpleRouter` class. - Moved `Pecee\SimpleRouter\Handler\CallbackExceptionHandler` to `Pecee\SimpleRouter\Handlers\CallbackExceptionHandler`. - Moved `Pecee\SimpleRouter\Handler\IExceptionHandler` to `Pecee\SimpleRouter\Handlers\IExceptionHandler`. - Added Events section to documentation. - Added more information on url-handling in documentation. - Optimisations. --- README.md | 521 ++++++++++-------- helpers.php | 33 +- src/Pecee/Http/Input/InputHandler.php | 41 +- .../Http/Middleware/BaseCsrfVerifier.php | 13 +- src/Pecee/Http/Request.php | 83 ++- .../Http/Security/CookieTokenProvider.php | 2 +- src/Pecee/Http/Url.php | 362 ++++++++++-- .../SimpleRouter/Event/EventArgument.php | 83 +++ .../SimpleRouter/Event/IEventArgument.php | 46 ++ .../Handlers/CallbackExceptionHandler.php | 4 +- .../Handlers/DebugEventHandler.php | 62 +++ .../SimpleRouter/Handlers/EventHandler.php | 178 ++++++ .../SimpleRouter/Handlers/IEventHandler.php | 26 + .../Handlers/IExceptionHandler.php | 2 +- src/Pecee/SimpleRouter/IRouterBootManager.php | 3 +- src/Pecee/SimpleRouter/Route/IGroupRoute.php | 2 +- .../SimpleRouter/Route/ILoadableRoute.php | 4 +- .../SimpleRouter/Route/LoadableRoute.php | 2 +- src/Pecee/SimpleRouter/Route/Route.php | 2 +- .../SimpleRouter/Route/RouteController.php | 2 +- src/Pecee/SimpleRouter/Route/RouteGroup.php | 2 +- .../SimpleRouter/Route/RoutePartialGroup.php | 9 +- .../SimpleRouter/Route/RouteResource.php | 8 +- src/Pecee/SimpleRouter/Router.php | 243 +++++--- src/Pecee/SimpleRouter/SimpleRouter.php | 80 +-- .../Dummy/Handler/ExceptionHandler.php | 2 +- .../Dummy/Handler/ExceptionHandlerFirst.php | 4 +- .../Dummy/Handler/ExceptionHandlerSecond.php | 4 +- .../Dummy/Handler/ExceptionHandlerThird.php | 2 +- .../Dummy/Managers/TestBootManager.php | 32 ++ .../Dummy/Security/SilentTokenProvider.php | 41 ++ tests/Pecee/SimpleRouter/EventHandlerTest.php | 75 +++ tests/Pecee/SimpleRouter/InputHandlerTest.php | 30 + tests/Pecee/SimpleRouter/RouterRouteTest.php | 8 +- tests/TestRouter.php | 6 +- 35 files changed, 1528 insertions(+), 489 deletions(-) create mode 100644 src/Pecee/SimpleRouter/Event/EventArgument.php create mode 100644 src/Pecee/SimpleRouter/Event/IEventArgument.php rename src/Pecee/{ => SimpleRouter}/Handlers/CallbackExceptionHandler.php (90%) create mode 100644 src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php create mode 100644 src/Pecee/SimpleRouter/Handlers/EventHandler.php create mode 100644 src/Pecee/SimpleRouter/Handlers/IEventHandler.php rename src/Pecee/{ => SimpleRouter}/Handlers/IExceptionHandler.php (84%) create mode 100644 tests/Pecee/SimpleRouter/Dummy/Managers/TestBootManager.php create mode 100644 tests/Pecee/SimpleRouter/Dummy/Security/SilentTokenProvider.php create mode 100644 tests/Pecee/SimpleRouter/EventHandlerTest.php create mode 100644 tests/Pecee/SimpleRouter/InputHandlerTest.php diff --git a/README.md b/README.md index 8034920..a592726 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,14 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Using custom exception handlers](#using-custom-exception-handlers) - [Urls](#urls) + - [Get the current url](#get-the-current-url) - [Get by name (single route)](#get-by-name-single-route) - [Get by name (controller route)](#get-by-name-controller-route) - [Get by class](#get-by-class) - - [Get by custom names for methods on a controller/resource route](#using-custom-names-for-methods-on-a-controllerresource-route) + - [Using custom names for methods on a controller/resource route](#using-custom-names-for-methods-on-a-controllerresource-route) - [Getting REST/resource controller urls](#getting-restresource-controller-urls) - - [Get the current url](#get-the-current-url) + - [Manipulating url](#manipulating-url) + - [Useful url tricks](#useful-url-tricks) - [Input & parameters](#input--parameters) - [Using the Input class to manage parameters](#using-the-input-class-to-manage-parameters) @@ -70,6 +72,11 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Get parameter object](#get-parameter-object) - [Managing files](#managing-files) - [Get all parameters](#get-all-parameters) + +- [Events](#events) + - [Available events](#available-events) + - [Registering new event](#registering-new-event) + - [Custom EventHandlers](#custom-eventhandlers) - [Advanced](#advanced) - [Url rewriting](#url-rewriting) @@ -298,7 +305,12 @@ We recommend that you add these helper functions to your project. These will all To implement the functions below, simply copy the code to a new file and require the file before initializing the router or copy the `helpers.php` we've included in this library. ```php +getInputHandler()->get($index, $defaultValue, $methods); + + if ($defaultValue !== null) { + return request()->getInputHandler()->getValue($index, $defaultValue, $methods); + } + + return request()->getInputHandler()->get($index, $methods); } return request()->getInputHandler(); } -function redirect($url, $code = null) +/** + * @param string $url + * @param int|null $code + */ +function redirect(string $url, ?int $code = null): void { if ($code !== null) { response()->httpCode($code); @@ -371,9 +388,8 @@ function redirect($url, $code = null) /** * Get current csrf-token * @return string|null - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ -function csrf_token() +function csrf_token(): ?string { $baseVerifier = Router::router()->getCsrfVerifier(); if ($baseVerifier !== null) { @@ -805,7 +821,7 @@ class SessionTokenProvider implements ITokenProvider /** * Refresh existing token */ - public function refresh() + public function refresh(): void { // Implement your own functionality here... } @@ -816,7 +832,18 @@ class SessionTokenProvider implements ITokenProvider * @param string $token * @return bool */ - public function validate($token) + public function validate($token): bool + { + // Implement your own functionality here... + } + + /** + * Get token token + * + * @param string|null $defaultValue + * @return string|null + */ + public function getToken(?string $defaultValue = null): ?string { // Implement your own functionality here... } @@ -847,10 +874,11 @@ namespace Demo\Middlewares; use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Request; -class CustomMiddleware implements Middleware { - - public function handle(Request $request) { +class CustomMiddleware implements IMiddleware { + public function handle(Request $request): void + { + // Authenticate user, will be available using request()->user $request->user = User::authenticate(); @@ -882,9 +910,11 @@ The code should be placed in the file that contains your routes. Router::get('/not-found', 'PageController@notFound'); Router::error(function(Request $request, \Exception $exception) { - if($exception instanceof NotFoundHttpException && $exception->getCode() == 404) { + + if($exception instanceof NotFoundHttpException && $exception->getCode() === 404) { response()->redirect('/not-found'); } + }); ``` @@ -895,13 +925,13 @@ This is a basic example of an ExceptionHandler implementation (please see "[Easi ```php namespace Demo\Handlers; -use Pecee\Handlers\IExceptionHandler; use Pecee\Http\Request; +use Pecee\SimpleRouter\Handlers\IExceptionHandler; use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; class CustomExceptionHandler implements IExceptionHandler { - public function handleError(Request $request, \Exception $error) + public function handleError(Request $request, \Exception $error): void { /* You can use the exception handler to format errors depending on the request and type. */ @@ -937,17 +967,33 @@ class CustomExceptionHandler implements IExceptionHandler By default all controller and resource routes will use a simplified version of their url as name. +You easily use the `url()` shortcut helper function to retrieve urls for your routes or manipulate the current url. + +`url()` will return a `Url` object which will return a `string` when rendered, so it can be used safely in templates etc. but +contains all the useful helpers methods in the `Url` class like `contains`, `indexOf` etc. +Check the [Useful url tricks](#useful-url-tricks) below. + +### Get the current url + +It has never been easier to get and/or manipulate the current url. + +The example below shows you how to get the current url: + +```php +# output: /current-url +url(); +``` + ### Get by name (single route) ```php SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']); +# output: /product-view/22/?category=shoes url('product', ['id' => 22], ['category' => 'shoes']); -url('product', null, ['category' => 'shoes']); -# output -# /product-view/22/?category=shoes -# /product-view/?category=shoes +# output: /product-view/?category=shoes +url('product', null, ['category' => 'shoes']); ``` ### Get by name (controller route) @@ -955,14 +1001,14 @@ url('product', null, ['category' => 'shoes']); ```php SimpleRouter::controller('/images', ImagesController::class, ['as' => 'picture']); +# output: /images/view/?category=shows url('picture@getView', null, ['category' => 'shoes']); -url('picture', 'getView', ['category' => 'shoes']); -url('picture', 'view'); -# output -# /images/view/?category=shows -# /images/view/?category=shows -# /images/view/ +# output: /images/view/?category=shows +url('picture', 'getView', ['category' => 'shoes']); + +# output: /images/view/ +url('picture', 'view'); ``` ### Get by class @@ -971,12 +1017,11 @@ url('picture', 'view'); SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']); SimpleRouter::controller('/images', 'ImagesController'); +# output: /product-view/22/?category=shoes url('ProductsController@show', ['id' => 22], ['category' => 'shoes']); -url('ImagesController@getImage', null, ['id' => 22]); -# output -# /product-view/22/?category=shoes -# /images/image/?id=22 +# output: /images/image/?id=22 +url('ImagesController@getImage', null, ['id' => 22]); ``` ### Using custom names for methods on a controller/resource route @@ -995,30 +1040,67 @@ url('gadgets.iphone'); ```php SimpleRouter::resource('/phones', PhonesController::class); +# output: /phones/ url('phones'); + +# output: /phones/ url('phones.index'); + +# output: /phones/create/ url('phones.create'); + +# output: /phones/edit/ url('phones.edit'); - -// etc.. - -# output -# /phones/ -# /phones/create/ -# /phones/edit/ ``` -### Get the current url +### Manipulating url + +You can easily manipulate the query-strings, by adding your get param arguments. ```php -url(); -url(null, null, ['q' => 'cars']); +# output: /current-url?q=cars -# output -# /CURRENT-URL/ -# /CURRENT-URL/?q=cars +url(null, null, ['q' => 'cars']); ``` +You can remove a query-string parameter by setting the value to `null`. + +The example below will remove any query-string parameter named `q` from the url but keep all others query-string parameters: + +```php +$url = url()->removeParam('q'); +``` + +For more information please check the [Useful url tricks](#useful-url-tricks) section of the documentation. + +### Useful url tricks + +Calling `url` will always return a `Url` object. Upon rendered it will return a `string` of the relative `url`, so it's safe to use in templates etc. + +However this allow us to use the useful methods on the `Url` object like `indexOf` and `contains` or retrieve specific parts of the url like the path, querystring parameters, host etc. You can also manipulate the url like removing- or adding parameters, changing host and more. + +In the example below, we check if the current url contains the `/api` part. + +```php +if(url()->contains('/api')) { + + // ... do stuff + +} +``` + +As mentioned earlier, you can also use the `Url` object to show specific parts of the url or control what part of the url you want. + +```php +# Grab the query-string parameter id from the current-url. +$id = url()->getParam('id'); + +# Get the absolute url for the current url. +$absoluteUrl = url()->getAbsoluteUrl(); +``` + +For more available methods please check the `Pecee\Http\Url` class. + # Input & parameters ## Using the Input class to manage parameters @@ -1062,7 +1144,7 @@ $object = input()->getObject($index, $defaultValue = null, $methods = null); * $defaultValue is returned if the value is empty. */ -$id = input()->get($index, $defaultValue, $method); +$id = input()->getValue($index, $defaultValue, $method); # -- shortcut to above -- @@ -1079,6 +1161,10 @@ $object = input($index, $defaultValue, 'file'); $object = input()->findGet($index, $defaultValue); $object = input()->findPost($index, $defaultValue); $object = input()->findFile($index, $defaultValue); + +# -- get the full object -- + +$object = input()->get($index, 'post', 'get'); ``` ### Managing files @@ -1143,6 +1229,134 @@ $siteId = input('site_id', 2, ['post', 'get']); --- +# Events + +This section will help you understand how to register your own callbacks to events in the router. +It will also cover the basics of event-handlers; how to use the handlers provided with the router and how to create your own custom event-handlers. + +## Available events + +This section contains all available events that can be registered using the `EventHandler`. + +| Name | Description | +| ------------- |------------- | +| `EventHandler::EVENT_ALL` | Fires when a event is triggered. | +| `EventHandler::EVENT_INIT` | Fires when router is initializing and before routes are loaded. | +| `EventHandler::EVENT_LOAD` | Fires when all routes has been loaded and rendered, just before the output is returned. | +| `EventHandler::EVENT_REWRITE` | Fires when a url-rewrite is and just before the routes are re-initialized. | +| `EventHandler::EVENT_BOOT` | Fires when the router is booting. This happens just before boot-managers are rendered and before any routes has been loaded. | +| `EventHandler::EVENT_RENDER_BOOTMANAGER` | Fires before a boot-manager is rendered. | +| `EventHandler::EVENT_LOAD_ROUTES` | Fires when the router is about to load all routes. | +| `EventHandler::EVENT_FIND_ROUTE` | Fires whenever the `findRoute` method is called within the `Router`. This usually happens when the router tries to find routes that contains a certain url, usually after the `EventHandler::EVENT_GET_URL` event. | +| `EventHandler::EVENT_GET_URL` | Fires whenever the `Router::getUrl` method or `url`-helper function is called and the router tries to find the route. | +| `EventHandler::EVENT_MATCH_ROUTE` | Fires when a route is matched and valid (correct request-type etc). and before the route is rendered. | +| `EventHandler::EVENT_RENDER_ROUTE` | Fires before a route is rendered. | +| `EventHandler::EVENT_LOAD_EXCEPTIONS` | Fires when the router is loading exception-handlers. | +| `EventHandler::EVENT_RENDER_EXCEPTION` | Fires before the router is rendering a exception-handler. | +| `EventHandler::EVENT_RENDER_MIDDLEWARE` | Fires before a middleware is rendered. | +| `EventHandler::EVENT_RENDER_CSRF` | Fires before the CSRF-verifier is rendered. | + +## Registering new event + +To register a new event you need to create a new instance of the `EventHandler` object. On this object you can add as many callbacks as you like by calling the `registerEvent` method. + +When you've registered events, make sure to add it to the router by calling +`SimpleRouter::addEventHandler()`. We recommend that you add your event-handlers within your `routes.php`. + +**Example:** + +```php +use Pecee\SimpleRouter\Handlers\EventHandler; +use Pecee\SimpleRouter\Event\EventArgument; + +// --- your routes goes here --- + +$eventHandler = new EventHandler(); +$eventHandler->register(EventHandler::EVENT_RENDER_ROUTE, function(EventArgument $argument) { + + // Fires when route is rendered ... + +}); + +SimpleRouter::addEventHandler($eventHandler); + +``` + +## Custom EventHandlers + +`EventHandler` is the class that manages events and must inherit from the `IEventHandler` interface. The handler knows how to handle events for the given handler-type. + +Most of the time the basic `\Pecee\SimpleRouter\Handler\EventHandler` class will be more than enough for most people as you simply register an event which fires when triggered. + +Let's go over how to create your very own event-handler class. + +Below is a basic example of a custom event-handler called `DatabaseDebugHandler`. The idea of the sample below is to logs all events to the database when triggered. Hopefully it will be enough to give you an idea on how the event-handlers work. + +```php +namespace Demo\Handlers; + +use Pecee\SimpleRouter\Event\EventArgument; +use Pecee\SimpleRouter\Router; + +class DatabaseDebugHandler implements IEventHandler +{ + + /** + * Debug callback + * @var \Closure + */ + protected $callback; + + public function __construct() + { + $this->callback = function (EventArgument $argument) { + // todo: store log in database + }; + } + + /** + * Get events. + * + * @param string|null $name Filter events by name. + * @return array + */ + public function getEvents(?string $name): array + { + return [ + $name => [ + $this->callback, + ], + ]; + } + + /** + * Fires any events registered with given event-name + * + * @param Router $router Router instance + * @param string $name Event name + * @param array ...$eventArgs Event arguments + */ + public function fireEvents(Router $router, string $name, ...$eventArgs): void + { + $callback = $this->callback; + $callback(new EventArgument($router, $eventArgs)); + } + + /** + * Set debug callback + * + * @param \Closure $event + */ + public function setCallback(\Closure $event): void + { + $this->callback = $event; + } + +} +``` + +--- + # Advanced ## Url rewriting @@ -1173,10 +1387,19 @@ To interfere with the router, we create a class that implements the ```IRouterBo ```php use Pecee\Http\Request; use Pecee\SimpleRouter\IRouterBootManager; +use Pecee\SimpleRouter\Router; -class CustomRouterRules implement IRouterBootManager { +class CustomRouterRules implement IRouterBootManager +{ - public function boot(Request $request) { + /** + * Called when router is booting and before the routes is loaded. + * + * @param \Pecee\SimpleRouter\Router $router + * @param \Pecee\Http\Request $request + */ + public function boot(\Pecee\SimpleRouter\Router $router, \Pecee\Http\Request $request): void + { $rewriteRules = [ '/my-cat-is-beatiful' => '/article/view/1', @@ -1449,196 +1672,10 @@ public function show($username) { ### Debug info ```php -array ( - 'url' => - Pecee\Http\Url::__set_state(array( - 'originalUrl' => NULL, - 'data' => - array ( - 'scheme' => NULL, - 'host' => NULL, - 'port' => NULL, - 'user' => NULL, - 'pass' => NULL, - 'path' => NULL, - 'query' => NULL, - 'fragment' => NULL, - ), - )), - 'method' => '', - 'host' => NULL, - 'loaded_routes' => - array ( - ), - 'all_routes' => - array ( - 0 => - Pecee\SimpleRouter\Route\RouteUrl::__set_state(array( - 'url' => '/user/{name}/', - 'name' => NULL, - 'regex' => NULL, - 'filterEmptyParams' => true, - 'defaultParameterRegex' => NULL, - 'paramModifiers' => '{}', - 'paramOptionalSymbol' => '?', - 'urlRegex' => '/^%s\\/?$/u', - 'group' => NULL, - 'parent' => NULL, - 'callback' => 'UserController@show', - 'defaultNamespace' => NULL, - 'namespace' => NULL, - 'requestMethods' => - array ( - 0 => 'get', - ), - 'where' => - array ( - 'name' => '[\\w]+', - ), - 'parameters' => - array ( - 'name' => NULL, - ), - 'originalParameters' => - array ( - ), - 'middlewares' => - array ( - ), - )), - ), - 'boot_managers' => - array ( - ), - 'csrf_verifier' => NULL, - 'log' => - array ( - 0 => - array ( - 'message' => 'Started routing request (rewrite: no)', - 'time' => '0.0000069141', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php', - 'line' => 57, - 'function' => 'routeRequest', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 1 => - array ( - 'message' => 'Loading routes', - 'time' => '0.0036418438', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 273, - 'function' => 'loadRoutes', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 2 => - array ( - 'message' => 'Processing routes', - 'time' => '0.0069010258', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 251, - 'function' => 'processRoutes', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 3 => - array ( - 'message' => 'Processing route "Pecee\\SimpleRouter\\Route\\RouteUrl"', - 'time' => '0.0099139214', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 251, - 'function' => 'processRoutes', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 4 => - array ( - 'message' => 'Finished loading routes', - 'time' => '0.0130679607', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 273, - 'function' => 'loadRoutes', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 5 => - array ( - 'message' => 'Matching route "Pecee\\SimpleRouter\\Route\\RouteUrl"', - 'time' => '0.0160858631', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php', - 'line' => 57, - 'function' => 'routeRequest', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 6 => - array ( - 'message' => 'Route not found: "/"', - 'time' => '0.0193598270', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php', - 'line' => 57, - 'function' => 'routeRequest', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 7 => - array ( - 'message' => 'Starting exception handling for "Pecee\\SimpleRouter\\Exceptions\\NotFoundHttpException"', - 'time' => '0.0229449272', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 345, - 'function' => 'handleException', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 8 => - array ( - 'message' => 'Finished exception handling - exception not handled, throwing', - 'time' => '0.0258929729', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 345, - 'function' => 'handleException', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - ), - 'router_output' => NULL, - 'library_version' => false, - 'php_version' => '7.2.0', - 'server_params' => - array (), -) -``` +[PASTE YOUR DEBUG-INFO HERE] + +``` Remember that a more detailed issue- description and debug-info might suck to write, but it will help others understand- and resolve your issue without asking for the information. diff --git a/helpers.php b/helpers.php index 2c4d86d..f7234a3 100644 --- a/helpers.php +++ b/helpers.php @@ -1,6 +1,9 @@ getInputHandler()->get($index, $defaultValue, $methods); + + if ($defaultValue !== null) { + return request()->getInputHandler()->getValue($index, $defaultValue, $methods); + } + + return request()->getInputHandler()->get($index, $methods); } return request()->getInputHandler(); } -function redirect($url, $code = null) +/** + * @param string $url + * @param int|null $code + */ +function redirect(string $url, ?int $code = null): void { if ($code !== null) { response()->httpCode($code); @@ -73,9 +81,8 @@ function redirect($url, $code = null) /** * Get current csrf-token * @return string|null - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ -function csrf_token() +function csrf_token(): ?string { $baseVerifier = Router::router()->getCsrfVerifier(); if ($baseVerifier !== null) { diff --git a/src/Pecee/Http/Input/InputHandler.php b/src/Pecee/Http/Input/InputHandler.php index 6fee12a..1f1d0e1 100644 --- a/src/Pecee/Http/Input/InputHandler.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -87,7 +87,6 @@ class InputHandler } $keys = [$key]; - $files = $this->rearrangeFiles($value['name'], $keys, $value); if (isset($list[$key]) === true) { @@ -212,31 +211,26 @@ class InputHandler * Get input object * * @param string $index - * @param string|null $defaultValue - * @param array|string|null $methods - * @return IInputItem|string + * @param array ...$methods + * @return IInputItem|null */ - public function getObject(string $index, ?string $defaultValue = null, $methods = null) + public function get(string $index, ...$methods) : ?IInputItem { - if ($methods !== null && \is_string($methods) === true) { - $methods = [$methods]; - } - $element = null; - if ($methods === null || \in_array('get', $methods, true) === true) { + if (\count($methods) === 0 || \in_array('get', $methods, true) === true) { $element = $this->findGet($index); } - if (($element === null && $methods === null) || ($methods !== null && \in_array('post', $methods, true) === true)) { + if (($element === null && \count($methods) === 0) || (\count($methods) === 0 && \in_array('post', $methods, true) === true)) { $element = $this->findPost($index); } - if (($element === null && $methods === null) || ($methods !== null && \in_array('file', $methods, true) === true)) { + if (($element === null && \count($methods) === 0) || (\count($methods) === 0 && \in_array('file', $methods, true) === true)) { $element = $this->findFile($index); } - return $element ?? $defaultValue; + return $element; } /** @@ -244,14 +238,14 @@ class InputHandler * * @param string $index * @param string|null $defaultValue - * @param array|string|null $methods - * @return InputItem|string + * @param array ...$methods + * @return string */ - public function get(string $index, ?string $defaultValue = null, $methods = null) + public function getValue(string $index, ?string $defaultValue = null, ...$methods) : ?string { - $input = $this->getObject($index, $defaultValue, $methods); + $input = $this->get($index, $methods); - if ($input instanceof InputItem) { + if ($input !== null) { return (trim($input->getValue()) === '') ? $defaultValue : $input->getValue(); } @@ -262,11 +256,12 @@ class InputHandler * Check if a input-item exist * * @param string $index + * @param array ...$method * @return bool */ - public function exists(string $index): bool + public function exists(string $index, ...$method): bool { - return ($this->getObject($index) !== null); + return $this->get($index, $method) !== null; } /** @@ -276,12 +271,16 @@ class InputHandler */ public function all(array $filter = null): array { - $output = $_GET + $_POST; + $output = $_GET; if ($this->request->getMethod() === 'post') { + // Append POST data + $output += $_POST; + $contents = file_get_contents('php://input'); + // Append any PHP-input json if (strpos(trim($contents), '{') === 0) { $post = json_decode($contents, true); if ($post !== false) { diff --git a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php index 613058a..7116cd7 100644 --- a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php +++ b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php @@ -67,14 +67,13 @@ class BaseCsrfVerifier implements IMiddleware if ($this->skip($request) === false && \in_array($request->getMethod(), ['post', 'put', 'delete'], true) === true) { - $token = $request->getInputHandler()->get(static::POST_KEY, null, 'post'); + $token = $request->getInputHandler()->getValue( + static::POST_KEY, + $request->getHeader(static::HEADER_KEY), + 'post' + ); - // If the token is not posted, check headers for valid x-csrf-token - if ($token === null) { - $token = $request->getHeader(static::HEADER_KEY); - } - - if ($this->tokenProvider->validate($token) === false) { + if ($this->tokenProvider->validate((string)$token) === false) { throw new TokenMismatchException('Invalid CSRF-token.'); } diff --git a/src/Pecee/Http/Request.php b/src/Pecee/Http/Request.php index 6ae3a38..90397e6 100644 --- a/src/Pecee/Http/Request.php +++ b/src/Pecee/Http/Request.php @@ -2,6 +2,7 @@ namespace Pecee\Http; +use Pecee\Http\Exceptions\MalformedUrlException; use Pecee\Http\Input\InputHandler; use Pecee\SimpleRouter\Route\ILoadableRoute; use Pecee\SimpleRouter\Route\RouteUrl; @@ -9,19 +10,58 @@ use Pecee\SimpleRouter\SimpleRouter; class Request { + /** + * Additional data + * + * @var array + */ private $data = []; + + /** + * Server headers + * @var array + */ protected $headers = []; + + /** + * Request host + * @var string + */ protected $host; + + /** + * Current request url + * @var Url + */ protected $url; + + /** + * Request method + * @var string + */ protected $method; + + /** + * Input handler + * @var InputHandler + */ protected $inputHandler; - protected $hasRewrite = false; + /** + * Defines if request has pending rewrite + * @var bool + */ + protected $hasPendingRewrite = false; /** * @var ILoadableRoute|null */ protected $rewriteRoute; + + /** + * Rewrite url + * @var string|null + */ protected $rewriteUrl; /** @@ -31,7 +71,7 @@ class Request /** * Request constructor. - * @throws \Pecee\Http\Exceptions\MalformedUrlException + * @throws MalformedUrlException */ public function __construct() { @@ -43,10 +83,10 @@ class Request $this->setHost($this->getHeader('http-host')); // Check if special IIS header exist, otherwise use default. - $this->setUrl($this->getHeader('unencoded-url', $this->getHeader('request-uri'))); + $this->setUrl(new Url($this->getHeader('unencoded-url', $this->getHeader('request-uri')))); $this->inputHandler = new InputHandler($this); - $this->method = strtolower($this->inputHandler->get('_method', $this->getHeader('request-method'))); + $this->method = strtolower($this->inputHandler->getValue('_method', $this->getHeader('request-method'))); } public function isSecure(): bool @@ -62,6 +102,16 @@ class Request return $this->url; } + /** + * Copy url object + * + * @return Url + */ + public function getUrlCopy(): Url + { + return clone $this->url; + } + /** * @return string|null */ @@ -205,12 +255,15 @@ class Request } /** - * @param string|Url $url - * @throws \Pecee\Http\Exceptions\MalformedUrlException + * @param Url $url */ - public function setUrl($url): void + public function setUrl(Url $url): void { - $this->url = ($url instanceof Url) ? $url : new Url($url); + $this->url = $url; + + if ($this->url->getHost() === null) { + $this->url->setHost((string)$this->getHost()); + } } /** @@ -237,7 +290,7 @@ class Request */ public function setRewriteRoute(ILoadableRoute $route): self { - $this->hasRewrite = true; + $this->hasPendingRewrite = true; $this->rewriteRoute = SimpleRouter::addDefaultNamespace($route); return $this; @@ -271,7 +324,7 @@ class Request */ public function setRewriteUrl(string $rewriteUrl): self { - $this->hasRewrite = true; + $this->hasPendingRewrite = true; $this->rewriteUrl = rtrim($rewriteUrl, '/') . '/'; return $this; @@ -284,7 +337,7 @@ class Request */ public function setRewriteCallback($callback): self { - $this->hasRewrite = true; + $this->hasPendingRewrite = true; return $this->setRewriteRoute(new RouteUrl($this->getUrl()->getPath(), $callback)); } @@ -339,9 +392,9 @@ class Request * * @return bool */ - public function hasRewrite(): bool + public function hasPendingRewrite(): bool { - return $this->hasRewrite; + return $this->hasPendingRewrite; } /** @@ -350,9 +403,9 @@ class Request * @param bool $boolean * @return Request */ - public function setHasRewrite(bool $boolean): self + public function setHasPendingRewrite(bool $boolean): self { - $this->hasRewrite = $boolean; + $this->hasPendingRewrite = $boolean; return $this; } diff --git a/src/Pecee/Http/Security/CookieTokenProvider.php b/src/Pecee/Http/Security/CookieTokenProvider.php index bbe5ccb..b871b35 100644 --- a/src/Pecee/Http/Security/CookieTokenProvider.php +++ b/src/Pecee/Http/Security/CookieTokenProvider.php @@ -63,7 +63,7 @@ class CookieTokenProvider implements ITokenProvider public function setToken(string $token): void { $this->token = $token; - setcookie(static::CSRF_KEY, $token, time() + 60 * $this->cookieTimeoutMinutes, '/'); + setcookie(static::CSRF_KEY, $token, (int)((time() + 60) * $this->cookieTimeoutMinutes), '/', ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly')); } /** diff --git a/src/Pecee/Http/Url.php b/src/Pecee/Http/Url.php index 440e1b4..212ae6e 100644 --- a/src/Pecee/Http/Url.php +++ b/src/Pecee/Http/Url.php @@ -7,36 +7,52 @@ use Pecee\Http\Exceptions\MalformedUrlException; class Url { private $originalUrl; - private $data = [ - 'scheme' => null, - 'host' => null, - 'port' => null, - 'user' => null, - 'pass' => null, - 'path' => null, - 'query' => null, - 'fragment' => null, - ]; + + private $scheme; + private $host; + private $port; + private $username; + private $password; + private $path; + private $params; + private $fragment; /** * Url constructor. + * * @param string $url * @throws MalformedUrlException */ public function __construct(?string $url) { $this->originalUrl = $url; - if ($url !== null) { - $this->data = $this->parseUrl($url) + $this->data; - if (isset($this->data['path']) === true && $this->data['path'] !== '/') { - $this->data['path'] = rtrim($this->data['path'], '/') . '/'; + if ($url !== null && $url !== '/') { + $data = $this->parseUrl($url); + + $this->scheme = $data['scheme'] ?? null; + $this->host = $data['host'] ?? null; + $this->port = $data['port'] ?? null; + $this->username = $data['user'] ?? null; + $this->password = $data['pass'] ?? null; + + if (isset($data['path']) === true) { + $this->setPath($data['path']); + } + + $this->fragment = $data['fragment'] ?? null; + + if (isset($data['query']) === true) { + $params = []; + parse_str($data['query'], $params); + $this->setParams($params); } } } /** * Check if url is using a secure protocol like https + * * @return bool */ public function isSecure(): bool @@ -46,6 +62,7 @@ class Url /** * Checks if url is relative + * * @return bool */ public function isRelative(): bool @@ -55,38 +72,94 @@ class Url /** * Get url scheme + * * @return string|null */ public function getScheme(): ?string { - return $this->data['scheme']; + return $this->scheme; + } + + /** + * Set the scheme of the url + * + * @param string $scheme + * @return static + */ + public function setScheme(string $scheme): self + { + $this->scheme = $scheme; + + return $this; } /** * Get url host + * * @return string|null */ public function getHost(): ?string { - return $this->data['host']; + return $this->host; + } + + /** + * Set the host of the url + * + * @param string $host + * @return static + */ + public function setHost(string $host): self + { + $this->host = $host; + + return $this; } /** * Get url port + * * @return int|null */ public function getPort(): ?int { - return ($this->data['port'] !== null) ? (int)$this->data['port'] : null; + return ($this->port !== null) ? (int)$this->port : null; + } + + /** + * Set the port of the url + * + * @param int $port + * @return static + */ + public function setPort(int $port): self + { + $this->port = $port; + + return $this; } /** * Parse username from url + * * @return string|null */ - public function getUserName(): ?string + public function getUsername(): ?string { - return $this->data['user']; + return $this->username; + } + + /** + * Set the username of the url + * + * @param string $username + * @return static + */ + public function setUsername(string $username): self + { + $this->username = $username; + + return $this; } /** @@ -95,7 +168,20 @@ class Url */ public function getPassword(): ?string { - return $this->data['pass']; + return $this->password; + } + + /** + * Set the url password + * + * @param string $password + * @return static + */ + public function setPassword(string $password): self + { + $this->password = $password; + + return $this; } /** @@ -104,25 +190,87 @@ class Url */ public function getPath(): ?string { - return $this->data['path'] ?? '/'; + return $this->path ?? '/'; } /** - * Get querystring from url - * @return string|null + * Set the url path + * + * @param string $path + * @return static */ - public function getQueryString(): ?string + public function setPath(string $path): self { - return $this->data['query']; + $this->path = rtrim($path, '/') . '/'; + + return $this; + } + + /** + * Get query-string from url + * + * @return array + */ + public function getParams(): array + { + return $this->params; + } + + /** + * Merge parameters array + * + * @param array $params + * @return static + */ + public function mergeParams(array $params): self + { + return $this->setParams(array_merge($this->getParams(), $params)); + } + + /** + * Set the url params + * + * @param array $params + * @return static + */ + public function setParams(array $params): self + { + $this->params = $params; + + return $this; + } + + /** + * Get query-string params as string + * + * @return string + */ + public function getQueryString(): string + { + return static::arrayToParams($this->getParams()); } /** * Get fragment from url (everything after #) + * * @return string|null */ public function getFragment(): ?string { - return $this->data['fragment']; + return $this->fragment; + } + + /** + * Set url fragment + * + * @param string $fragment + * @return static + */ + public function setFragment(string $fragment): self + { + $this->fragment = $fragment; + + return $this; } /** @@ -133,32 +281,6 @@ class Url return $this->originalUrl; } - /** - * UTF-8 aware parse_url() replacement. - * @param string $url - * @param int $component - * @throws MalformedUrlException - * @return array - */ - public function parseUrl(string $url, int $component = -1): array - { - $encodedUrl = preg_replace_callback( - '/[^:\/@?&=#]+/u', - function ($matches) { - return urlencode($matches[0]); - }, - $url - ); - - $parts = parse_url($encodedUrl, $component); - - if ($parts === false) { - throw new MalformedUrlException('Malformed URL: ' . $url); - } - - return array_map('urldecode', $parts); - } - /** * Get position of value. * Returns -1 on failure. @@ -185,17 +307,143 @@ class Url } /** - * Returns data array with information about the url - * @return array + * Check if url contains parameter/query string. + * + * @param string $name + * @return bool */ - public function getData(): array + public function hasParam(string $name): bool { - return $this->data; + return \in_array($name, $this->getParams(), true); + } + + /** + * Removes parameter from query-string + * + * @param string $name + */ + public function removeParam(string $name): void + { + if ($this->hasParam($name) === true) { + $params = $this->getParams(); + $key = \array_search($name, $params, true); + + if ($key === true) { + unset($params[$key]); + $this->setParams($params); + } + } + } + + /** + * Get parameter by name. + * Returns parameter value or default value. + * + * @param string $name + * @param string|null $defaultValue + * @return string|null + */ + public function getParam(string $name, ?string $defaultValue = null): ?string + { + $output = null; + + if ($this->hasParam($name) === true) { + $params = $this->getParams(); + $key = \array_search($name, $params, true); + + if ($key === true) { + $output = $params[$key]; + } + } + + return $output ?? $defaultValue; + } + + /** + * UTF-8 aware parse_url() replacement. + * @param string $url + * @param int $component + * @return array + * @throws MalformedUrlException + */ + public function parseUrl(string $url, int $component = -1): array + { + $encodedUrl = preg_replace_callback( + '/[^:\/@?&=#]+/u', + function ($matches) { + return urlencode($matches[0]); + }, + $url + ); + + $parts = parse_url($encodedUrl, $component); + + if ($parts === false) { + throw new MalformedUrlException(sprintf('Failed to parse url: "%s"', $url)); + } + + return array_map('urldecode', $parts); + } + + /** + * Convert array to query-string params + * + * @param array $getParams + * @param bool $includeEmpty + * @return string + */ + public static function arrayToParams(array $getParams = [], bool $includeEmpty = true): string + { + if (\count($getParams) !== 0) { + + if ($includeEmpty === false) { + $getParams = array_filter($getParams, function ($item) { + return (trim($item) !== ''); + }); + } + + return http_build_query($getParams); + } + + return ''; + } + + /** + * Returns the relative url + * + * @return string + */ + public function getRelativeUrl(): string + { + $params = $this->getQueryString(); + + $path = $this->path ?? ''; + $query = $params !== '' ? '?' . $params : ''; + $fragment = $this->fragment !== null ? '#' . $this->fragment : ''; + + return $path . $query . $fragment; + } + + /** + * Returns the absolute url + * + * @return string + */ + public function getAbsoluteUrl(): string + { + $scheme = $this->scheme !== null ? $this->scheme . '://' : ''; + $host = $this->host ?? ''; + $port = $this->port !== null ? ':' . $this->port : ''; + $user = $this->username ?? ''; + $pass = $this->password !== null ? ':' . $this->password : ''; + $pass = ($user || $pass) ? $pass . '@' : ''; + + return $scheme . $user . $pass . $host . $port . $this->getRelativeUrl(); } public function __toString() { - return $this->getOriginalUrl(); + return $this->getRelativeUrl(); } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Event/EventArgument.php b/src/Pecee/SimpleRouter/Event/EventArgument.php new file mode 100644 index 0000000..f4aeed8 --- /dev/null +++ b/src/Pecee/SimpleRouter/Event/EventArgument.php @@ -0,0 +1,83 @@ +eventName = $eventName; + $this->router = $router; + $this->arguments = $arguments; + } + + /** + * Get event name + * + * @return string + */ + public function getEventName(): string + { + return $this->eventName; + } + + /** + * Set the event name + * + * @param string $name + */ + public function setEventName(string $name): void + { + $this->eventName = $name; + } + + /** + * Get the router instance + * + * @return Router + */ + public function getRouter(): Router + { + return $this->router; + } + + /** + * Get the request instance + * + * @return Request + */ + public function getRequest(): Request + { + return $this->getRouter()->getRequest(); + } + + /** + * Get arguments + * + * @return array + */ + public function getArguments(): array + { + return $this->arguments; + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Event/IEventArgument.php b/src/Pecee/SimpleRouter/Event/IEventArgument.php new file mode 100644 index 0000000..a8a6d7e --- /dev/null +++ b/src/Pecee/SimpleRouter/Event/IEventArgument.php @@ -0,0 +1,46 @@ +callback = function (EventArgument $argument) { + // todo: log in database + }; + } + + /** + * Get events. + * + * @param string|null $name Filter events by name. + * @return array + */ + public function getEvents(?string $name): array + { + return [ + $name => [ + $this->callback, + ], + ]; + } + + /** + * Fires any events registered with given event-name + * + * @param Router $router Router instance + * @param string $name Event name + * @param array ...$eventArgs Event arguments + */ + public function fireEvents(Router $router, string $name, ...$eventArgs): void + { + $callback = $this->callback; + $callback(new EventArgument($router, $eventArgs)); + } + + /** + * Set debug callback + * + * @param \Closure $event + */ + public function setCallback(\Closure $event): void + { + $this->callback = $event; + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Handlers/EventHandler.php b/src/Pecee/SimpleRouter/Handlers/EventHandler.php new file mode 100644 index 0000000..7b3b5af --- /dev/null +++ b/src/Pecee/SimpleRouter/Handlers/EventHandler.php @@ -0,0 +1,178 @@ +registeredEvents[$name]) === true) { + $this->registeredEvents[$name][] = $callback; + } else { + $this->registeredEvents[$name] = [$callback]; + } + + return $this; + } + + /** + * Get events. + * + * @param string|null $name Filter events by name. + * @param array ...$names Add multiple names... + * @return array + */ + public function getEvents(?string $name, ...$names): array + { + if ($name === null) { + return $this->registeredEvents; + } + + $names[] = $name; + $events = []; + + foreach ($names as $eventName) { + if(isset($this->registeredEvents[$eventName]) === true) { + $events += $this->registeredEvents[$eventName]; + } + } + + return $events; + } + + /** + * Fires any events registered with given event-name + * + * @param Router $router Router instance + * @param string $name Event name + * @param array ...$eventArgs Event arguments + */ + public function fireEvents(Router $router, string $name, ...$eventArgs): void + { + $events = $this->getEvents(static::EVENT_ALL, $name); + + /* @var $event \Closure */ + foreach ($events as $event) { + $event(new EventArgument($name, $router, $eventArgs)); + } + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Handlers/IEventHandler.php b/src/Pecee/SimpleRouter/Handlers/IEventHandler.php new file mode 100644 index 0000000..2276778 --- /dev/null +++ b/src/Pecee/SimpleRouter/Handlers/IEventHandler.php @@ -0,0 +1,26 @@ +getUrl(); diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 2f349c3..2f0d4eb 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -136,7 +136,7 @@ abstract class Route implements IRoute $parameters = []; - // Ensures that hostnames/domains will work with parameters + // Ensures that host names/domains will work with parameters $url = '/' . ltrim($url, '/'); if ((bool)preg_match_all('/' . $regex . '/u', $route, $parameters) === false) { diff --git a/src/Pecee/SimpleRouter/Route/RouteController.php b/src/Pecee/SimpleRouter/Route/RouteController.php index b15a402..e090652 100644 --- a/src/Pecee/SimpleRouter/Route/RouteController.php +++ b/src/Pecee/SimpleRouter/Route/RouteController.php @@ -49,7 +49,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string|null $name * @return string */ - public function findUrl($method = null, $parameters = null, $name = null): string + public function findUrl(?string $method = null, $parameters = null, ?string $name = null): string { if (strpos($name, '.') !== false) { $found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, false); diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index 8c88e6b..3ad9060 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -2,7 +2,7 @@ namespace Pecee\SimpleRouter\Route; -use Pecee\Handlers\IExceptionHandler; +use Pecee\SimpleRouter\Handlers\IExceptionHandler; use Pecee\Http\Request; class RouteGroup extends Route implements IGroupRoute diff --git a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php index 9656554..956c248 100644 --- a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php +++ b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php @@ -6,7 +6,14 @@ use Pecee\Http\Request; class RoutePartialGroup extends RouteGroup implements IPartialGroupRoute { - protected $urlRegex = '/^%s\/?/u'; + + /** + * RoutePartialGroup constructor. + */ + public function __construct() + { + $this->urlRegex = '/^%s\/?/u'; + } /** * Method called to check if route matches diff --git a/src/Pecee/SimpleRouter/Route/RouteResource.php b/src/Pecee/SimpleRouter/Route/RouteResource.php index 13fe2e7..2096831 100644 --- a/src/Pecee/SimpleRouter/Route/RouteResource.php +++ b/src/Pecee/SimpleRouter/Route/RouteResource.php @@ -60,7 +60,13 @@ class RouteResource extends LoadableRoute implements IControllerRoute return (strtolower($this->name) === strtolower($name)); } - public function findUrl($method = null, $parameters = null, $name = null): string + /** + * @param string|null $method + * @param array|string|null $parameters + * @param string|null $name + * @return string + */ + public function findUrl(?string $method = null, $parameters = null, ?string $name = null): string { $url = array_search($name, $this->names, false); if ($url !== false) { diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 4a96f7a..c2a0eec 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -3,7 +3,11 @@ namespace Pecee\SimpleRouter; use Pecee\Exceptions\InvalidArgumentException; -use Pecee\Handlers\IExceptionHandler; +use Pecee\Http\Exceptions\MalformedUrlException; +use Pecee\Http\Url; +use Pecee\SimpleRouter\Handlers\EventHandler; +use Pecee\SimpleRouter\Handlers\IEventHandler; +use Pecee\SimpleRouter\Handlers\IExceptionHandler; use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Request; use Pecee\SimpleRouter\Exceptions\HttpException; @@ -27,32 +31,32 @@ class Router * Defines if a route is currently being processed. * @var bool */ - protected $processingRoute; + protected $isProcessingRoute; /** * All added routes * @var array */ - protected $routes; + protected $routes = []; /** * List of processed routes * @var array */ - protected $processedRoutes; + protected $processedRoutes = []; /** * Stack of routes used to keep track of sub-routes added * when a route is being processed. * @var array */ - protected $routeStack; + protected $routeStack = []; /** * List of added bootmanagers * @var array */ - protected $bootManagers; + protected $bootManagers = []; /** * Csrf verifier class @@ -64,7 +68,7 @@ class Router * Get exception handlers * @var array */ - protected $exceptionHandlers; + protected $exceptionHandlers = []; /** * List of loaded exception that has been loaded. @@ -72,7 +76,7 @@ class Router * * @var array */ - protected $loadedExceptionHandlers; + protected $loadedExceptionHandlers = []; /** * Enable or disabled debugging @@ -92,9 +96,14 @@ class Router */ protected $debugList = []; + /** + * Contains any registered event-handler. + * @var array + */ + protected $eventHandlers = []; + /** * Router constructor. - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public function __construct() { @@ -102,18 +111,28 @@ class Router } /** - * @throws \Pecee\Http\Exceptions\MalformedUrlException + * Resets the router by reloading request and clearing all routes and data. */ public function reset(): void { - $this->processingRoute = false; - $this->request = new Request(); + $this->isProcessingRoute = false; + + try { + $this->request = new Request(); + } catch (MalformedUrlException $e) { + $this->debug(sprintf('Invalid request-uri url: %s', $e->getMessage())); + } + $this->routes = []; $this->bootManagers = []; $this->routeStack = []; $this->processedRoutes = []; $this->exceptionHandlers = []; $this->loadedExceptionHandlers = []; + $this->eventHandlers = []; + $this->debugList = []; + $this->csrfVerifier = null; + $this->debugStartTime = microtime(true); } /** @@ -127,7 +146,7 @@ class Router * If a route is currently being processed, that means that the route being added are rendered from the parent * routes callback, so we add them to the stack instead. */ - if ($this->processingRoute === true) { + if ($this->isProcessingRoute === true) { $this->routeStack[] = $route; return $route; @@ -147,9 +166,9 @@ class Router protected function renderAndProcess(IRoute $route): void { - $this->processingRoute = true; + $this->isProcessingRoute = true; $route->renderRoute($this->request, $this); - $this->processingRoute = false; + $this->isProcessingRoute = false; if (\count($this->routeStack) !== 0) { @@ -239,46 +258,78 @@ class Router { $this->debug('Loading routes'); + $this->fireEvents(EventHandler::EVENT_BOOT); + /* Initialize boot-managers */ + /* @var $manager IRouterBootManager */ foreach ($this->bootManagers as $manager) { + $this->debug('Rendering bootmanager %s', \get_class($manager)); - $manager->boot($this->request); + $this->fireEvents(EventHandler::EVENT_RENDER_BOOTMANAGER, $manager); + + /* Render bootmanager */ + $manager->boot($this, $this->request); + $this->debug('Finished rendering bootmanager'); } + $this->fireEvents(EventHandler::EVENT_LOAD_ROUTES); + /* Loop through each route-request */ $this->processRoutes($this->routes); $this->debug('Finished loading routes'); } + /** + * Start the routing + * + * @return string|null + * @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException + * @throws \Pecee\Http\Middleware\Exceptions\TokenMismatchException + * @throws HttpException + * @throws \Exception + */ + public function start(): ?string + { + $this->debug('Router starting'); + + $this->fireEvents(EventHandler::EVENT_INIT); + + $this->loadRoutes(); + + if ($this->csrfVerifier !== null) { + + $this->fireEvents(EventHandler::EVENT_RENDER_CSRF); + + /* Verify csrf token for request */ + $this->csrfVerifier->handle($this->request); + } + + $output = $this->routeRequest(); + + $this->fireEvents(EventHandler::EVENT_LOAD); + + $this->debug('Routing complete'); + + return $output; + } + /** * Routes the request * - * @param bool $rewrite * @return string|null * @throws HttpException * @throws \Exception */ - public function routeRequest(bool $rewrite = false): ?string + public function routeRequest(): ?string { - $this->debug('Started routing request (rewrite: %s)', $rewrite === true ? 'yes' : 'no'); + $this->debug('Routing request'); $methodNotAllowed = false; try { - - if ($rewrite === false) { - $this->loadRoutes(); - - if ($this->csrfVerifier !== null) { - - /* Verify csrf token for request */ - $this->csrfVerifier->handle($this->request); - } - } - $url = $this->request->getRewriteUrl() ?? $this->request->getUrl()->getPath(); /* @var $route ILoadableRoute */ @@ -289,6 +340,8 @@ class Router /* If the route matches */ if ($route->matchRoute($url, $this->request) === true) { + $this->fireEvents(EventHandler::EVENT_MATCH_ROUTE); + /* Check if request method matches */ if (\count($route->getRequestMethods()) !== 0 && \in_array($this->request->getMethod(), $route->getRequestMethods(), true) === false) { $this->debug('Method "%s" not allowed', $this->request->getMethod()); @@ -296,6 +349,8 @@ class Router continue; } + $this->fireEvents(EventHandler::EVENT_RENDER_MIDDLEWARE); + $route->loadMiddleware($this->request, $this); $output = $this->handleRouteRewrite($key, $url); @@ -303,13 +358,10 @@ class Router return $output; } - /* Render route */ $methodNotAllowed = false; $this->request->addLoadedRoute($route); - $output = $route->renderRoute($this->request, $this); - if ($output !== null) { return $output; } @@ -360,7 +412,7 @@ class Router protected function handleRouteRewrite($key, string $url): ?string { /* If the request has changed */ - if ($this->request->hasRewrite() === false) { + if ($this->request->hasPendingRewrite() === false) { return null; } @@ -373,9 +425,11 @@ class Router if ($this->request->getRewriteUrl() !== $url) { unset($this->processedRoutes[$key]); - $this->request->setHasRewrite(false); + $this->request->setHasPendingRewrite(false); - return $this->routeRequest(true); + $this->fireEvents(EventHandler::EVENT_REWRITE); + + return $this->routeRequest(); } return null; @@ -391,6 +445,8 @@ class Router { $this->debug('Starting exception handling for "%s"', \get_class($e)); + $this->fireEvents(EventHandler::EVENT_LOAD_EXCEPTIONS); + /* @var $handler IExceptionHandler */ foreach ($this->exceptionHandlers as $key => $handler) { @@ -398,6 +454,8 @@ class Router $handler = new $handler(); } + $this->fireEvents(EventHandler::EVENT_RENDER_EXCEPTION); + $this->debug('Processing exception-handler "%s"', \get_class($handler)); if (($handler instanceof IExceptionHandler) === false) { @@ -410,12 +468,14 @@ class Router $handler->handleError($this->request, $e); $this->debug('Finished rendering exception-handler'); - if (isset($this->loadedExceptionHandlers[$key]) === false && $this->request->hasRewrite() === true) { + if (isset($this->loadedExceptionHandlers[$key]) === false && $this->request->hasPendingRewrite() === true) { $this->loadedExceptionHandlers[$key] = $handler; $this->debug('Exception handler contains rewrite, reloading routes'); - return $this->routeRequest(true); + $this->fireEvents(EventHandler::EVENT_REWRITE); + + return $this->routeRequest(); } } catch (\Exception $e) { @@ -429,22 +489,6 @@ class Router throw $e; } - public function arrayToParams(array $getParams = [], bool $includeEmpty = true): string - { - if (\count($getParams) !== 0) { - - if ($includeEmpty === false) { - $getParams = array_filter($getParams, function ($item) { - return (trim($item) !== ''); - }); - } - - return '?' . http_build_query($getParams); - } - - return ''; - } - /** * Find route by alias, class, callback or method. * @@ -453,8 +497,8 @@ class Router */ public function findRoute(string $name): ?ILoadableRoute { - $this->debug('Finding route by name "%s"', $name); + $this->fireEvents(EventHandler::EVENT_FIND_ROUTE); /* @var $route ILoadableRoute */ foreach ($this->processedRoutes as $route) { @@ -485,10 +529,11 @@ class Router } /* Check if callback matches (if it's not a function) */ - if (\is_string($name) === true && \is_string($route->getCallback()) && strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && \is_callable($route->getCallback()) === false) { + $callback = $route->getCallback(); + if (\is_string($name) === true && \is_string($callback) === true && strpos($name, '@') !== false && strpos($callback, '@') !== false && \is_callable($callback) === false) { /* Check if the entire callback is matching */ - if (strpos($route->getCallback(), $name) === 0 || strtolower($route->getCallback()) === strtolower($name)) { + if (strpos($callback, $name) === 0 || strtolower($callback) === strtolower($name)) { $this->debug('Found route "%s" by callback "%s"', $route->getUrl(), $name); return $route; @@ -523,19 +568,22 @@ class Router * @param string|null $name * @param string|array|null $parameters * @param array|null $getParams + * @return Url * @throws InvalidArgumentException - * @return string + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public function getUrl(?string $name = null, $parameters = null, $getParams = null): string + public function getUrl(?string $name = null, $parameters = null, ?array $getParams = null): Url { $this->debug('Finding url', \func_get_args()); + $this->fireEvents(EventHandler::EVENT_GET_URL); + if ($getParams !== null && \is_array($getParams) === false) { throw new InvalidArgumentException('Invalid type for getParams. Must be array or null'); } if ($name === '' && $parameters === '') { - return '/'; + return new Url('/'); } /* Only merge $_GET when all parameters are null */ @@ -547,21 +595,29 @@ class Router /* Return current route if no options has been specified */ if ($name === null && $parameters === null) { - return $this->request->getUrl()->getPath() . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setParams($getParams); } $loadedRoute = $this->request->getLoadedRoute(); /* If nothing is defined and a route is loaded we use that */ if ($name === null && $loadedRoute !== null) { - return $loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name) . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setPath($loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name)) + ->setParams($getParams); } /* We try to find a match on the given name */ $route = $this->findRoute($name); if ($route !== null) { - return $route->findUrl($route->getMethod(), $parameters, $name) . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setPath($route->findUrl($route->getMethod(), $parameters, $name)) + ->setParams($getParams); } /* Using @ is most definitely a controller@method or alias@method */ @@ -575,12 +631,18 @@ class Router /* Check if the route contains the name/alias */ if ($route->hasName($controller) === true) { - return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setPath($route->findUrl($method, $parameters, $name)) + ->setParams($getParams); } /* Check if the route controller is equal to the name */ if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($controller)) { - return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setPath($route->findUrl($method, $parameters, $name)) + ->setParams($getParams); } } @@ -588,8 +650,12 @@ class Router /* No result so we assume that someone is using a hardcoded url and join everything together. */ $url = trim(implode('/', array_merge((array)$name, (array)$parameters)), '/'); + $url = (($url === '') ? '/' : '/' . $url . '/'); - return (($url === '') ? '/' : '/' . $url . '/') . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setPath($url) + ->setParams($getParams); } /** @@ -682,6 +748,47 @@ class Router return $this; } + /** + * Register event handler + * + * @param IEventHandler $handler + * @return static + */ + public function addEventHandler(IEventHandler $handler): self + { + $this->eventHandlers[] = $handler; + + return $this; + } + + /** + * Get registered event-handler. + * + * @return array + */ + public function getEventHandlers(): array + { + return $this->eventHandlers; + } + + /** + * Fire event + * + * @param string $name + * @param array ...$args + */ + protected function fireEvents($name, ...$args): void + { + if (\count($this->eventHandlers) === 0) { + return; + } + + /* @var IEventHandler $eventHandler */ + foreach ($this->eventHandlers as $eventHandler) { + $eventHandler->fireEvents($this, $name, $args); + } + } + /** * Add new debug message * @param string $message @@ -708,10 +815,6 @@ class Router */ public function setDebugEnabled(bool $boolean): void { - if ($boolean === true) { - $this->debugStartTime = microtime(true); - } - $this->debugEnabled = $boolean; } diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index ea0410f..4f495e6 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -11,11 +11,14 @@ namespace Pecee\SimpleRouter; use Pecee\Exceptions\InvalidArgumentException; -use Pecee\Handlers\CallbackExceptionHandler; +use Pecee\Http\Exceptions\MalformedUrlException; +use Pecee\Http\Url; +use Pecee\SimpleRouter\Handlers\CallbackExceptionHandler; use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Request; use Pecee\Http\Response; use Pecee\SimpleRouter\Exceptions\HttpException; +use Pecee\SimpleRouter\Handlers\IEventHandler; use Pecee\SimpleRouter\Route\IGroupRoute; use Pecee\SimpleRouter\Route\IPartialGroupRoute; use Pecee\SimpleRouter\Route\IRoute; @@ -48,20 +51,18 @@ class SimpleRouter /** * Start routing * - * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws HttpException * @throws \Exception */ public static function start(): void { - echo static::router()->routeRequest(); + echo static::router()->start(); } /** * Start the routing an return array with debugging-information * * @return array - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function startDebug(): array { @@ -94,19 +95,23 @@ class SimpleRouter } } + $request = static::request(); + $router = static::router(); + return [ - 'url' => static::request()->getUrl(), - 'method' => static::request()->getMethod(), - 'host' => static::request()->getHost(), - 'loaded_routes' => static::request()->getLoadedRoutes(), - 'all_routes' => static::router()->getRoutes(), - 'boot_managers' => static::router()->getBootManagers(), - 'csrf_verifier' => static::router()->getCsrfVerifier(), - 'log' => static::router()->getDebugLog(), + 'url' => $request->getUrl(), + 'method' => $request->getMethod(), + 'host' => $request->getHost(), + 'loaded_routes' => $request->getLoadedRoutes(), + 'all_routes' => $router->getRoutes(), + 'boot_managers' => $router->getBootManagers(), + 'csrf_verifier' => $router->getCsrfVerifier(), + 'log' => $router->getDebugLog(), + 'event_handlers' => $router->getEventHandlers(), 'router_output' => $routerOutput, 'library_version' => $version, 'php_version' => PHP_VERSION, - 'server_params' => static::request()->getHeaders(), + 'server_params' => $request->getHeaders(), ]; } @@ -124,19 +129,27 @@ class SimpleRouter * Base CSRF verifier * * @param BaseCsrfVerifier $baseCsrfVerifier - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier): void { static::router()->setCsrfVerifier($baseCsrfVerifier); } + /** + * Add new event handler to the router + * + * @param IEventHandler $eventHandler + */ + public static function addEventHandler(IEventHandler $eventHandler): void + { + static::router()->addEventHandler($eventHandler); + } + /** * Boot managers allows you to alter the routes before the routing occurs. * Perfect if you want to load pretty-urls from a file or database. * * @param IRouterBootManager $bootManager - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function addBootManager(IRouterBootManager $bootManager): void { @@ -151,7 +164,6 @@ class SimpleRouter * @param array|null $settings * * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function get(string $url, $callback, array $settings = null): IRoute { @@ -165,7 +177,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function post(string $url, $callback, array $settings = null): IRoute { @@ -179,7 +190,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function put(string $url, $callback, array $settings = null): IRoute { @@ -193,7 +203,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function patch(string $url, $callback, array $settings = null): IRoute { @@ -207,7 +216,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function options(string $url, $callback, array $settings = null): IRoute { @@ -221,7 +229,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function delete(string $url, $callback, array $settings = null): IRoute { @@ -234,7 +241,6 @@ class SimpleRouter * @param array $settings * @param \Closure $callback * @return RouteGroup - * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws InvalidArgumentException */ public static function group(array $settings = [], \Closure $callback): IGroupRoute @@ -260,7 +266,6 @@ class SimpleRouter * @param \Closure $callback * @param array $settings * @return RoutePartialGroup - * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws InvalidArgumentException */ public static function partialGroup(string $url, \Closure $callback, array $settings = []): IPartialGroupRoute @@ -288,7 +293,6 @@ class SimpleRouter * @param array|null $settings * @see SimpleRouter::form * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function basic(string $url, $callback, array $settings = null): IRoute { @@ -304,7 +308,6 @@ class SimpleRouter * @param array|null $settings * @see SimpleRouter::form * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function form(string $url, $callback, array $settings = null): IRoute { @@ -319,7 +322,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl|IRoute - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function match(array $requestMethods, string $url, $callback, array $settings = null) { @@ -343,7 +345,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl|IRoute - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function all(string $url, $callback, array $settings = null) { @@ -366,7 +367,6 @@ class SimpleRouter * @param string $controller * @param array|null $settings * @return RouteController|IRoute - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function controller(string $url, $controller, array $settings = null) { @@ -389,7 +389,6 @@ class SimpleRouter * @param string $controller * @param array|null $settings * @return RouteResource|IRoute - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function resource(string $url, $controller, array $settings = null) { @@ -410,7 +409,6 @@ class SimpleRouter * * @param \Closure $callback * @return CallbackExceptionHandler $callbackHandler - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function error(\Closure $callback): CallbackExceptionHandler { @@ -443,20 +441,28 @@ class SimpleRouter * @param string|null $name * @param string|array|null $parameters * @param array|null $getParams - * @throws \Pecee\Exceptions\InvalidArgumentException - * @return string - * @throws \Pecee\Http\Exceptions\MalformedUrlException + * @return Url */ - public static function getUrl(?string $name = null, $parameters = null, $getParams = null): string + public static function getUrl(?string $name = null, $parameters = null, $getParams = null): Url { - return static::router()->getUrl($name, $parameters, $getParams); + try { + return static::router()->getUrl($name, $parameters, $getParams); + } catch (\Exception $e) { + try { + return new Url('/'); + } catch (MalformedUrlException $e) { + + } + } + + // This will never happen... + return null; } /** * Get the request * * @return \Pecee\Http\Request - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function request(): Request { @@ -467,7 +473,6 @@ class SimpleRouter * Get the response object * * @return Response - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function response(): Response { @@ -482,7 +487,6 @@ class SimpleRouter * Returns the router instance * * @return Router - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function router(): Router { diff --git a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandler.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandler.php index 2db833f..90dfc1a 100644 --- a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandler.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandler.php @@ -1,6 +1,6 @@ setUrl('/'); + $request->setUrl(new \Pecee\Http\Url('/')); } } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php index 2360047..274bd3c 100644 --- a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php @@ -1,13 +1,13 @@ setUrl('/'); + $request->setUrl(new \Pecee\Http\Url('/')); } } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php index d295157..793746a 100644 --- a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php @@ -1,6 +1,6 @@ routes = $routes; + $this->aliasUrl = $aliasUrl; + } + + /** + * Called when router loads it's routes + * + * @param \Pecee\SimpleRouter\Router $router + * @param \Pecee\Http\Request $request + */ + public function boot(\Pecee\SimpleRouter\Router $router, \Pecee\Http\Request $request): void + { + foreach ($this->routes as $url) { + // If the current url matches the rewrite url, we use our custom route + + if ($request->getUrl()->contains($url) === true) { + $request->setRewriteUrl($this->aliasUrl); + } + + } + } +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/Dummy/Security/SilentTokenProvider.php b/tests/Pecee/SimpleRouter/Dummy/Security/SilentTokenProvider.php new file mode 100644 index 0000000..f7dfe52 --- /dev/null +++ b/tests/Pecee/SimpleRouter/Dummy/Security/SilentTokenProvider.php @@ -0,0 +1,41 @@ +refresh(); + } + + /** + * Refresh existing token + */ + public function refresh(): void + { + $this->token = uniqid('', false); + } + + /** + * Validate valid CSRF token + * + * @param string $token + * @return bool + */ + public function validate(string $token): bool + { + return ($token === $this->token); + } + + /** + * Get token token + * + * @param string|null $defaultValue + * @return string|null + */ + public function getToken(?string $defaultValue = null): ?string + { + return $this->token ?? $defaultValue; + } +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/EventHandlerTest.php b/tests/Pecee/SimpleRouter/EventHandlerTest.php new file mode 100644 index 0000000..7e49e6a --- /dev/null +++ b/tests/Pecee/SimpleRouter/EventHandlerTest.php @@ -0,0 +1,75 @@ +register(EventHandler::EVENT_ALL, function(EventArgument $arg) use(&$events) { + $key = \array_search($arg->getEventName(), $events, true); + unset($events[$key]); + }); + + TestRouter::addEventHandler($eventHandler); + + TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) { + + // Trigger rewrite + $request->setRewriteUrl('/'); + + }); + + TestRouter::get('/', 'DummyController@method1')->name('home'); + + TestRouter::router()->findRoute('home'); + TestRouter::router()->getUrl('home'); + + $csrfVerifier = new \Pecee\Http\Middleware\BaseCsrfVerifier(); + $csrfVerifier->setTokenProvider(new SilentTokenProvider()); + TestRouter::csrfVerifier($csrfVerifier); + + TestRouter::debug('/not-existing'); + + $this->assertEquals($events, []); + + TestRouter::router()->reset(); + + } + + public function testAllEvent() { + + $status = 0; + + $eventHandler = new EventHandler(); + $eventHandler->register(EventHandler::EVENT_ALL, function(EventArgument $arg) use(&$status) { + $status++; + }); + + TestRouter::addEventHandler($eventHandler); + + TestRouter::get('/', 'DummyController@method1'); + TestRouter::debug('/'); + + // All event should fire for each other event + $this->assertEquals(\count(EventHandler::$events), $status); + } + + public function testEvents() { + $this->assertEquals(true, true); + } + +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/InputHandlerTest.php b/tests/Pecee/SimpleRouter/InputHandlerTest.php new file mode 100644 index 0000000..69e36c0 --- /dev/null +++ b/tests/Pecee/SimpleRouter/InputHandlerTest.php @@ -0,0 +1,30 @@ +assertEquals(true, true); + } + + public function testPost() { + $this->assertEquals(true, true); + } + + public function testFile() { + $this->assertEquals(true, true); + } + + public function testFiles() { + $this->assertEquals(true, true); + } + + public function testAll() { + $this->assertEquals(true, true); + } + +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/RouterRouteTest.php b/tests/Pecee/SimpleRouter/RouterRouteTest.php index 63d69f9..cfdd490 100644 --- a/tests/Pecee/SimpleRouter/RouterRouteTest.php +++ b/tests/Pecee/SimpleRouter/RouterRouteTest.php @@ -93,6 +93,7 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testDomainAllowedRoute() { $this->result = false; + TestRouter::request()->setHost('hello.world.com'); TestRouter::group(['domain' => '{subdomain}.world.com'], function () { TestRouter::get('/test', function ($subdomain = null) { @@ -100,7 +101,7 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase }); }); - TestRouter::request()->setHost('hello.world.com'); + TestRouter::debug('/test', 'get'); $this->assertTrue($this->result); @@ -109,6 +110,8 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testDomainNotAllowedRoute() { + TestRouter::request()->setHost('other.world.com'); + $this->result = false; TestRouter::group(['domain' => '{subdomain}.world.com'], function () { @@ -117,9 +120,6 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase }); }); - TestRouter::request()->setHost('other.world.com'); - - TestRouter::debug('/test', 'get'); $this->assertFalse($this->result); diff --git a/tests/TestRouter.php b/tests/TestRouter.php index 60f820c..a6a5321 100644 --- a/tests/TestRouter.php +++ b/tests/TestRouter.php @@ -5,8 +5,10 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter public static function debugNoReset($testUrl, $testMethod = 'get') { - static::request()->setUrl($testUrl); - static::request()->setMethod($testMethod); + $request = static::request(); + + $request->setUrl((new \Pecee\Http\Url($testUrl))->setHost('local.unitTest')); + $request->setMethod($testMethod); static::start(); } From f5a023117aacbf069de1626a1f91bc70f3a48ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 18:51:28 +0200 Subject: [PATCH 06/25] Development - Added event-arguments data. - Added event-arguments to the event list in documentation. - Fixed missing exceptions thrown in phpDocs. - Added unit-tests for new event functionality. --- README.md | 43 +++++----- .../SimpleRouter/Event/EventArgument.php | 28 +++++++ .../Handlers/DebugEventHandler.php | 4 +- .../SimpleRouter/Handlers/EventHandler.php | 8 +- .../SimpleRouter/Handlers/IEventHandler.php | 4 +- .../SimpleRouter/Route/LoadableRoute.php | 6 +- src/Pecee/SimpleRouter/Route/Route.php | 2 +- src/Pecee/SimpleRouter/Router.php | 80 ++++++++++++++----- src/Pecee/SimpleRouter/SimpleRouter.php | 2 + tests/Pecee/SimpleRouter/EventHandlerTest.php | 40 ++++++---- tests/Pecee/SimpleRouter/InputHandlerTest.php | 15 ++-- 11 files changed, 162 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index a592726..6e6294c 100644 --- a/README.md +++ b/README.md @@ -1238,23 +1238,25 @@ It will also cover the basics of event-handlers; how to use the handlers provide This section contains all available events that can be registered using the `EventHandler`. -| Name | Description | -| ------------- |------------- | -| `EventHandler::EVENT_ALL` | Fires when a event is triggered. | -| `EventHandler::EVENT_INIT` | Fires when router is initializing and before routes are loaded. | -| `EventHandler::EVENT_LOAD` | Fires when all routes has been loaded and rendered, just before the output is returned. | -| `EventHandler::EVENT_REWRITE` | Fires when a url-rewrite is and just before the routes are re-initialized. | -| `EventHandler::EVENT_BOOT` | Fires when the router is booting. This happens just before boot-managers are rendered and before any routes has been loaded. | -| `EventHandler::EVENT_RENDER_BOOTMANAGER` | Fires before a boot-manager is rendered. | -| `EventHandler::EVENT_LOAD_ROUTES` | Fires when the router is about to load all routes. | -| `EventHandler::EVENT_FIND_ROUTE` | Fires whenever the `findRoute` method is called within the `Router`. This usually happens when the router tries to find routes that contains a certain url, usually after the `EventHandler::EVENT_GET_URL` event. | -| `EventHandler::EVENT_GET_URL` | Fires whenever the `Router::getUrl` method or `url`-helper function is called and the router tries to find the route. | -| `EventHandler::EVENT_MATCH_ROUTE` | Fires when a route is matched and valid (correct request-type etc). and before the route is rendered. | -| `EventHandler::EVENT_RENDER_ROUTE` | Fires before a route is rendered. | -| `EventHandler::EVENT_LOAD_EXCEPTIONS` | Fires when the router is loading exception-handlers. | -| `EventHandler::EVENT_RENDER_EXCEPTION` | Fires before the router is rendering a exception-handler. | -| `EventHandler::EVENT_RENDER_MIDDLEWARE` | Fires before a middleware is rendered. | -| `EventHandler::EVENT_RENDER_CSRF` | Fires before the CSRF-verifier is rendered. | +All event callbacks will retrieve a `EventArgument` object as parameter. This object contains easy access to event-name, router- and request instance and any special event-arguments related to the given event. You can see what special event arguments each event returns in the list below. + +| Name | Special arguments | Description | +| ------------- |----------- | ---- | +| `EVENT_ALL` | - | Fires when a event is triggered. | +| `EVENT_INIT` | - | Fires when router is initializing and before routes are loaded. | +| `EVENT_LOAD` | `loadedRoutes` | Fires when all routes has been loaded and rendered, just before the output is returned. | +| `EVENT_REWRITE` | `rewriteUrl`
`rewriteRoute` | Fires when a url-rewrite is and just before the routes are re-initialized. | +| `EVENT_BOOT` | `bootmanagers` | Fires when the router is booting. This happens just before boot-managers are rendered and before any routes has been loaded. | +| `EVENT_RENDER_BOOTMANAGER` | `bootmanagers`
`bootmanager` | Fires before a boot-manager is rendered. | +| `EVENT_LOAD_ROUTES` | `routes` | Fires when the router is about to load all routes. | +| `EVENT_FIND_ROUTE` | `name` | Fires whenever the `findRoute` method is called within the `Router`. This usually happens when the router tries to find routes that contains a certain url, usually after the `EventHandler::EVENT_GET_URL` event. | +| `EVENT_GET_URL` | `name`
`parameters`
`getParams` | Fires whenever the `Router::getUrl` method or `url`-helper function is called and the router tries to find the route. | +| `EVENT_MATCH_ROUTE` | `route` | Fires when a route is matched and valid (correct request-type etc). and before the route is rendered. | +| `EVENT_RENDER_ROUTE` | `route` | Fires before a route is rendered. | +| `EVENT_LOAD_EXCEPTIONS` | `exception`
`exceptionHandlers` | Fires when the router is loading exception-handlers. | +| `EVENT_RENDER_EXCEPTION` | `exception`
`exceptionHandler`
`exceptionHandlers` | Fires before the router is rendering a exception-handler. | +| `EVENT_RENDER_MIDDLEWARES` | `route`
`middlewares` | Fires before middlewares for a route is rendered. | +| `EVENT_RENDER_CSRF` | `csrfVerifier` | Fires before the CSRF-verifier is rendered. | ## Registering new event @@ -1272,9 +1274,14 @@ use Pecee\SimpleRouter\Event\EventArgument; // --- your routes goes here --- $eventHandler = new EventHandler(); + +// Add event that fires when a route is rendered $eventHandler->register(EventHandler::EVENT_RENDER_ROUTE, function(EventArgument $argument) { - // Fires when route is rendered ... + // Get the route by using the special argument for this event. + $route = $argument->route; + + // DO STUFF... }); diff --git a/src/Pecee/SimpleRouter/Event/EventArgument.php b/src/Pecee/SimpleRouter/Event/EventArgument.php index f4aeed8..3346ffe 100644 --- a/src/Pecee/SimpleRouter/Event/EventArgument.php +++ b/src/Pecee/SimpleRouter/Event/EventArgument.php @@ -70,6 +70,34 @@ class EventArgument implements IEventArgument return $this->getRouter()->getRequest(); } + /** + * @param string $name + * @return mixed + */ + public function __get($name) + { + return $this->arguments[$name] ?? null; + } + + /** + * @param string $name + * @return bool + */ + public function __isset($name) + { + return array_key_exists($name, $this->arguments); + } + + /** + * @param string $name + * @param mixed $value + * @throws \InvalidArgumentException + */ + public function __set($name, $value) + { + throw new \InvalidArgumentException('Not supported'); + } + /** * Get arguments * diff --git a/src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php b/src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php index 2aa693f..690b275 100644 --- a/src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php +++ b/src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php @@ -41,9 +41,9 @@ class DebugEventHandler implements IEventHandler * * @param Router $router Router instance * @param string $name Event name - * @param array ...$eventArgs Event arguments + * @param array $eventArgs Event arguments */ - public function fireEvents(Router $router, string $name, ...$eventArgs): void + public function fireEvents(Router $router, string $name, array $eventArgs = []): void { $callback = $this->callback; $callback(new EventArgument($router, $eventArgs)); diff --git a/src/Pecee/SimpleRouter/Handlers/EventHandler.php b/src/Pecee/SimpleRouter/Handlers/EventHandler.php index 7b3b5af..57e0727 100644 --- a/src/Pecee/SimpleRouter/Handlers/EventHandler.php +++ b/src/Pecee/SimpleRouter/Handlers/EventHandler.php @@ -80,7 +80,7 @@ class EventHandler implements IEventHandler /** * Fires before a middleware is rendered. */ - public const EVENT_RENDER_MIDDLEWARE = 'onRenderMiddleware'; + public const EVENT_RENDER_MIDDLEWARES = 'onRenderMiddlewares'; /** * Fires before the CSRF-verifier is rendered. @@ -105,7 +105,7 @@ class EventHandler implements IEventHandler self::EVENT_RENDER_ROUTE, self::EVENT_LOAD_EXCEPTIONS, self::EVENT_RENDER_EXCEPTION, - self::EVENT_RENDER_MIDDLEWARE, + self::EVENT_RENDER_MIDDLEWARES, self::EVENT_RENDER_CSRF, ]; @@ -163,9 +163,9 @@ class EventHandler implements IEventHandler * * @param Router $router Router instance * @param string $name Event name - * @param array ...$eventArgs Event arguments + * @param array $eventArgs Event arguments */ - public function fireEvents(Router $router, string $name, ...$eventArgs): void + public function fireEvents(Router $router, string $name, array $eventArgs = []): void { $events = $this->getEvents(static::EVENT_ALL, $name); diff --git a/src/Pecee/SimpleRouter/Handlers/IEventHandler.php b/src/Pecee/SimpleRouter/Handlers/IEventHandler.php index 2276778..9ae63c2 100644 --- a/src/Pecee/SimpleRouter/Handlers/IEventHandler.php +++ b/src/Pecee/SimpleRouter/Handlers/IEventHandler.php @@ -19,8 +19,8 @@ interface IEventHandler { * * @param Router $router Router instance * @param string $name Event name - * @param array ...$eventArgs Event arguments + * @param array $eventArgs Event arguments */ - public function fireEvents(Router $router, string $name, ...$eventArgs) : void; + public function fireEvents(Router $router, string $name, array $eventArgs = []) : void; } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Route/LoadableRoute.php b/src/Pecee/SimpleRouter/Route/LoadableRoute.php index 51c2591..d76cee7 100644 --- a/src/Pecee/SimpleRouter/Route/LoadableRoute.php +++ b/src/Pecee/SimpleRouter/Route/LoadableRoute.php @@ -42,9 +42,11 @@ abstract class LoadableRoute extends Route implements ILoadableRoute throw new HttpException($middleware . ' must be inherit the IMiddleware interface'); } - $router->debug('Loading middleware "%s"', \get_class($middleware)); + $className = \get_class($middleware); + + $router->debug('Loading middleware "%s"', $className); $middleware->handle($request); - $router->debug('Finished loading middleware'); + $router->debug('Finished loading middleware "%s"', $className); } $router->debug('Finished loading middlewares'); diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 2f0d4eb..0426dfb 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -82,7 +82,7 @@ abstract class Route implements IRoute */ public function renderRoute(Request $request, Router $router): ?string { - $router->debug('Starting rendering route'); + $router->debug('Starting rendering route "%s"', \get_class($this)); $callback = $this->getCallback(); diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index c2a0eec..425a44d 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -258,23 +258,31 @@ class Router { $this->debug('Loading routes'); - $this->fireEvents(EventHandler::EVENT_BOOT); + $this->fireEvents(EventHandler::EVENT_BOOT, [ + 'bootmanagers' => $this->bootManagers, + ]); /* Initialize boot-managers */ /* @var $manager IRouterBootManager */ foreach ($this->bootManagers as $manager) { - $this->debug('Rendering bootmanager %s', \get_class($manager)); - $this->fireEvents(EventHandler::EVENT_RENDER_BOOTMANAGER, $manager); + $className = \get_class($manager); + $this->debug('Rendering bootmanager "%s"', $className); + $this->fireEvents(EventHandler::EVENT_RENDER_BOOTMANAGER, [ + 'bootmanagers' => $this->bootManagers, + 'bootmanager' => $manager, + ]); /* Render bootmanager */ $manager->boot($this, $this->request); - $this->debug('Finished rendering bootmanager'); + $this->debug('Finished rendering bootmanager "%s"', $className); } - $this->fireEvents(EventHandler::EVENT_LOAD_ROUTES); + $this->fireEvents(EventHandler::EVENT_LOAD_ROUTES, [ + 'routes' => $this->routes, + ]); /* Loop through each route-request */ $this->processRoutes($this->routes); @@ -301,7 +309,9 @@ class Router if ($this->csrfVerifier !== null) { - $this->fireEvents(EventHandler::EVENT_RENDER_CSRF); + $this->fireEvents(EventHandler::EVENT_RENDER_CSRF, [ + 'csrfVerifier' => $this->csrfVerifier, + ]); /* Verify csrf token for request */ $this->csrfVerifier->handle($this->request); @@ -309,7 +319,9 @@ class Router $output = $this->routeRequest(); - $this->fireEvents(EventHandler::EVENT_LOAD); + $this->fireEvents(EventHandler::EVENT_LOAD, [ + 'loadedRoutes' => $this->getRequest()->getLoadedRoutes(), + ]); $this->debug('Routing complete'); @@ -340,7 +352,9 @@ class Router /* If the route matches */ if ($route->matchRoute($url, $this->request) === true) { - $this->fireEvents(EventHandler::EVENT_MATCH_ROUTE); + $this->fireEvents(EventHandler::EVENT_MATCH_ROUTE, [ + 'route' => $route, + ]); /* Check if request method matches */ if (\count($route->getRequestMethods()) !== 0 && \in_array($this->request->getMethod(), $route->getRequestMethods(), true) === false) { @@ -349,7 +363,10 @@ class Router continue; } - $this->fireEvents(EventHandler::EVENT_RENDER_MIDDLEWARE); + $this->fireEvents(EventHandler::EVENT_RENDER_MIDDLEWARES, [ + 'route' => $route, + 'middlewares' => $route->getMiddlewares(), + ]); $route->loadMiddleware($this->request, $this); @@ -361,6 +378,11 @@ class Router $methodNotAllowed = false; $this->request->addLoadedRoute($route); + + $this->fireEvents(EventHandler::EVENT_RENDER_ROUTE, [ + 'route' => $route, + ]); + $output = $route->renderRoute($this->request, $this); if ($output !== null) { return $output; @@ -427,7 +449,10 @@ class Router unset($this->processedRoutes[$key]); $this->request->setHasPendingRewrite(false); - $this->fireEvents(EventHandler::EVENT_REWRITE); + $this->fireEvents(EventHandler::EVENT_REWRITE, [ + 'rewriteUrl' => $this->request->getRewriteUrl(), + 'rewriteRoute' => $this->request->getRewriteRoute(), + ]); return $this->routeRequest(); } @@ -445,7 +470,10 @@ class Router { $this->debug('Starting exception handling for "%s"', \get_class($e)); - $this->fireEvents(EventHandler::EVENT_LOAD_EXCEPTIONS); + $this->fireEvents(EventHandler::EVENT_LOAD_EXCEPTIONS, [ + 'exception' => $e, + 'exceptionHandlers' => $this->exceptionHandlers, + ]); /* @var $handler IExceptionHandler */ foreach ($this->exceptionHandlers as $key => $handler) { @@ -454,7 +482,11 @@ class Router $handler = new $handler(); } - $this->fireEvents(EventHandler::EVENT_RENDER_EXCEPTION); + $this->fireEvents(EventHandler::EVENT_RENDER_EXCEPTION, [ + 'exception' => $e, + 'exceptionHandler' => $handler, + 'exceptionHandlers' => $this->exceptionHandlers, + ]); $this->debug('Processing exception-handler "%s"', \get_class($handler)); @@ -473,7 +505,10 @@ class Router $this->debug('Exception handler contains rewrite, reloading routes'); - $this->fireEvents(EventHandler::EVENT_REWRITE); + $this->fireEvents(EventHandler::EVENT_REWRITE, [ + 'rewriteUrl' => $this->request->getRewriteUrl(), + 'rewriteRoute' => $this->request->getRewriteRoute(), + ]); return $this->routeRequest(); } @@ -498,7 +533,10 @@ class Router public function findRoute(string $name): ?ILoadableRoute { $this->debug('Finding route by name "%s"', $name); - $this->fireEvents(EventHandler::EVENT_FIND_ROUTE); + + $this->fireEvents(EventHandler::EVENT_FIND_ROUTE, [ + 'name' => $name, + ]); /* @var $route ILoadableRoute */ foreach ($this->processedRoutes as $route) { @@ -576,7 +614,11 @@ class Router { $this->debug('Finding url', \func_get_args()); - $this->fireEvents(EventHandler::EVENT_GET_URL); + $this->fireEvents(EventHandler::EVENT_GET_URL, [ + 'name' => $name, + 'parameters' => $parameters, + 'getParams' => $getParams, + ]); if ($getParams !== null && \is_array($getParams) === false) { throw new InvalidArgumentException('Invalid type for getParams. Must be array or null'); @@ -772,12 +814,12 @@ class Router } /** - * Fire event + * Fire event in event-handler. * * @param string $name - * @param array ...$args + * @param array $arguments */ - protected function fireEvents($name, ...$args): void + protected function fireEvents($name, array $arguments = []): void { if (\count($this->eventHandlers) === 0) { return; @@ -785,7 +827,7 @@ class Router /* @var IEventHandler $eventHandler */ foreach ($this->eventHandlers as $eventHandler) { - $eventHandler->fireEvents($this, $name, $args); + $eventHandler->fireEvents($this, $name, $arguments); } } diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 4f495e6..8e56f2d 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -51,6 +51,8 @@ class SimpleRouter /** * Start routing * + * @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException + * @throws \Pecee\Http\Middleware\Exceptions\TokenMismatchException * @throws HttpException * @throws \Exception */ diff --git a/tests/Pecee/SimpleRouter/EventHandlerTest.php b/tests/Pecee/SimpleRouter/EventHandlerTest.php index 7e49e6a..2447c9f 100644 --- a/tests/Pecee/SimpleRouter/EventHandlerTest.php +++ b/tests/Pecee/SimpleRouter/EventHandlerTest.php @@ -4,6 +4,7 @@ require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyController.php'; require_once 'Dummy/Handler/ExceptionHandler.php'; require_once 'Dummy/Security/SilentTokenProvider.php'; +require_once 'Dummy/Managers/TestBootManager.php'; use \Pecee\SimpleRouter\Handlers\EventHandler; use \Pecee\SimpleRouter\Event\EventArgument; @@ -11,21 +12,22 @@ use \Pecee\SimpleRouter\Event\EventArgument; class EventHandlerTest extends \PHPUnit\Framework\TestCase { - public function testMissingEvents() { - + public function testAllEventTriggered() + { $events = EventHandler::$events; // Remove the all event - unset($events[\array_search(EventHandler::EVENT_ALL, $events, true)], $events[\array_search(EventHandler::EVENT_REWRITE, $events, true)]); + unset($events[\array_search(EventHandler::EVENT_ALL, $events, true)]); $eventHandler = new EventHandler(); - $eventHandler->register(EventHandler::EVENT_ALL, function(EventArgument $arg) use(&$events) { + $eventHandler->register(EventHandler::EVENT_ALL, function (EventArgument $arg) use (&$events) { $key = \array_search($arg->getEventName(), $events, true); unset($events[$key]); }); TestRouter::addEventHandler($eventHandler); + // Add rewrite TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) { // Trigger rewrite @@ -35,28 +37,36 @@ class EventHandlerTest extends \PHPUnit\Framework\TestCase TestRouter::get('/', 'DummyController@method1')->name('home'); + // Trigger findRoute TestRouter::router()->findRoute('home'); + + // Trigger getUrl TestRouter::router()->getUrl('home'); + // Add csrf-verifier $csrfVerifier = new \Pecee\Http\Middleware\BaseCsrfVerifier(); $csrfVerifier->setTokenProvider(new SilentTokenProvider()); TestRouter::csrfVerifier($csrfVerifier); - TestRouter::debug('/not-existing'); + // Add boot-manager + TestRouter::addBootManager(new TestBootManager([ + '/test', + ], '/')); + + // Start router + TestRouter::debug('/non-existing'); $this->assertEquals($events, []); - - TestRouter::router()->reset(); - } - public function testAllEvent() { + public function testAllEvent() + { - $status = 0; + $status = false; $eventHandler = new EventHandler(); - $eventHandler->register(EventHandler::EVENT_ALL, function(EventArgument $arg) use(&$status) { - $status++; + $eventHandler->register(EventHandler::EVENT_ALL, function (EventArgument $arg) use (&$status) { + $status = true; }); TestRouter::addEventHandler($eventHandler); @@ -65,11 +75,7 @@ class EventHandlerTest extends \PHPUnit\Framework\TestCase TestRouter::debug('/'); // All event should fire for each other event - $this->assertEquals(\count(EventHandler::$events), $status); - } - - public function testEvents() { - $this->assertEquals(true, true); + $this->assertEquals(true, $status); } } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/InputHandlerTest.php b/tests/Pecee/SimpleRouter/InputHandlerTest.php index 69e36c0..7cdebc3 100644 --- a/tests/Pecee/SimpleRouter/InputHandlerTest.php +++ b/tests/Pecee/SimpleRouter/InputHandlerTest.php @@ -7,23 +7,28 @@ require_once 'Dummy/Handler/ExceptionHandler.php'; class InputHandlerTest extends \PHPUnit\Framework\TestCase { - public function testGet() { + public function testGet() + { $this->assertEquals(true, true); } - public function testPost() { + public function testPost() + { $this->assertEquals(true, true); } - public function testFile() { + public function testFile() + { $this->assertEquals(true, true); } - public function testFiles() { + public function testFiles() + { $this->assertEquals(true, true); } - public function testAll() { + public function testAll() + { $this->assertEquals(true, true); } From 931b50098cc0f5f5dcc9a34a2c44cd127ddfe3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 18:54:03 +0200 Subject: [PATCH 07/25] Updated documentation --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 6e6294c..553cf8b 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,6 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind. -**Please note that this documentation is currently work-in-progress. Feel free to contribute.** - ---- - ## Table of Contents - [Getting started](#getting-started) From 4c61899560747b6ab4add3a09c64206648e3362e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 19:00:18 +0200 Subject: [PATCH 08/25] Updated documentation --- README.md | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 553cf8b..a46e868 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Getting started](#getting-started) - [Notes](#notes-1) - [Requirements](#requirements) - - [Feedback and development](#feedback-and-development) - - [Contribution development guidelines](#contribution-development-guidelines) - [Features](#features) - [Installation](#installation) - [Setting up Apache](#setting-up-apache) @@ -91,6 +89,8 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Reporting a new issue](#reporting-a-new-issue) - [Procedure for reporting a new issue](#procedure-for-reporting-a-new-issue) - [Issue template](#issue-template) + - [Feedback and development](#feedback-and-development) + - [Contribution development guidelines](#contribution-development-guidelines) - [Credits](#credits) - [Sites](#sites) @@ -99,7 +99,8 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a ___ # Getting started -Add the latest version of Simple PHP Router running this command. + +Add the latest version of the simple-router project running this command. ``` composer require pecee/simple-router @@ -129,29 +130,7 @@ You can find the demo-project here: [https://github.com/skipperbent/simple-route ## Requirements -- PHP 5.5 or greater - -### Feedback and development - -If the library is missing a feature that you need in your project or if you have feedback, we'd love to hear from you. -Feel free to leave us feedback by [creating a new issue](https://github.com/skipperbent/simple-php-router/issues/new). - -**Experiencing an issue?** - -Please refer to our [Help and support](#help-and-support) section in the documentation before reporting a new issue. - -##### Contribution development guidelines - -- Please try to follow the PSR-2 codestyle guidelines. - -- Please create your pull requests to the development base that matches the version number you want to change. -For example when pushing changes to version 3, the pull request should use the `v3-development` base/branch. - -- Create detailed descriptions for your commits, as these will be used in the changelog for new releases. - -- When changing existing functionality, please ensure that the unit-tests working. - -- When adding new stuff, please remember to add new unit-tests for the functionality. +- PHP 7.1 or greater (version 3.x and below supports PHP 5.5+) ## Features @@ -1685,6 +1664,28 @@ Remember that a more detailed issue- description and debug-info might suck to wr **Note:** please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue. Providing the necessary steps to reproduce the error within your description, adding useful debugging info etc. will help others quickly resolve the issue you are reporting. +### Feedback and development + +If the library is missing a feature that you need in your project or if you have feedback, we'd love to hear from you. +Feel free to leave us feedback by [creating a new issue](https://github.com/skipperbent/simple-php-router/issues/new). + +**Experiencing an issue?** + +Please refer to our [Help and support](#help-and-support) section in the documentation before reporting a new issue. + +##### Contribution development guidelines + +- Please try to follow the PSR-2 codestyle guidelines. + +- Please create your pull requests to the development base that matches the version number you want to change. +For example when pushing changes to version 3, the pull request should use the `v3-development` base/branch. + +- Create detailed descriptions for your commits, as these will be used in the changelog for new releases. + +- When changing existing functionality, please ensure that the unit-tests working. + +- When adding new stuff, please remember to add new unit-tests for the functionality. + --- # Credits From 1a59a659fe1dede205e67a71470c591a652ec0d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 19:02:31 +0200 Subject: [PATCH 09/25] Updated documentation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a46e868..5d48ba2 100644 --- a/README.md +++ b/README.md @@ -1664,7 +1664,7 @@ Remember that a more detailed issue- description and debug-info might suck to wr **Note:** please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue. Providing the necessary steps to reproduce the error within your description, adding useful debugging info etc. will help others quickly resolve the issue you are reporting. -### Feedback and development +## Feedback and development If the library is missing a feature that you need in your project or if you have feedback, we'd love to hear from you. Feel free to leave us feedback by [creating a new issue](https://github.com/skipperbent/simple-php-router/issues/new). @@ -1673,7 +1673,7 @@ Feel free to leave us feedback by [creating a new issue](https://github.com/skip Please refer to our [Help and support](#help-and-support) section in the documentation before reporting a new issue. -##### Contribution development guidelines +### Contribution development guidelines - Please try to follow the PSR-2 codestyle guidelines. From cca2f5cb88ac157b6e14ff7bf5c328801093e851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 19:13:55 +0200 Subject: [PATCH 10/25] Added donate option --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5d48ba2..0850b38 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ -# Simple PHP router +# simple-router -Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind. +Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. +Heavily inspired by the way Laravel handles routing, with both simplicity and expandability in mind. + +### Support the project + +If you like simple-router and wish to see the continued development and maintenance of the project, +please consider showing your support by buying me a coffee. Supporters will be listed under the credits section of this documentation. + +You can donate any amount of your choice by [clicking here](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NNX4D2RUSALCN). ## Table of Contents From af2ac6031da76240ac81729844ce24ef296a6840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 21:16:02 +0200 Subject: [PATCH 11/25] Development - Added dependency injection support. - Added php-di composer dependency. - Added `ClassLoader` class. - Added `IClassLoader` interface. - Added unit-tests for dependency injection. - Updated documentation to reflect new features. --- .gitignore | 3 +- README.md | 89 ++++++++++++- composer.json | 5 +- src/Pecee/Http/Input/InputHandler.php | 4 +- .../SimpleRouter/ClassLoader/ClassLoader.php | 118 ++++++++++++++++++ .../SimpleRouter/ClassLoader/IClassLoader.php | 12 ++ .../SimpleRouter/Handlers/EventHandler.php | 2 +- .../SimpleRouter/Handlers/IEventHandler.php | 7 +- src/Pecee/SimpleRouter/Route/IGroupRoute.php | 2 +- .../SimpleRouter/Route/LoadableRoute.php | 2 +- src/Pecee/SimpleRouter/Route/Route.php | 20 +-- src/Pecee/SimpleRouter/Route/RouteGroup.php | 2 +- src/Pecee/SimpleRouter/Router.php | 73 ++++++++--- src/Pecee/SimpleRouter/SimpleRouter.php | 20 ++- .../SimpleRouter/DependencyInjectionTest.php | 53 ++++++++ tests/Pecee/SimpleRouter/GroupTest.php | 9 +- tests/Pecee/SimpleRouter/RouterRouteTest.php | 29 +++-- 17 files changed, 382 insertions(+), 68 deletions(-) create mode 100644 src/Pecee/SimpleRouter/ClassLoader/ClassLoader.php create mode 100644 src/Pecee/SimpleRouter/ClassLoader/IClassLoader.php create mode 100644 tests/Pecee/SimpleRouter/DependencyInjectionTest.php diff --git a/.gitignore b/.gitignore index 848a58c..4d58d1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea composer.lock -vendor/ \ No newline at end of file +vendor/ +tests/tmp/* \ No newline at end of file diff --git a/README.md b/README.md index 0850b38..80c329a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # simple-router Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. -Heavily inspired by the way Laravel handles routing, with both simplicity and expandability in mind. +Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind. ### Support the project @@ -43,6 +43,9 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [Partial groups](#partial-groups) - [Form Method Spoofing](#form-method-spoofing) - [Accessing The Current Route](#accessing-the-current-route) + - [Dependency injection](#dependency-injection) + - [Enabling dependency injection](#enabling-dependency-injection) + - [More reading](#more-reading) - [Other examples](#other-examples) - [CSRF-protection](#csrf-protection) @@ -52,7 +55,7 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [Custom Token-provider](#custom-token-provider) - [Middlewares](#middlewares) - - [Example](#example) + - [Example](#example-1) - [ExceptionHandlers](#exceptionhandlers) - [Handling 404, 403 and other errors](#handling-404-403-and-other-errors) @@ -674,6 +677,88 @@ SimpleRouter::request()->getLoadedRoute(); request()->getLoadedRoute(); ``` +## Dependency injection + +simple-router supports dependency injection using the [`php-di`](http://php-di.org/) library. + +Dependency injection allows the framework to automatically "inject" (load) classes added as parameters. This can simplify your code, as you can avoid creating new instances of objects you are using often in your `Controllers` etc. + +Here's a basic example of a controller class using dependency injection: + +```php +namespace Demo\Controllers; + +class DefaultController { + + public function login(User $user): string + { + // ... + } + +} +``` + +The example above will automatically create a new instance of the `User` from the `$user` parameter. This means that the `$user` class contains a new instance of the `User` class and we won't need to create a new instance our self. + +**WARNING:** dependency injection can have some negative impact in performance. If you experience any performance issues, we recommend disabling this functionality. + +### Enabling dependency injection + +Dependency injection is disabled per default to avoid any performance issues. + +Before enabling dependency injection, we recommend that you read the [Container configuration](http://php-di.org/doc/container-configuration.html) section of the php-di documentation. This section covers how to configure php-di to different environments and speed-up the performance. + +#### Enabling for development environment + +The example below should ONLY be used on a development environment. + +```php +// Create our new php-di container +$container = (new \DI\ContainerBuilder()) + ->useAutowiring(true) + ->build(); + +// Add our container to simple-router and enable dependency injection +SimpleRouter::enableDependencyInjection($container); +``` + +Please check the [More reading](#more-reading) section of the documentation for useful php-di links and tutorials. + +#### Enabling for production environment + +The example below compiles the injections, which can help speed up performance. + +**Note:** You should change the `$cacheDir` to a cache-storage within your project. + +```php +// Cache directory +$cacheDir = sys_get_temp_dir('simple-router'); + +// Create our new php-di container +$container = (new \DI\ContainerBuilder()) + ->enableCompilation($cacheDir) + ->writeProxiesToFile(true, $cacheDir . '/proxies') + ->useAutowiring(true) + ->build(); + +// Add our container to simple-router and enable dependency injection +SimpleRouter::enableDependencyInjection($container); +``` + +Please check the [More reading](#more-reading) section of the documentation for useful php-di links and tutorials. + +### More reading + +For more information about dependency injection, configuration and settings - we recommend that you check the php-di documentation or some of the useful links we've gathered below. + +#### Useful links + +- [php-di documentation](http://php-di.org/doc/) +- [Understanding dependency injection](http://php-di.org/doc/understanding-di.html) +- [Best practices guide](http://php-di.org/doc/best-practices.html) +- [Configuring the container](http://php-di.org/doc/container-configuration.html) +- [Definitions](http://php-di.org/doc/definition.html) + ## Other examples You can find many more examples in the `routes.php` example-file below: diff --git a/composer.json b/composer.json index 9c88dd6..d0af205 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,8 @@ } ], "require": { - "php": ">=7.1" + "php": ">=7.1", + "php-di/php-di": "^6.0" }, "require-dev": { "phpunit/phpunit": "^6.0", @@ -38,4 +39,4 @@ "Pecee\\": "src/Pecee/" } } -} \ No newline at end of file +} diff --git a/src/Pecee/Http/Input/InputHandler.php b/src/Pecee/Http/Input/InputHandler.php index 1f1d0e1..9b15a2a 100644 --- a/src/Pecee/Http/Input/InputHandler.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -214,7 +214,7 @@ class InputHandler * @param array ...$methods * @return IInputItem|null */ - public function get(string $index, ...$methods) : ?IInputItem + public function get(string $index, ...$methods): ?IInputItem { $element = null; @@ -241,7 +241,7 @@ class InputHandler * @param array ...$methods * @return string */ - public function getValue(string $index, ?string $defaultValue = null, ...$methods) : ?string + public function getValue(string $index, ?string $defaultValue = null, ...$methods): ?string { $input = $this->get($index, $methods); diff --git a/src/Pecee/SimpleRouter/ClassLoader/ClassLoader.php b/src/Pecee/SimpleRouter/ClassLoader/ClassLoader.php new file mode 100644 index 0000000..caea1a5 --- /dev/null +++ b/src/Pecee/SimpleRouter/ClassLoader/ClassLoader.php @@ -0,0 +1,118 @@ +useDependencyInjection === true) { + $container = $this->getContainer(); + if ($container !== null) { + try { + return $container->get($class); + } catch (\Exception $e) { + throw new NotFoundHttpException($e->getMessage(), (int)$e->getCode(), $e->getPrevious()); + } + } + } + + return new $class(); + } + + /** + * Load closure + * + * @param \Closure $closure + * @param array $parameters + * @return mixed + * @throws NotFoundHttpException + */ + public function loadClosure(\Closure $closure, array $parameters) + { + if ($this->useDependencyInjection === true) { + $container = $this->getContainer(); + if ($container !== null) { + try { + return $container->call($closure, $parameters); + } catch (\Exception $e) { + throw new NotFoundHttpException($e->getMessage(), (int)$e->getCode(), $e->getPrevious()); + } + } + } + + return \call_user_func_array($closure, $parameters); + } + + /** + * Get dependency injector container. + * + * @return Container|null + */ + public function getContainer(): ?Container + { + return $this->container; + } + + /** + * Set the dependency-injector container. + * + * @param Container $container + * @return ClassLoader + */ + public function setContainer(Container $container): self + { + $this->container = $container; + + return $this; + } + + /** + * Enable or disable dependency injection. + * + * @param bool $enabled + * @return static + */ + public function useDependencyInjection(bool $enabled): self + { + $this->useDependencyInjection = $enabled; + + return $this; + } + + /** + * Return true if dependency injection is enabled. + * + * @return bool + */ + public function isDependencyInjectionEnabled(): bool + { + return $this->useDependencyInjection; + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/ClassLoader/IClassLoader.php b/src/Pecee/SimpleRouter/ClassLoader/IClassLoader.php new file mode 100644 index 0000000..d978ac1 --- /dev/null +++ b/src/Pecee/SimpleRouter/ClassLoader/IClassLoader.php @@ -0,0 +1,12 @@ +registeredEvents[$eventName]) === true) { + if (isset($this->registeredEvents[$eventName]) === true) { $events += $this->registeredEvents[$eventName]; } } diff --git a/src/Pecee/SimpleRouter/Handlers/IEventHandler.php b/src/Pecee/SimpleRouter/Handlers/IEventHandler.php index 9ae63c2..bf4e49a 100644 --- a/src/Pecee/SimpleRouter/Handlers/IEventHandler.php +++ b/src/Pecee/SimpleRouter/Handlers/IEventHandler.php @@ -4,7 +4,8 @@ namespace Pecee\SimpleRouter\Handlers; use Pecee\SimpleRouter\Router; -interface IEventHandler { +interface IEventHandler +{ /** * Get events. @@ -12,7 +13,7 @@ interface IEventHandler { * @param string|null $name Filter events by name. * @return array */ - public function getEvents(?string $name) : array; + public function getEvents(?string $name): array; /** * Fires any events registered with given event-name @@ -21,6 +22,6 @@ interface IEventHandler { * @param string $name Event name * @param array $eventArgs Event arguments */ - public function fireEvents(Router $router, string $name, array $eventArgs = []) : void; + public function fireEvents(Router $router, string $name, array $eventArgs = []): void; } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Route/IGroupRoute.php b/src/Pecee/SimpleRouter/Route/IGroupRoute.php index a1ebd33..2c18d1d 100644 --- a/src/Pecee/SimpleRouter/Route/IGroupRoute.php +++ b/src/Pecee/SimpleRouter/Route/IGroupRoute.php @@ -2,8 +2,8 @@ namespace Pecee\SimpleRouter\Route; -use Pecee\SimpleRouter\Handlers\IExceptionHandler; use Pecee\Http\Request; +use Pecee\SimpleRouter\Handlers\IExceptionHandler; interface IGroupRoute extends IRoute { diff --git a/src/Pecee/SimpleRouter/Route/LoadableRoute.php b/src/Pecee/SimpleRouter/Route/LoadableRoute.php index d76cee7..9fc4cb3 100644 --- a/src/Pecee/SimpleRouter/Route/LoadableRoute.php +++ b/src/Pecee/SimpleRouter/Route/LoadableRoute.php @@ -35,7 +35,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute foreach ($this->getMiddlewares() as $middleware) { if (\is_object($middleware) === false) { - $middleware = $this->loadClass($middleware); + $middleware = $router->getClassLoader()->loadClass($middleware); } if (($middleware instanceof IMiddleware) === false) { diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 0426dfb..ad56adc 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -57,21 +57,6 @@ abstract class Route implements IRoute protected $originalParameters = []; protected $middlewares = []; - /** - * Load class by name - * @param string $name - * @return mixed - * @throws NotFoundHttpException - */ - protected function loadClass($name) - { - if (class_exists($name) === false) { - throw new NotFoundHttpException(sprintf('Class "%s" does not exist', $name), 404); - } - - return new $name(); - } - /** * Render route * @@ -107,7 +92,7 @@ abstract class Route implements IRoute /* When the callback is a function */ - return \call_user_func_array($callback, $parameters); + return $router->getClassLoader()->loadClosure($callback, $parameters); } /* When the callback is a class + method */ @@ -118,7 +103,8 @@ abstract class Route implements IRoute $className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0]; $router->debug('Loading class %s', $className); - $class = $this->loadClass($className); + $class = $router->getClassLoader()->loadClass($className); + $method = $controller[1]; if (method_exists($class, $method) === false) { diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index 3ad9060..4e72215 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -2,8 +2,8 @@ namespace Pecee\SimpleRouter\Route; -use Pecee\SimpleRouter\Handlers\IExceptionHandler; use Pecee\Http\Request; +use Pecee\SimpleRouter\Handlers\IExceptionHandler; class RouteGroup extends Route implements IGroupRoute { diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 425a44d..05d9908 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -4,14 +4,16 @@ namespace Pecee\SimpleRouter; use Pecee\Exceptions\InvalidArgumentException; use Pecee\Http\Exceptions\MalformedUrlException; +use Pecee\Http\Middleware\BaseCsrfVerifier; +use Pecee\Http\Request; use Pecee\Http\Url; +use Pecee\SimpleRouter\ClassLoader\ClassLoader; +use Pecee\SimpleRouter\ClassLoader\IClassLoader; +use Pecee\SimpleRouter\Exceptions\HttpException; +use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; use Pecee\SimpleRouter\Handlers\EventHandler; use Pecee\SimpleRouter\Handlers\IEventHandler; use Pecee\SimpleRouter\Handlers\IExceptionHandler; -use Pecee\Http\Middleware\BaseCsrfVerifier; -use Pecee\Http\Request; -use Pecee\SimpleRouter\Exceptions\HttpException; -use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; use Pecee\SimpleRouter\Route\IControllerRoute; use Pecee\SimpleRouter\Route\IGroupRoute; use Pecee\SimpleRouter\Route\ILoadableRoute; @@ -102,6 +104,12 @@ class Router */ protected $eventHandlers = []; + /** + * Class loader instance + * @var ClassLoader + */ + protected $classLoader; + /** * Router constructor. */ @@ -115,6 +123,7 @@ class Router */ public function reset(): void { + $this->debugStartTime = microtime(true); $this->isProcessingRoute = false; try { @@ -132,7 +141,7 @@ class Router $this->eventHandlers = []; $this->debugList = []; $this->csrfVerifier = null; - $this->debugStartTime = microtime(true); + $this->classLoader = new ClassLoader(); } /** @@ -271,7 +280,7 @@ class Router $this->debug('Rendering bootmanager "%s"', $className); $this->fireEvents(EventHandler::EVENT_RENDER_BOOTMANAGER, [ 'bootmanagers' => $this->bootManagers, - 'bootmanager' => $manager, + 'bootmanager' => $manager, ]); /* Render bootmanager */ @@ -364,7 +373,7 @@ class Router } $this->fireEvents(EventHandler::EVENT_RENDER_MIDDLEWARES, [ - 'route' => $route, + 'route' => $route, 'middlewares' => $route->getMiddlewares(), ]); @@ -483,8 +492,8 @@ class Router } $this->fireEvents(EventHandler::EVENT_RENDER_EXCEPTION, [ - 'exception' => $e, - 'exceptionHandler' => $handler, + 'exception' => $e, + 'exceptionHandler' => $handler, 'exceptionHandlers' => $this->exceptionHandlers, ]); @@ -711,20 +720,28 @@ class Router /** * Set BootManagers + * * @param array $bootManagers + * @return static */ - public function setBootManagers(array $bootManagers): void + public function setBootManagers(array $bootManagers): self { $this->bootManagers = $bootManagers; + + return $this; } /** * Add BootManager + * * @param IRouterBootManager $bootManager + * @return static */ - public function addBootManager(IRouterBootManager $bootManager): void + public function addBootManager(IRouterBootManager $bootManager): self { $this->bootManagers[] = $bootManager; + + return $this; } /** @@ -783,13 +800,36 @@ class Router * @param BaseCsrfVerifier $csrfVerifier * @return static */ - public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier) + public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier): self { $this->csrfVerifier = $csrfVerifier; return $this; } + /** + * Set class loader + * + * @param IClassLoader $loader + * @return static + */ + public function setClassLoader(IClassLoader $loader) + { + $this->classLoader = $loader; + + return $this; + } + + /** + * Get class loader + * + * @return ClassLoader + */ + public function getClassLoader(): IClassLoader + { + return $this->classLoader; + } + /** * Register event handler * @@ -853,11 +893,14 @@ class Router /** * Enable or disables debugging * - * @param bool $boolean + * @param bool $enabled + * @return static */ - public function setDebugEnabled(bool $boolean): void + public function setDebugEnabled(bool $enabled): self { - $this->debugEnabled = $boolean; + $this->debugEnabled = $enabled; + + return $this; } /** diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 8e56f2d..3209812 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -10,14 +10,16 @@ namespace Pecee\SimpleRouter; +use DI\Container; use Pecee\Exceptions\InvalidArgumentException; use Pecee\Http\Exceptions\MalformedUrlException; -use Pecee\Http\Url; -use Pecee\SimpleRouter\Handlers\CallbackExceptionHandler; use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Request; use Pecee\Http\Response; +use Pecee\Http\Url; +use Pecee\SimpleRouter\ClassLoader\IClassLoader; use Pecee\SimpleRouter\Exceptions\HttpException; +use Pecee\SimpleRouter\Handlers\CallbackExceptionHandler; use Pecee\SimpleRouter\Handlers\IEventHandler; use Pecee\SimpleRouter\Route\IGroupRoute; use Pecee\SimpleRouter\Route\IPartialGroupRoute; @@ -530,6 +532,20 @@ class SimpleRouter return $route; } + /** + * Enable or disable dependency injection + * + * @param Container $container + * @return IClassLoader + */ + public static function enableDependencyInjection(Container $container): IClassLoader + { + return static::router() + ->getClassLoader() + ->useDependencyInjection(true) + ->setContainer($container); + } + /** * Get default namespace * @return string|null diff --git a/tests/Pecee/SimpleRouter/DependencyInjectionTest.php b/tests/Pecee/SimpleRouter/DependencyInjectionTest.php new file mode 100644 index 0000000..8863f19 --- /dev/null +++ b/tests/Pecee/SimpleRouter/DependencyInjectionTest.php @@ -0,0 +1,53 @@ +useAutowiring(true) + ->ignorePhpDocErrors(true) + ->build(); + + TestRouter::enableDependencyInjection($container); + + $className = null; + + TestRouter::get('/', function (DummyMiddleware $url) use (&$className) { + $className = \get_class($url); + }); + + TestRouter::debug('/'); + + $this->assertEquals(DummyMiddleware::class, $className); + } + + public function testDependencyInjectionProduction() + { + $cacheDir = dirname(__DIR__, 2) . '/tmp'; + + $builder = new \DI\ContainerBuilder(); + $builder + ->enableCompilation($cacheDir) + ->writeProxiesToFile(true, $cacheDir . '/proxies') + ->ignorePhpDocErrors(true) + ->useAutowiring(true); + + $container = $builder->build(); + + TestRouter::enableDependencyInjection($container); + + $className = null; + + TestRouter::get('/', function (DummyMiddleware $url) use (&$className) { + $className = \get_class($url); + }); + + TestRouter::debug('/'); + + $this->assertEquals(DummyMiddleware::class, $className); + } +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/GroupTest.php b/tests/Pecee/SimpleRouter/GroupTest.php index c059253..ce79b26 100644 --- a/tests/Pecee/SimpleRouter/GroupTest.php +++ b/tests/Pecee/SimpleRouter/GroupTest.php @@ -5,14 +5,13 @@ require_once 'Dummy/DummyController.php'; class GroupTest extends \PHPUnit\Framework\TestCase { - protected $result; public function testGroupLoad() { - $this->result = false; + $result = false; - TestRouter::group(['prefix' => '/group'], function () { - $this->result = true; + TestRouter::group(['prefix' => '/group'], function () use(&$result) { + $result = true; }); try { @@ -20,7 +19,7 @@ class GroupTest extends \PHPUnit\Framework\TestCase } catch(\Exception $e) { } - $this->assertTrue($this->result); + $this->assertTrue($result); } public function testNestedGroup() diff --git a/tests/Pecee/SimpleRouter/RouterRouteTest.php b/tests/Pecee/SimpleRouter/RouterRouteTest.php index cfdd490..8e3caea 100644 --- a/tests/Pecee/SimpleRouter/RouterRouteTest.php +++ b/tests/Pecee/SimpleRouter/RouterRouteTest.php @@ -6,21 +6,20 @@ require_once 'Dummy/Exception/ExceptionHandlerException.php'; class RouterRouteTest extends \PHPUnit\Framework\TestCase { - protected $result = false; - public function testMultiParam() { - TestRouter::get('/test-{param1}-{param2}', function ($param1, $param2) { + $result = false; + TestRouter::get('/test-{param1}-{param2}', function ($param1, $param2) use(&$result) { if ($param1 === 'param1' && $param2 === 'param2') { - $this->result = true; + $result = true; } }); TestRouter::debug('/test-param1-param2', 'get'); - $this->assertTrue($this->result); + $this->assertTrue($result); } @@ -92,19 +91,19 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testDomainAllowedRoute() { - $this->result = false; + $result = false; TestRouter::request()->setHost('hello.world.com'); - TestRouter::group(['domain' => '{subdomain}.world.com'], function () { - TestRouter::get('/test', function ($subdomain = null) { - $this->result = ($subdomain === 'hello'); + TestRouter::group(['domain' => '{subdomain}.world.com'], function () use(&$result) { + TestRouter::get('/test', function ($subdomain = null) use(&$result) { + $result = ($subdomain === 'hello'); }); }); TestRouter::debug('/test', 'get'); - $this->assertTrue($this->result); + $this->assertTrue($result); } @@ -112,17 +111,17 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase { TestRouter::request()->setHost('other.world.com'); - $this->result = false; + $result = false; - TestRouter::group(['domain' => '{subdomain}.world.com'], function () { - TestRouter::get('/test', function ($subdomain = null) { - $this->result = ($subdomain === 'hello'); + TestRouter::group(['domain' => '{subdomain}.world.com'], function () use(&$result) { + TestRouter::get('/test', function ($subdomain = null) use(&$result) { + $result = ($subdomain === 'hello'); }); }); TestRouter::debug('/test', 'get'); - $this->assertFalse($this->result); + $this->assertFalse($result); } From 8eded4a619dcb79c3fe2e4e8cda692d2c4c44d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 21:19:52 +0200 Subject: [PATCH 12/25] Updated documentation. --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index 80c329a..18ec07d 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [Setting up IIS](#setting-up-iis) - [Configuration](#configuration) - [Helper functions](#helper-functions) - - [Routes](#routes) - [Basic routing](#basic-routing) - [Available methods](#available-methods) @@ -47,20 +46,16 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [Enabling dependency injection](#enabling-dependency-injection) - [More reading](#more-reading) - [Other examples](#other-examples) - - [CSRF-protection](#csrf-protection) - [Adding CSRF-verifier](#adding-csrf-verifier) - [Getting CSRF-token](#getting-csrf-token) - [Custom CSRF-verifier](#custom-csrf-verifier) - [Custom Token-provider](#custom-token-provider) - - [Middlewares](#middlewares) - [Example](#example-1) - - [ExceptionHandlers](#exceptionhandlers) - [Handling 404, 403 and other errors](#handling-404-403-and-other-errors) - [Using custom exception handlers](#using-custom-exception-handlers) - - [Urls](#urls) - [Get the current url](#get-the-current-url) - [Get by name (single route)](#get-by-name-single-route) @@ -70,19 +65,16 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [Getting REST/resource controller urls](#getting-restresource-controller-urls) - [Manipulating url](#manipulating-url) - [Useful url tricks](#useful-url-tricks) - - [Input & parameters](#input--parameters) - [Using the Input class to manage parameters](#using-the-input-class-to-manage-parameters) - [Get single parameter value](#get-single-parameter-value) - [Get parameter object](#get-parameter-object) - [Managing files](#managing-files) - [Get all parameters](#get-all-parameters) - - [Events](#events) - [Available events](#available-events) - [Registering new event](#registering-new-event) - [Custom EventHandlers](#custom-eventhandlers) - - [Advanced](#advanced) - [Url rewriting](#url-rewriting) - [Changing current route](#changing-current-route) @@ -90,7 +82,6 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [Adding routes manually](#adding-routes-manually) - [Parameters](#parameters) - [Extending](#extending) - - [Help and support](#help-and-support) - [Common issues and fixes](#common-issues-and-fixes) - [Debugging](#debugging) @@ -102,7 +93,6 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [Issue template](#issue-template) - [Feedback and development](#feedback-and-development) - [Contribution development guidelines](#contribution-development-guidelines) - - [Credits](#credits) - [Sites](#sites) - [License](#license) From e6db83c97a865b74c12720d11ff6595988e9d4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 21:44:41 +0200 Subject: [PATCH 13/25] Fixed defaultValue issue in `getValue` method in `InputHandler` class. --- src/Pecee/Http/Input/InputHandler.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Pecee/Http/Input/InputHandler.php b/src/Pecee/Http/Input/InputHandler.php index 9b15a2a..051a187 100644 --- a/src/Pecee/Http/Input/InputHandler.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -244,12 +244,7 @@ class InputHandler public function getValue(string $index, ?string $defaultValue = null, ...$methods): ?string { $input = $this->get($index, $methods); - - if ($input !== null) { - return (trim($input->getValue()) === '') ? $defaultValue : $input->getValue(); - } - - return $input; + return ($input === null || ($input !== null && trim($input->getValue()) === '')) ? $defaultValue : $input->getValue(); } /** From a11595fb866df308441b381c0bc86645c32631e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 22:17:53 +0200 Subject: [PATCH 14/25] Bugfixes - Fixed `$methods` argument not properly passed in `InputHandler` class. - Updated helpers.php with latest changes. --- README.md | 11 +++-------- helpers.php | 11 +++-------- src/Pecee/Http/Input/InputHandler.php | 2 +- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 18ec07d..de1bf4e 100644 --- a/README.md +++ b/README.md @@ -331,18 +331,13 @@ function request(): Request * Get input class * @param string|null $index Parameter index name * @param string|null $defaultValue Default return value - * @param string|array|null $methods Default method + * @param array ...$methods Default methods * @return \Pecee\Http\Input\InputHandler|\Pecee\Http\Input\IInputItem|string */ -function input($index = null, $defaultValue = null, $methods = null) +function input($index = null, $defaultValue = null, ...$methods) { if ($index !== null) { - - if ($defaultValue !== null) { - return request()->getInputHandler()->getValue($index, $defaultValue, $methods); - } - - return request()->getInputHandler()->get($index, $methods); + return request()->getInputHandler()->get($index, ...$methods) ?? $defaultValue; } return request()->getInputHandler(); diff --git a/helpers.php b/helpers.php index f7234a3..914c445 100644 --- a/helpers.php +++ b/helpers.php @@ -48,18 +48,13 @@ function request(): Request * Get input class * @param string|null $index Parameter index name * @param string|null $defaultValue Default return value - * @param string|array|null $methods Default method + * @param array ...$methods Default methods * @return \Pecee\Http\Input\InputHandler|\Pecee\Http\Input\IInputItem|string */ -function input($index = null, $defaultValue = null, $methods = null) +function input($index = null, $defaultValue = null, ...$methods) { if ($index !== null) { - - if ($defaultValue !== null) { - return request()->getInputHandler()->getValue($index, $defaultValue, $methods); - } - - return request()->getInputHandler()->get($index, $methods); + return request()->getInputHandler()->get($index, ...$methods) ?? $defaultValue; } return request()->getInputHandler(); diff --git a/src/Pecee/Http/Input/InputHandler.php b/src/Pecee/Http/Input/InputHandler.php index 051a187..6275c5d 100644 --- a/src/Pecee/Http/Input/InputHandler.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -243,7 +243,7 @@ class InputHandler */ public function getValue(string $index, ?string $defaultValue = null, ...$methods): ?string { - $input = $this->get($index, $methods); + $input = $this->get($index, ...$methods); return ($input === null || ($input !== null && trim($input->getValue()) === '')) ? $defaultValue : $input->getValue(); } From a1dc4c5119d1393e3005dd25c271f69631f2b727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 22:35:21 +0200 Subject: [PATCH 15/25] Bugfixes - Updated `input` helper function. - Update documentation to reflect v4 changes in `InputHandler` class. --- README.md | 24 +++++++++++------------- helpers.php | 8 ++++---- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index de1bf4e..0592ed9 100644 --- a/README.md +++ b/README.md @@ -284,9 +284,9 @@ To implement the functions below, simply copy the code to a new file and require getInputHandler()->get($index, ...$methods) ?? $defaultValue; + return request()->getInputHandler()->getValue($index, $defaultValue, ...$methods); } return request()->getInputHandler(); @@ -1172,18 +1172,16 @@ $value = input($index, $defaultValue, $methods); ### Get parameter object -Will return an instance of `InputItem` or `InputFile` depending on the type. +The example below will return an instance of `InputItem` or `InputFile` depending on the type. You can use this in your html as it will render the value of the item. -However if you want to compare value in your if statements, you have to use -the `getValue` or use the `input()` instead. + +If you want to compare value in your if statements, you have to use the `getValue` or use the `input()` helper function instead. If items is grouped in the html, it will return an array of items. -**Note:** `getObject` will only return `$defaultValue` if the item doesn't exist. If you want `$defaultValue` to be returned if the item is empty, please use `input()` instead. - ```php -$object = input()->getObject($index, $defaultValue = null, $methods = null); +$object = input()->get($index, $defaultValue = null, $methods = null); ``` ### Return specific GET parameter (where name is the name of your parameter): @@ -1205,9 +1203,9 @@ $id = input($index, $defaultValue, $method); # -- match specific -- -$object = input($index, $defaultValue, 'get'); -$object = input($index, $defaultValue, 'post'); -$object = input($index, $defaultValue, 'file'); +$value = input($index, $defaultValue, 'get'); +$value = input($index, $defaultValue, 'post'); +$value = input($index, $defaultValue, 'file'); # -- or -- diff --git a/helpers.php b/helpers.php index 914c445..264df05 100644 --- a/helpers.php +++ b/helpers.php @@ -1,9 +1,9 @@ getInputHandler()->get($index, ...$methods) ?? $defaultValue; + return request()->getInputHandler()->getValue($index, $defaultValue, ...$methods); } return request()->getInputHandler(); From da219d0b19e17dfe5ef256a69ef8f0517e8e664b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 23:13:56 +0200 Subject: [PATCH 16/25] Bugfixes - Fixed rewrite from ExceptionHandler sometimes not working correctly. - Fixed default-namespace for Group and partial groups. --- src/Pecee/SimpleRouter/Router.php | 5 +++++ src/Pecee/SimpleRouter/SimpleRouter.php | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 05d9908..28ad270 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -510,6 +510,7 @@ class Router $this->debug('Finished rendering exception-handler'); if (isset($this->loadedExceptionHandlers[$key]) === false && $this->request->hasPendingRewrite() === true) { + $this->loadedExceptionHandlers[$key] = $handler; $this->debug('Exception handler contains rewrite, reloading routes'); @@ -519,6 +520,10 @@ class Router 'rewriteRoute' => $this->request->getRewriteRoute(), ]); + if ($this->request->getRewriteRoute() !== null) { + $this->processedRoutes[] = $this->request->getRewriteRoute(); + } + return $this->routeRequest(); } diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 3209812..2e4436a 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -256,6 +256,7 @@ class SimpleRouter $group = new RouteGroup(); $group->setCallback($callback); $group->setSettings($settings); + $group = static::addDefaultNamespace($group); static::router()->addRoute($group); @@ -283,6 +284,7 @@ class SimpleRouter $group = new RoutePartialGroup(); $group->setSettings($settings); $group->setCallback($callback); + $group = static::addDefaultNamespace($group); static::router()->addRoute($group); @@ -511,6 +513,12 @@ class SimpleRouter { if (static::$defaultNamespace !== null) { + if ($route instanceof IGroupRoute) { + $route->setNamespace(static::$defaultNamespace . '\\' . ltrim($route->getNamespace(), '\\')); + + return $route; + } + $callback = $route->getCallback(); /* Only add default namespace on relative callbacks */ From 17471a53cd482bd72b6a7c4db7ae2e680fb2ac2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 23:28:03 +0200 Subject: [PATCH 17/25] Removed namespace from groups --- src/Pecee/SimpleRouter/SimpleRouter.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 2e4436a..3209812 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -256,7 +256,6 @@ class SimpleRouter $group = new RouteGroup(); $group->setCallback($callback); $group->setSettings($settings); - $group = static::addDefaultNamespace($group); static::router()->addRoute($group); @@ -284,7 +283,6 @@ class SimpleRouter $group = new RoutePartialGroup(); $group->setSettings($settings); $group->setCallback($callback); - $group = static::addDefaultNamespace($group); static::router()->addRoute($group); @@ -513,12 +511,6 @@ class SimpleRouter { if (static::$defaultNamespace !== null) { - if ($route instanceof IGroupRoute) { - $route->setNamespace(static::$defaultNamespace . '\\' . ltrim($route->getNamespace(), '\\')); - - return $route; - } - $callback = $route->getCallback(); /* Only add default namespace on relative callbacks */ From 80a42030ea059a9c92eedb43a09b54c2f57038f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Thu, 29 Mar 2018 23:47:27 +0200 Subject: [PATCH 18/25] Bugfixes - Fixed getting specific input-value by request-method in InputHandler. - Added .idea files --- .gitignore | 1 - .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/dictionaries/simon.xml | 12 + .idea/markdown-navigator.xml | 72 ++ .../markdown-navigator/profiles_settings.xml | 3 + .idea/modules.xml | 8 + .idea/php-test-framework.xml | 14 + .idea/php.xml | 52 + .idea/simple-php-router.iml | 49 + .idea/vcs.xml | 6 + .idea/workspace.xml | 998 ++++++++++++++++++ src/Pecee/Http/Input/InputHandler.php | 4 +- 12 files changed, 1221 insertions(+), 3 deletions(-) create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/dictionaries/simon.xml create mode 100644 .idea/markdown-navigator.xml create mode 100644 .idea/markdown-navigator/profiles_settings.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/php-test-framework.xml create mode 100644 .idea/php.xml create mode 100644 .idea/simple-php-router.iml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml diff --git a/.gitignore b/.gitignore index 4d58d1e..f3bdaaa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.idea composer.lock vendor/ tests/tmp/* \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..91dfc80 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dictionaries/simon.xml b/.idea/dictionaries/simon.xml new file mode 100644 index 0000000..87f7ef2 --- /dev/null +++ b/.idea/dictionaries/simon.xml @@ -0,0 +1,12 @@ + + + + bootmanager + bootmanagers + csrf + middlewares + pecee + urldecode + + + \ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml new file mode 100644 index 0000000..e41dd85 --- /dev/null +++ b/.idea/markdown-navigator.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml new file mode 100644 index 0000000..57927c5 --- /dev/null +++ b/.idea/markdown-navigator/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a5ba0c2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml new file mode 100644 index 0000000..82ec95c --- /dev/null +++ b/.idea/php-test-framework.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..db05e2b --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/simple-php-router.iml b/.idea/simple-php-router.iml new file mode 100644 index 0000000..2c4d89b --- /dev/null +++ b/.idea/simple-php-router.iml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..f4f4678 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,998 @@ + + + + + + + + + + $PROJECT_DIR$/composer.json + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EVENT_RENDER_ROUTE + function renderRoute + ->renderRoute + fireEvent + renderRoute + findRoute + ->fireEvents + RENDER_MIDD + EVENT_RENDER_CSRF + RENDER_ROUTE + requirements + # Advan + # Feed + : void + call_user_func_array + loadClass + $this-> + $this->result + $this->r + getInput + getValue + helpers.php + input()->get + getObject + helpers + group + addDefault + handleExc + function group + function parti + + + D:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route + D:\Workspace\simple-php-router\src + D:\Workspace\simple-php-router\tests\Pecee\SimpleRouter\Dummy + D:\Workspace\simple-php-router + E:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route + E:\Workspace\simple-php-router\tests + E:\Workspace\simple-php-router\src\Pecee + E:\Workspace\simple-php-router + E:\Workspace\simple-php-router\src\Pecee\SimpleRouter + E:\Workspace\simple-php-router\tests\Pecee\SimpleRouter + E:\Workspace\simple-php-router\src + + + + + + + + + + + false + + false + false + true + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + - - + + + + @@ -378,24 +410,21 @@
- - - - - + + + + + - - - - - + + + + + - - @@ -460,7 +489,9 @@ - + + + @@ -497,7 +528,7 @@ - @@ -509,29 +540,30 @@ - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -544,415 +576,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -962,16 +591,15 @@ - - - + + - - + + @@ -980,17 +608,286 @@ - - - + + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Pecee/Http/Input/IInputItem.php b/src/Pecee/Http/Input/IInputItem.php index ae32b50..cc5a4e0 100644 --- a/src/Pecee/Http/Input/IInputItem.php +++ b/src/Pecee/Http/Input/IInputItem.php @@ -9,14 +9,14 @@ interface IInputItem public function setIndex(string $index): self; - public function getName(): string; + public function getName(): ?string; public function setName(string $name): self; - public function getValue(): string; + public function getValue(): ?string; public function setValue(string $value): self; - public function __toString(); + public function __toString(): string; } \ No newline at end of file diff --git a/src/Pecee/Http/Input/InputFile.php b/src/Pecee/Http/Input/InputFile.php index 1af9a28..e4b5672 100644 --- a/src/Pecee/Http/Input/InputFile.php +++ b/src/Pecee/Http/Input/InputFile.php @@ -140,7 +140,7 @@ class InputFile implements IInputItem * * @return string */ - public function getName(): string + public function getName(): ?string { return $this->name; } @@ -177,7 +177,7 @@ class InputFile implements IInputItem * * @return string mixed */ - public function getFilename(): string + public function getFilename(): ?string { return $this->filename; } @@ -256,12 +256,12 @@ class InputFile implements IInputItem return $this; } - public function __toString() + public function __toString(): string { return $this->getTmpName(); } - public function getValue(): string + public function getValue(): ?string { return $this->getFilename(); } diff --git a/src/Pecee/Http/Input/InputItem.php b/src/Pecee/Http/Input/InputItem.php index c3f8129..6c677b8 100644 --- a/src/Pecee/Http/Input/InputItem.php +++ b/src/Pecee/Http/Input/InputItem.php @@ -35,7 +35,7 @@ class InputItem implements IInputItem /** * @return string */ - public function getName(): string + public function getName(): ?string { return $this->name; } @@ -55,7 +55,7 @@ class InputItem implements IInputItem /** * @return string */ - public function getValue(): string + public function getValue(): ?string { return $this->value; } @@ -72,7 +72,7 @@ class InputItem implements IInputItem return $this; } - public function __toString() + public function __toString(): string { return (string)$this->value; } diff --git a/src/Pecee/Http/Url.php b/src/Pecee/Http/Url.php index 212ae6e..099dd0a 100644 --- a/src/Pecee/Http/Url.php +++ b/src/Pecee/Http/Url.php @@ -4,7 +4,7 @@ namespace Pecee\Http; use Pecee\Http\Exceptions\MalformedUrlException; -class Url +class Url implements \JsonSerializable { private $originalUrl; @@ -441,7 +441,19 @@ class Url return $scheme . $user . $pass . $host . $port . $this->getRelativeUrl(); } - public function __toString() + /** + * Specify data which should be serialized to JSON + * @link http://php.net/manual/en/jsonserializable.jsonserialize.php + * @return mixed data which can be serialized by json_encode, + * which is a value of any type other than a resource. + * @since 5.4.0 + */ + public function jsonSerialize(): string + { + return $this->getRelativeUrl(); + } + + public function __toString(): string { return $this->getRelativeUrl(); } From 833961ddc37cc4acd97fd4b18b59b282e909a82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Fri, 30 Mar 2018 05:37:04 +0200 Subject: [PATCH 20/25] Fixed `getError` in `InputFile` returning string instead of int. --- .idea/workspace.xml | 42 ++++++++++++++---------------- src/Pecee/Http/Input/InputFile.php | 8 +++--- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 7507829..97854c5 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,11 +2,7 @@ - - - - - + - + @@ -81,11 +77,11 @@ - + - - + + @@ -103,9 +99,6 @@ - function renderRoute - ->renderRoute - fireEvent renderRoute findRoute ->fireEvents @@ -133,6 +126,9 @@ function parti __toString getValue + getError + (int) + setSize D:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route @@ -202,9 +198,9 @@ @@ -491,7 +487,7 @@ - + @@ -528,7 +524,7 @@ - @@ -870,13 +866,6 @@ - - - - - - - @@ -887,7 +876,14 @@ - + + + + + + + + diff --git a/src/Pecee/Http/Input/InputFile.php b/src/Pecee/Http/Input/InputFile.php index e4b5672..2919187 100644 --- a/src/Pecee/Http/Input/InputFile.php +++ b/src/Pecee/Http/Input/InputFile.php @@ -49,7 +49,7 @@ class InputFile implements IInputItem return (new static($values['index'])) ->setSize((int)$values['size']) - ->setError($values['error']) + ->setError((int)$values['error']) ->setType($values['type']) ->setTmpName($values['tmp_name']) ->setFilename($values['name']); @@ -216,11 +216,11 @@ class InputFile implements IInputItem /** * Get upload-error code. * - * @return string + * @return int */ - public function getError(): string + public function getError(): int { - return $this->errors; + return (int)$this->errors; } /** From 5df0c128641d0a734e0bba9c8df5f7aeed153319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Fri, 30 Mar 2018 05:54:12 +0200 Subject: [PATCH 21/25] Updated helpers --- .idea/workspace.xml | 83 ++++++++++++++++++++++++++++----------------- README.md | 2 +- helpers.php | 2 +- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 97854c5..1e9ac9b 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,7 +2,8 @@ - + + - + - - + + @@ -115,7 +131,6 @@ $this->result $this->r getInput - helpers.php input()->get getObject helpers @@ -129,6 +144,7 @@ getError (int) setSize + helpers.php D:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route @@ -191,8 +207,6 @@ @@ -487,7 +503,7 @@ - + @@ -524,7 +540,7 @@ - @@ -544,11 +560,11 @@ - + - + @@ -814,16 +830,6 @@ - - - - - - - - - - @@ -835,13 +841,6 @@ - - - - - - - @@ -882,8 +881,28 @@ - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index 0592ed9..16aa41c 100644 --- a/README.md +++ b/README.md @@ -332,7 +332,7 @@ function request(): Request * @param string|null $index Parameter index name * @param string|null $defaultValue Default return value * @param array ...$methods Default methods - * @return \Pecee\Http\Input\InputHandler|\Pecee\Http\Input\IInputItem|string + * @return \Pecee\Http\Input\InputHandler|string */ function input($index = null, $defaultValue = null, ...$methods) { diff --git a/helpers.php b/helpers.php index 264df05..00ffa4b 100644 --- a/helpers.php +++ b/helpers.php @@ -49,7 +49,7 @@ function request(): Request * @param string|null $index Parameter index name * @param string|null $defaultValue Default return value * @param array ...$methods Default methods - * @return \Pecee\Http\Input\InputHandler|\Pecee\Http\Input\IInputItem|string + * @return \Pecee\Http\Input\InputHandler|string */ function input($index = null, $defaultValue = null, ...$methods) { From 53f0b7d8e23e895f1a056c08f6d18e41ddbb40f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Fri, 30 Mar 2018 06:47:27 +0200 Subject: [PATCH 22/25] - Fixed `getName` method in `LoadableRoute` class can contain nullable value. --- .idea/workspace.xml | 192 +++++++++++------- .../SimpleRouter/Route/LoadableRoute.php | 2 +- src/Pecee/SimpleRouter/Route/Route.php | 2 +- 3 files changed, 120 insertions(+), 76 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 1e9ac9b..1da4204 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,8 +2,9 @@ - - + + + - - - - - - - - - - - - - - - + + - + - + - - - - - + + - - - - - + + + + + + + + + + + + + + + + + + + + @@ -69,7 +69,7 @@ - + @@ -78,7 +78,7 @@ - + @@ -87,7 +87,7 @@ - + @@ -97,7 +97,7 @@ - + @@ -115,7 +115,6 @@ - renderRoute findRoute ->fireEvents RENDER_MIDD @@ -145,19 +144,20 @@ (int) setSize helpers.php + getName D:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route D:\Workspace\simple-php-router\src D:\Workspace\simple-php-router\tests\Pecee\SimpleRouter\Dummy D:\Workspace\simple-php-router - E:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route E:\Workspace\simple-php-router\tests E:\Workspace\simple-php-router\src\Pecee E:\Workspace\simple-php-router E:\Workspace\simple-php-router\src\Pecee\SimpleRouter E:\Workspace\simple-php-router\tests\Pecee\SimpleRouter E:\Workspace\simple-php-router\src + E:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route @@ -200,8 +200,6 @@ @@ -277,8 +277,8 @@ - @@ -346,6 +346,14 @@ + + + + + + + + @@ -503,7 +511,8 @@ - + + @@ -540,7 +549,7 @@ - @@ -560,8 +569,8 @@ - - + + @@ -590,6 +599,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -637,13 +684,6 @@ - - - - - - - @@ -745,13 +785,6 @@ - - - - - - - @@ -767,13 +800,6 @@ - - - - - - - @@ -853,7 +879,7 @@ - + @@ -867,14 +893,14 @@ - + - + @@ -882,29 +908,47 @@ - + - - + + - + - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Route/LoadableRoute.php b/src/Pecee/SimpleRouter/Route/LoadableRoute.php index 9fc4cb3..a719af7 100644 --- a/src/Pecee/SimpleRouter/Route/LoadableRoute.php +++ b/src/Pecee/SimpleRouter/Route/LoadableRoute.php @@ -154,7 +154,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute * * @return string */ - public function getName(): string + public function getName(): ?string { return $this->name; } diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index ad56adc..c71686c 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -192,7 +192,7 @@ abstract class Route implements IRoute return $this->callback; } - return 'function_' . md5($this->callback); + return 'function:' . md5($this->callback); } /** From 1dc88d23e1d1c10a3aa57cc77605bab1d76b9bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sun, 1 Apr 2018 03:02:21 +0200 Subject: [PATCH 23/25] Bugfixes and optimizations - Fixed `hasParam` not working returning expected value in `Url` class. - Chained the remaining methods in the `Url` class. - Simplified `removeParam` method in `Url` class. - Added new `removeParams` method to `Url` class for removal of multiple params. --- .idea/workspace.xml | 232 ++++++++++++------------ src/Pecee/Http/Url.php | 46 ++--- src/Pecee/SimpleRouter/SimpleRouter.php | 2 +- 3 files changed, 142 insertions(+), 138 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 1da4204..0bf9070 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -3,8 +3,8 @@ - - + + - + - - + + + + + + + + + + + + + + @@ -31,8 +43,8 @@ - - + + @@ -42,8 +54,8 @@ - - + + @@ -57,20 +69,11 @@ - + - - - - - - - - - - - + + @@ -87,8 +90,20 @@ - - + + + + + + + + + + + + + + @@ -102,28 +117,13 @@ - - - - - - - - - - findRoute - ->fireEvents - RENDER_MIDD - EVENT_RENDER_CSRF - RENDER_ROUTE requirements # Advan # Feed - : void call_user_func_array loadClass $this-> @@ -145,6 +145,12 @@ setSize helpers.php getName + url( + url() + : void + void + set + hasParam D:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route @@ -205,18 +211,18 @@ @@ -276,9 +282,8 @@ - -