diff --git a/.gitignore b/.gitignore index f3bdaaa..8dc5dfc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ composer.lock vendor/ -tests/tmp/* \ No newline at end of file +tests/tmp/* +.idea/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 91dfc80..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/dictionaries/simon.xml b/.idea/dictionaries/simon.xml deleted file mode 100644 index 87f7ef2..0000000 --- a/.idea/dictionaries/simon.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - bootmanager - bootmanagers - csrf - middlewares - pecee - urldecode - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 15a15b2..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml deleted file mode 100644 index e41dd85..0000000 --- a/.idea/markdown-navigator.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml deleted file mode 100644 index 57927c5..0000000 --- a/.idea/markdown-navigator/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index a5ba0c2..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml deleted file mode 100644 index 82ec95c..0000000 --- a/.idea/php-test-framework.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml deleted file mode 100644 index 868ac8c..0000000 --- a/.idea/php.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/simple-php-router.iml b/.idea/simple-php-router.iml deleted file mode 100644 index f342345..0000000 --- a/.idea/simple-php-router.iml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index b3aaabb..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,877 +0,0 @@ - - - - - - - - - - - - - $PROJECT_DIR$/composer.json - - - - - - - - options - parent::set - parseParameters - stripos - setPrefix - var_dum - parse - getParams - setQuery - contains - matchRoute - ->getValue - ->find - function find - Req - value( - file( - setUrl - TODO - input()->get - function get - REQUEST_TYPE_ - or method - setDebugEnabled - debugEnabled - optiona - \/ - requirements - ler = new I - csrf_token - - - 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\tests - E:\Workspace\simple-php-router\src\Pecee - E:\Workspace\simple-php-router\tests\Pecee\SimpleRouter - E:\Workspace\simple-php-router\src\Pecee\SimpleRouter\Route - E:\Workspace\simple-php-router\src\Pecee\SimpleRouter - E:\Workspace\simple-php-router\src - E:\Workspace\simple-php-router - - - - - - - - - - - - - - - - - false - - false - false - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index ab5dc2a..9f96203 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ You can donate any amount of your choice by [clicking here](https://www.paypal.c - [Helper functions](#helper-functions) - [Routes](#routes) - [Basic routing](#basic-routing) + - [Class hinting](#class-hinting) - [Available methods](#available-methods) - [Multiple HTTP-verbs](#multiple-http-verbs) - [Route parameters](#route-parameters) @@ -123,7 +124,7 @@ composer require pecee/simple-router The goal of this project is to create a router that is more or less 100% compatible with the Laravel documentation, while remaining as simple as possible, and as easy to integrate and change without compromising either speed or complexity. Being lightweight is the #1 priority. -We've included a simple demo project for the router which can be found in the `demo-project` folder. This project should give you a basic understanding of how to setup and use simple-php-router project. +We've included a simple demo project for the router which can be found [here](https://github.com/skipperbent/simple-router-demo). This project should give you a basic understanding of how to setup and use simple-php-router project. Please note that the demo-project only covers how to integrate the `simple-php-router` in a project without an existing framework. If you are using a framework in your project, the implementation might vary. @@ -404,6 +405,14 @@ SimpleRouter::get('/', function() { }); ``` +### Class hinting + +You can use class hinting to load a class & method like this: + +```php +SimpleRouter::get('/', [MyClass::class, 'myMethod']); +``` + ### Available methods Here you can see a list over all available routes: @@ -785,12 +794,17 @@ SimpleRouter::group(['middleware' => \Demo\Middlewares\Site::class, 'exceptionHa SimpleRouter::get('/answers/{id}', 'ControllerAnswers@show', ['where' => ['id' => '[0-9]+']]); + /** + * Class hinting is supported too + */ + + SimpleRouter::get('/answers/{id}', [ControllerAnswers::class, 'show'], ['where' => ['id' => '[0-9]+']]); /** * Restful resource (see IRestController interface for available methods) */ - SimpleRouter::resource('/rest', ControllerRessource::class); + SimpleRouter::resource('/rest', ControllerResource::class); /** @@ -811,7 +825,6 @@ SimpleRouter::group(['middleware' => \Demo\Middlewares\Site::class, 'exceptionHa }); SimpleRouter::get('/page/404', 'ControllerPage@notFound', ['as' => 'page.notfound']); - ``` --- diff --git a/src/Pecee/Http/Input/InputHandler.php b/src/Pecee/Http/Input/InputHandler.php index 662df21..e485505 100644 --- a/src/Pecee/Http/Input/InputHandler.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -27,6 +27,24 @@ class InputHandler */ protected $request; + /** + * Original post variables + * @var array + */ + protected $originalPost = []; + + /** + * Original get/params variables + * @var array + */ + protected $originalParams = []; + + /** + * Get original file variables + * @var array + */ + protected $originalFile = []; + /** * Input constructor. * @param Request $request @@ -46,22 +64,34 @@ class InputHandler { /* Parse get requests */ if (\count($_GET) !== 0) { - $this->get = $this->parseInputItem($_GET); + $this->originalParams = $_GET; + $this->get = $this->parseInputItem($this->originalParams); } /* Parse post requests */ - $postVars = $_POST; + $this->originalPost = $_POST; - if (\in_array($this->request->getMethod(), ['put', 'patch', 'delete'], false) === true) { - parse_str(file_get_contents('php://input'), $postVars); + if (\in_array($this->request->getMethod(), Request::$requestTypesPost, false) === true) { + + $contents = file_get_contents('php://input'); + + // Append any PHP-input json + if (strpos(trim($contents), '{') === 0) { + $post = json_decode($contents, true); + + if ($post !== false) { + $this->originalPost += $post; + } + } } - if (\count($postVars) !== 0) { - $this->post = $this->parseInputItem($postVars); + if (\count($this->originalPost) !== 0) { + $this->post = $this->parseInputItem($this->originalPost); } /* Parse get requests */ if (\count($_FILES) !== 0) { + $this->originalFile = $_FILES; $this->file = $this->parseFiles(); } } @@ -192,11 +222,11 @@ class InputHandler { $element = null; - if (\count($methods) === 0 || \in_array('get', $methods, true) === true) { + if (\count($methods) === 0 || \in_array(Request::REQUEST_TYPE_GET, $methods, true) === true) { $element = $this->get($index); } - if (($element === null && \count($methods) === 0) || (\count($methods) !== 0 && \in_array('post', $methods, true) === true)) { + if (($element === null && \count($methods) === 0) || (\count($methods) !== 0 && \in_array(Request::REQUEST_TYPE_POST, $methods, true) === true)) { $element = $this->post($index); } @@ -288,24 +318,7 @@ class InputHandler */ public function all(array $filter = []): array { - $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) { - $output += $post; - } - } - } - + $output = $this->originalParams + $this->originalPost + $this->originalFile; $output = (\count($filter) > 0) ? array_intersect_key($output, array_flip($filter)) : $output; foreach ($filter as $filterKey) { @@ -350,4 +363,64 @@ class InputHandler $this->file[$key] = $item; } + /** + * Get original post variables + * @return array + */ + public function getOriginalPost(): array + { + return $this->originalPost; + } + + /** + * Set original post variables + * @param array $post + * @return static $this + */ + public function setOriginalPost(array $post): self + { + $this->originalPost = $post; + return $this; + } + + /** + * Get original get variables + * @return array + */ + public function getOriginalParams(): array + { + return $this->originalParams; + } + + /** + * Set original get-variables + * @param array $params + * @return static $this + */ + public function setOriginalParams(array $params): self + { + $this->originalParams = $params; + return $this; + } + + /** + * Get original file variables + * @return array + */ + public function getOriginalFile(): array + { + return $this->originalFile; + } + + /** + * Set original file posts variables + * @param array $file + * @return static $this + */ + public function setOriginalFile(array $file): self + { + $this->originalFile = $file; + return $this; + } + } \ No newline at end of file diff --git a/src/Pecee/Http/Input/InputItem.php b/src/Pecee/Http/Input/InputItem.php index bd8667e..19ed96d 100644 --- a/src/Pecee/Http/Input/InputItem.php +++ b/src/Pecee/Http/Input/InputItem.php @@ -2,9 +2,6 @@ namespace Pecee\Http\Input; -use Exception; -use Traversable; - class InputItem implements IInputItem, \IteratorAggregate { public $index; @@ -81,7 +78,7 @@ class InputItem implements IInputItem, \IteratorAggregate return (\is_array($value) === true) ? json_encode($value) : $value; } - public function getIterator() + public function getIterator(): \ArrayIterator { return new \ArrayIterator($this->getValue()); } diff --git a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php index 4815e8c..f137504 100644 --- a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php +++ b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php @@ -63,13 +63,12 @@ class BaseCsrfVerifier implements IMiddleware */ public function handle(Request $request): void { - - if ($this->skip($request) === false && \in_array($request->getMethod(), ['post', 'put', 'delete'], true) === true) { + if ($this->skip($request) === false && \in_array($request->getMethod(), Request::$requestTypesPost, true) === true) { $token = $request->getInputHandler()->value( static::POST_KEY, $request->getHeader(static::HEADER_KEY), - 'post' + Request::$requestTypesPost ); if ($this->tokenProvider->validate((string)$token) === false) { @@ -80,7 +79,6 @@ class BaseCsrfVerifier implements IMiddleware // Refresh existing token $this->tokenProvider->refresh(); - } public function getTokenProvider(): ITokenProvider diff --git a/src/Pecee/Http/Request.php b/src/Pecee/Http/Request.php index 6a7b883..d10c48b 100644 --- a/src/Pecee/Http/Request.php +++ b/src/Pecee/Http/Request.php @@ -4,12 +4,46 @@ namespace Pecee\Http; use Pecee\Http\Exceptions\MalformedUrlException; use Pecee\Http\Input\InputHandler; +use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\SimpleRouter\Route\ILoadableRoute; use Pecee\SimpleRouter\Route\RouteUrl; use Pecee\SimpleRouter\SimpleRouter; class Request { + 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 const REQUEST_TYPE_HEAD = 'head'; + + /** + * All request-types + * @var string[] + */ + public static $requestTypes = [ + self::REQUEST_TYPE_GET, + self::REQUEST_TYPE_POST, + self::REQUEST_TYPE_PUT, + self::REQUEST_TYPE_PATCH, + self::REQUEST_TYPE_OPTIONS, + self::REQUEST_TYPE_DELETE, + self::REQUEST_TYPE_HEAD, + ]; + + /** + * Post request-types. + * @var string[] + */ + public static $requestTypesPost = [ + self::REQUEST_TYPE_POST, + self::REQUEST_TYPE_PUT, + self::REQUEST_TYPE_PATCH, + self::REQUEST_TYPE_DELETE, + ]; + /** * Additional data * @@ -77,14 +111,14 @@ class Request { foreach ($_SERVER as $key => $value) { $this->headers[strtolower($key)] = $value; - $this->headers[strtolower(str_replace('_', '-', $key))] = $value; + $this->headers[str_replace('_', '-', strtolower($key))] = $value; } $this->setHost($this->getHeader('http-host')); // Check if special IIS header exist, otherwise use default. $this->setUrl(new Url($this->getHeader('unencoded-url', $this->getHeader('request-uri')))); - + $this->method = strtolower($this->getHeader('request-method')); $this->inputHandler = new InputHandler($this); $this->method = strtolower($this->inputHandler->value('_method', $this->getHeader('request-method'))); @@ -147,6 +181,15 @@ class Request return $this->getHeader('php-auth-pw'); } + /** + * Get the csrf token + * @return string|null + */ + public function getCsrfToken(): ?string + { + return $this->getHeader(BaseCsrfVerifier::HEADER_KEY); + } + /** * Get all headers * @return array @@ -165,6 +208,13 @@ class Request */ public function getIp(bool $safe = false): ?string { + return $this->getHeader( + 'http-cf-connecting-ip', + $this->getHeader( + 'http-x-forwarded-for', + $this->getHeader('remote-addr') + ) + ); $client_header = null; if(!$safe){ if ($this->getHeader('http-cf-connecting-ip') !== null) { @@ -212,14 +262,28 @@ class Request /** * Get header value by name * - * @param string $name - * @param string|null $defaultValue + * @param string $name Name of the header. + * @param string|null $defaultValue Value to be returned if header is not found. + * @param bool $tryParse When enabled the method will try to find the header from both from client (http) and server-side variants, if the header is not found. * * @return string|null */ - public function getHeader($name, $defaultValue = null): ?string + public function getHeader(string $name, $defaultValue = null, $tryParse = true): ?string { - return $this->headers[strtolower($name)] ?? $defaultValue; + $name = strtolower($name); + $header = $this->headers[$name] ?? null; + + if ($tryParse === true && $header === null) { + if (strpos($name, 'http-') === 0) { + // Trying to find client header variant which was not found, searching for header variant without http- prefix. + $header = $this->headers[str_replace('http-', '', $name)] ?? null; + } else { + // Trying to find server variant which was not found, searching for client variant with http- prefix. + $header = $this->headers['http-' . $name] ?? null; + } + } + + return $header ?? $defaultValue; } /** diff --git a/src/Pecee/SimpleRouter/ClassLoader/ClassLoader.php b/src/Pecee/SimpleRouter/ClassLoader/ClassLoader.php index c73c7a8..c928339 100644 --- a/src/Pecee/SimpleRouter/ClassLoader/ClassLoader.php +++ b/src/Pecee/SimpleRouter/ClassLoader/ClassLoader.php @@ -48,7 +48,7 @@ class ClassLoader implements IClassLoader /** * Load closure * - * @param \Closure $closure + * @param Callable $closure * @param array $parameters * @return mixed * @throws NotFoundHttpException diff --git a/src/Pecee/SimpleRouter/Route/IRoute.php b/src/Pecee/SimpleRouter/Route/IRoute.php index 4654e1f..1af8fe8 100644 --- a/src/Pecee/SimpleRouter/Route/IRoute.php +++ b/src/Pecee/SimpleRouter/Route/IRoute.php @@ -22,8 +22,8 @@ interface IRoute * * @param Request $request * @param Router $router - * @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException * @return string + * @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException */ public function renderRoute(Request $request, Router $router): ?string; @@ -82,7 +82,7 @@ interface IRoute /** * Set callback * - * @param string $callback + * @param string|array|\Closure $callback * @return static */ public function setCallback($callback): self; @@ -206,4 +206,18 @@ interface IRoute */ public function setMiddlewares(array $middlewares): self; + /** + * If enabled parameters containing null-value will not be passed along to the callback. + * + * @param bool $enabled + * @return static $this + */ + public function setFilterEmptyParams(bool $enabled): self; + + /** + * Status if filtering of empty params is enabled or disabled + * @return bool + */ + public function getFilterEmptyParams(): bool; + } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Route/LoadableRoute.php b/src/Pecee/SimpleRouter/Route/LoadableRoute.php index 536c415..ba03159 100644 --- a/src/Pecee/SimpleRouter/Route/LoadableRoute.php +++ b/src/Pecee/SimpleRouter/Route/LoadableRoute.php @@ -60,7 +60,7 @@ abstract class LoadableRoute extends Route implements ILoadableRoute return null; } - return ((bool)preg_match($this->regex, $request->getHost() . $url) !== false); + return ((bool)preg_match($this->regex, $url) !== false); } /** diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index de3ba74..3e9370c 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -10,25 +10,7 @@ use Pecee\SimpleRouter\Router; abstract class Route implements IRoute { protected const PARAMETERS_REGEX_FORMAT = '%s([\w]+)(\%s?)%s'; - protected const PARAMETERS_DEFAULT_REGEX = '[\w\-]+'; - - 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 const REQUEST_TYPE_HEAD = 'head'; - - public static $requestTypes = [ - self::REQUEST_TYPE_GET, - self::REQUEST_TYPE_POST, - self::REQUEST_TYPE_PUT, - self::REQUEST_TYPE_PATCH, - self::REQUEST_TYPE_OPTIONS, - self::REQUEST_TYPE_DELETE, - self::REQUEST_TYPE_HEAD, - ]; + protected const PARAMETERS_DEFAULT_REGEX = '[\w-]+'; /** * If enabled parameters containing null-value @@ -99,22 +81,19 @@ abstract class Route implements IRoute return $router->getClassLoader()->loadClosure($callback, $parameters); } - /* When the callback is a class + method */ - $controller = explode('@', $callback); + $controller = $this->getClass(); + $method = $this->getMethod(); $namespace = $this->getNamespace(); - - $className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0]; + $className = ($namespace !== null && $controller[0] !== '\\') ? $namespace . '\\' . $controller : $controller; $router->debug('Loading class %s', $className); $class = $router->getClassLoader()->loadClass($className); - if (\count($controller) === 1) { + if ($method === null) { $controller[1] = '__invoke'; } - $method = $controller[1]; - if (method_exists($class, $method) === false) { throw new NotFoundHttpException(sprintf('Method "%s" does not exist in class "%s"', $method, $className), 404); } @@ -144,7 +123,7 @@ abstract class Route implements IRoute $urlRegex = preg_quote($route, '/'); } else { - foreach (preg_split('/((\-?\/?)\{[^}]+\})/', $route) as $key => $t) { + foreach (preg_split('/((-?\/?){[^}]+})/', $route) as $key => $t) { $regex = ''; @@ -159,7 +138,7 @@ abstract class Route implements IRoute $regex = $parameterRegex ?? $this->defaultParameterRegex ?? static::PARAMETERS_DEFAULT_REGEX; } - $regex = sprintf('((\/|\-)(?P<%2$s>%3$s))%1$s', $parameters[2][$key], $name, $regex); + $regex = sprintf('((\/|-)(?P<%2$s>%3$s))%1$s', $parameters[2][$key], $name, $regex); } $urlRegex .= preg_quote($t, '/') . $regex; @@ -271,7 +250,7 @@ abstract class Route implements IRoute /** * Set callback * - * @param string $callback + * @param string|array\Closure $callback * @return static */ public function setCallback($callback): IRoute @@ -291,6 +270,10 @@ abstract class Route implements IRoute public function getMethod(): ?string { + if (\is_array($this->callback) === true && \count($this->callback) > 1) { + return $this->callback[1]; + } + if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) { $tmp = explode('@', $this->callback); @@ -302,6 +285,10 @@ abstract class Route implements IRoute public function getClass(): ?string { + if (\is_array($this->callback) === true && \count($this->callback) > 0) { + return $this->callback[0]; + } + if (\is_string($this->callback) === true && strpos($this->callback, '@') !== false) { $tmp = explode('@', $this->callback); @@ -313,14 +300,14 @@ abstract class Route implements IRoute public function setMethod(string $method): IRoute { - $this->callback = sprintf('%s@%s', $this->getClass(), $method); + $this->callback = [$this->getClass(), $method]; return $this; } public function setClass(string $class): IRoute { - $this->callback = sprintf('%s@%s', $class, $this->getMethod()); + $this->callback = [$class, $this->getMethod()]; return $this; } @@ -456,9 +443,9 @@ abstract class Route implements IRoute * Add regular expression parameter match. * Alias for LoadableRoute::where() * - * @see LoadableRoute::where() * @param array $options * @return static + * @see LoadableRoute::where() */ public function where(array $options) { @@ -506,9 +493,9 @@ abstract class Route implements IRoute /** * Add middleware class-name * - * @deprecated This method is deprecated and will be removed in the near future. * @param IMiddleware|string $middleware * @return static + * @deprecated This method is deprecated and will be removed in the near future. */ public function setMiddleware($middleware) { @@ -575,4 +562,25 @@ abstract class Route implements IRoute return $this->defaultParameterRegex; } + /** + * If enabled parameters containing null-value will not be passed along to the callback. + * + * @param bool $enabled + * @return static $this + */ + public function setFilterEmptyParams(bool $enabled): IRoute + { + $this->filterEmptyParams = $enabled; + return $this; + } + + /** + * Status if filtering of empty params is enabled or disabled + * @return bool + */ + public function getFilterEmptyParams(): bool + { + return $this->filterEmptyParams; + } + } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Route/RouteController.php b/src/Pecee/SimpleRouter/Route/RouteController.php index 09091f1..551ddbb 100644 --- a/src/Pecee/SimpleRouter/Route/RouteController.php +++ b/src/Pecee/SimpleRouter/Route/RouteController.php @@ -64,7 +64,7 @@ class RouteController extends LoadableRoute implements IControllerRoute if ($method !== null) { /* Remove requestType from method-name, if it exists */ - foreach (static::$requestTypes as $requestType) { + foreach (Request::$requestTypes as $requestType) { if (stripos($method, $requestType) === 0) { $method = (string)substr($method, \strlen($requestType)); @@ -110,7 +110,7 @@ class RouteController extends LoadableRoute implements IControllerRoute $this->parameters = \array_slice($path, 1); // Set callback - $this->setCallback($this->controller . '@' . $this->method); + $this->setCallback([$this->controller, $this->method]); return true; } diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index 1c4cc33..e3fc8ac 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -52,8 +52,16 @@ class RouteGroup extends Route implements IGroupRoute return false; } + // Parse parameter + + $prefix = $this->prefix; + + foreach($this->getParameters() as $parameter => $value) { + $prefix = str_ireplace('{' . $parameter . '}', $value, $prefix); + } + /* Skip if prefix doesn't match */ - if ($this->prefix !== null && stripos($url, $this->prefix) === false) { + if ($this->prefix !== null && stripos($url, $prefix) === false) { return false; } diff --git a/src/Pecee/SimpleRouter/Route/RouteResource.php b/src/Pecee/SimpleRouter/Route/RouteResource.php index 6c79f40..7ae692c 100644 --- a/src/Pecee/SimpleRouter/Route/RouteResource.php +++ b/src/Pecee/SimpleRouter/Route/RouteResource.php @@ -78,7 +78,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute protected function call($method): bool { - $this->setCallback($this->controller . '@' . $method); + $this->setCallback([$this->controller, $method]); return true; } @@ -115,32 +115,32 @@ class RouteResource extends LoadableRoute implements IControllerRoute $method = $request->getMethod(); // Delete - if ($method === static::REQUEST_TYPE_DELETE && $id !== null) { + if ($method === Request::REQUEST_TYPE_DELETE && $id !== null) { return $this->call($this->methodNames['destroy']); } // Update - if ($id !== null && \in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], true) === true) { + if ($id !== null && \in_array($method, [Request::REQUEST_TYPE_PATCH, Request::REQUEST_TYPE_PUT], true) === true) { return $this->call($this->methodNames['update']); } // Edit - if ($method === static::REQUEST_TYPE_GET && $id !== null && $action === 'edit') { + if ($method === Request::REQUEST_TYPE_GET && $id !== null && $action === 'edit') { return $this->call($this->methodNames['edit']); } // Create - if ($method === static::REQUEST_TYPE_GET && $id === 'create') { + if ($method === Request::REQUEST_TYPE_GET && $id === 'create') { return $this->call($this->methodNames['create']); } // Save - if ($method === static::REQUEST_TYPE_POST) { + if ($method === Request::REQUEST_TYPE_POST) { return $this->call($this->methodNames['store']); } // Show - if ($method === static::REQUEST_TYPE_GET && $id !== null) { + if ($method === Request::REQUEST_TYPE_GET && $id !== null) { return $this->call($this->methodNames['show']); } diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 8b1378e..758204f 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -305,8 +305,8 @@ class Router * Start the routing * * @return string|null - * @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException - * @throws \Pecee\Http\Middleware\Exceptions\TokenMismatchException + * @throws NotFoundHttpException + * @throws TokenMismatchException * @throws HttpException * @throws \Exception */ diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index b8f4d06..284078b 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -12,7 +12,6 @@ namespace Pecee\SimpleRouter; use DI\Container; use Pecee\Exceptions\InvalidArgumentException; -use Pecee\Http\Exceptions\MalformedUrlException; use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Request; use Pecee\Http\Response; @@ -76,7 +75,6 @@ class SimpleRouter ob_start(); static::router()->setDebugEnabled(true)->start(); $routerOutput = ob_get_clean(); - ob_end_clean(); } catch (\Exception $e) { } @@ -178,79 +176,79 @@ class SimpleRouter * Route the given url to your callback on GET request method. * * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * * @return RouteUrl */ public static function get(string $url, $callback, array $settings = null): IRoute { - return static::match(['get'], $url, $callback, $settings); + return static::match([Request::REQUEST_TYPE_GET], $url, $callback, $settings); } /** * Route the given url to your callback on POST request method. * * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * @return RouteUrl */ public static function post(string $url, $callback, array $settings = null): IRoute { - return static::match(['post'], $url, $callback, $settings); + return static::match([Request::REQUEST_TYPE_POST], $url, $callback, $settings); } /** * Route the given url to your callback on PUT request method. * * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * @return RouteUrl */ public static function put(string $url, $callback, array $settings = null): IRoute { - return static::match(['put'], $url, $callback, $settings); + return static::match([Request::REQUEST_TYPE_PUT], $url, $callback, $settings); } /** * Route the given url to your callback on PATCH request method. * * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * @return RouteUrl */ public static function patch(string $url, $callback, array $settings = null): IRoute { - return static::match(['patch'], $url, $callback, $settings); + return static::match([Request::REQUEST_TYPE_PATCH], $url, $callback, $settings); } /** * Route the given url to your callback on OPTIONS request method. * * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * @return RouteUrl */ public static function options(string $url, $callback, array $settings = null): IRoute { - return static::match(['options'], $url, $callback, $settings); + return static::match([Request::REQUEST_TYPE_OPTIONS], $url, $callback, $settings); } /** * Route the given url to your callback on DELETE request method. * * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * @return RouteUrl */ public static function delete(string $url, $callback, array $settings = null): IRoute { - return static::match(['delete'], $url, $callback, $settings); + return static::match([Request::REQUEST_TYPE_DELETE], $url, $callback, $settings); } /** @@ -307,14 +305,14 @@ class SimpleRouter * Alias for the form method * * @param string $url - * @param callable $callback + * @param string|array|\Closure $callback * @param array|null $settings * @see SimpleRouter::form * @return RouteUrl */ public static function basic(string $url, $callback, array $settings = null): IRoute { - return static::match(['get', 'post'], $url, $callback, $settings); + return static::form($url, $callback, $settings); } /** @@ -322,14 +320,17 @@ class SimpleRouter * Route the given url to your callback on POST and GET request method. * * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * @see SimpleRouter::form * @return RouteUrl */ public static function form(string $url, $callback, array $settings = null): IRoute { - return static::match(['get', 'post'], $url, $callback, $settings); + return static::match([ + Request::REQUEST_TYPE_GET, + Request::REQUEST_TYPE_POST + ], $url, $callback, $settings); } /** @@ -337,7 +338,7 @@ class SimpleRouter * * @param array $requestMethods * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * @return RouteUrl|IRoute */ @@ -358,7 +359,7 @@ class SimpleRouter * This type will route the given url to your callback and allow any type of request method * * @param string $url - * @param string|\Closure $callback + * @param string|array|\Closure $callback * @param array|null $settings * @return RouteUrl|IRoute */ @@ -382,7 +383,7 @@ class SimpleRouter * @param array|null $settings * @return RouteController|IRoute */ - public static function controller(string $url, $controller, array $settings = null) + public static function controller(string $url, string $controller, array $settings = null) { $route = new RouteController($url, $controller); $route = static::addDefaultNamespace($route); @@ -402,7 +403,7 @@ class SimpleRouter * @param array|null $settings * @return RouteResource|IRoute */ - public static function resource(string $url, $controller, array $settings = null) + public static function resource(string $url, string $controller, array $settings = null) { $route = new RouteResource($url, $controller); $route = static::addDefaultNamespace($route); @@ -465,7 +466,7 @@ class SimpleRouter /** * Get the request * - * @return \Pecee\Http\Request + * @return Request */ public static function request(): Request { diff --git a/tests/Pecee/SimpleRouter/Dummy/DummyController.php b/tests/Pecee/SimpleRouter/Dummy/DummyController.php index 8e38c2d..4150118 100644 --- a/tests/Pecee/SimpleRouter/Dummy/DummyController.php +++ b/tests/Pecee/SimpleRouter/Dummy/DummyController.php @@ -2,6 +2,12 @@ class DummyController { + public function index() + { + + } + + public function method1() { diff --git a/tests/Pecee/SimpleRouter/Dummy/ResourceController.php b/tests/Pecee/SimpleRouter/Dummy/ResourceController.php index 0a70e6f..bd36ab3 100644 --- a/tests/Pecee/SimpleRouter/Dummy/ResourceController.php +++ b/tests/Pecee/SimpleRouter/Dummy/ResourceController.php @@ -4,43 +4,36 @@ class ResourceController implements \Pecee\Controllers\IResourceController public function index() : ?string { - echo 'index'; - return null; + return 'index'; } public function show($id) : ?string { - echo 'show ' . $id; - return null; + return 'show ' . $id; } public function store() : ?string { - echo 'store'; - return null; + return 'store'; } public function create() : ?string { - echo 'create'; - return null; + return 'create'; } public function edit($id) : ?string { - echo 'edit ' . $id; - return null; + return 'edit ' . $id; } public function update($id) : ?string { - echo 'update ' . $id; - return null; + return 'update ' . $id; } public function destroy($id) : ?string { - echo 'destroy ' . $id; - return null; + return 'destroy ' . $id; } } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/InputHandlerTest.php b/tests/Pecee/SimpleRouter/InputHandlerTest.php index 9732c19..7fcb87b 100644 --- a/tests/Pecee/SimpleRouter/InputHandlerTest.php +++ b/tests/Pecee/SimpleRouter/InputHandlerTest.php @@ -6,23 +6,29 @@ require_once 'Dummy/Handler/ExceptionHandler.php'; class InputHandlerTest extends \PHPUnit\Framework\TestCase { + protected $names = [ + 'Lester', + 'Michael', + 'Franklin', + 'Trevor', + ]; + + protected $brands = [ + 'Samsung', + 'Apple', + 'HP', + 'Canon', + ]; + + protected $day = 'monday'; public function testPost() { global $_POST; - $names = [ - 'Lester', - 'Michael', - 'Franklin', - 'Trevor', - ]; - - $day = 'monday'; - $_POST = [ - 'names' => $names, - 'day' => $day, + 'names' => $this->names, + 'day' => $this->day, ]; $router = TestRouter::router(); @@ -31,11 +37,12 @@ class InputHandlerTest extends \PHPUnit\Framework\TestCase $handler = TestRouter::request()->getInputHandler(); - $this->assertEquals($names, $handler->value('names')); - $this->assertEquals($names, $handler->all(['names'])['names']); - $this->assertEquals($day, $handler->value('day')); + $this->assertEquals($this->names, $handler->value('names')); + $this->assertEquals($this->names, $handler->all(['names'])['names']); + $this->assertEquals($this->day, $handler->value('day')); $this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->find('day')); $this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->post('day')); + $this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->find('day', 'post')); // Check non-existing and wrong request-type $this->assertCount(1, $handler->all(['non-existing'])); @@ -53,9 +60,10 @@ class InputHandlerTest extends \PHPUnit\Framework\TestCase /* @var $object \Pecee\Http\Input\InputItem */ foreach($objects as $i => $object) { $this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $object); - $this->assertEquals($names[$i], $object->getValue()); + $this->assertEquals($this->names[$i], $object->getValue()); } + // Reset $_POST = []; } @@ -63,18 +71,9 @@ class InputHandlerTest extends \PHPUnit\Framework\TestCase { global $_GET; - $names = [ - 'Lester', - 'Michael', - 'Franklin', - 'Trevor', - ]; - - $day = 'monday'; - $_GET = [ - 'names' => $names, - 'day' => $day, + 'names' => $this->names, + 'day' => $this->day, ]; $router = TestRouter::router(); @@ -83,9 +82,9 @@ class InputHandlerTest extends \PHPUnit\Framework\TestCase $handler = TestRouter::request()->getInputHandler(); - $this->assertEquals($names, $handler->value('names')); - $this->assertEquals($names, $handler->all(['names'])['names']); - $this->assertEquals($day, $handler->value('day')); + $this->assertEquals($this->names, $handler->value('names')); + $this->assertEquals($this->names, $handler->all(['names'])['names']); + $this->assertEquals($this->day, $handler->value('day')); $this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->find('day')); $this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->get('day')); @@ -105,25 +104,73 @@ class InputHandlerTest extends \PHPUnit\Framework\TestCase /* @var $object \Pecee\Http\Input\InputItem */ foreach($objects as $i => $object) { $this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $object); - $this->assertEquals($names[$i], $object->getValue()); + $this->assertEquals($this->names[$i], $object->getValue()); } + // Reset $_GET = []; } public function testFile() { + // TODO: implement test-file $this->assertEquals(true, true); } public function testFiles() { + // TODO: implement test-files $this->assertEquals(true, true); } public function testAll() { - $this->assertEquals(true, true); + global $_POST; + global $_GET; + + $_POST = [ + 'names' => $this->names, + 'is_sad' => true, + ]; + + $_GET = [ + 'brands' => $this->brands, + 'is_happy' => true, + ]; + + $router = TestRouter::router(); + $router->reset(); + $router->getRequest()->setMethod('post'); + + $handler = TestRouter::request()->getInputHandler(); + + // GET + $brandsFound = $handler->all(['brands', 'nothing']); + + $this->assertArrayHasKey('brands', $brandsFound); + $this->assertArrayHasKey('nothing', $brandsFound); + $this->assertEquals($this->brands, $brandsFound['brands']); + $this->assertNull($brandsFound['nothing']); + + // POST + $namesFound = $handler->all(['names', 'nothing']); + + $this->assertArrayHasKey('names', $namesFound); + $this->assertArrayHasKey('nothing', $namesFound); + $this->assertEquals($this->names, $namesFound['names']); + $this->assertNull($namesFound['nothing']); + + // DEFAULT VALUE + $nonExisting = $handler->all([ + 'non-existing' + ]); + + $this->assertArrayHasKey('non-existing', $nonExisting); + $this->assertNull($nonExisting['non-existing']); + + // Reset + $_GET = []; + $_POST = []; } } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/RouterPartialGroupTest.php b/tests/Pecee/SimpleRouter/RouterPartialGroupTest.php index 21c9fe2..40e288a 100644 --- a/tests/Pecee/SimpleRouter/RouterPartialGroupTest.php +++ b/tests/Pecee/SimpleRouter/RouterPartialGroupTest.php @@ -25,4 +25,49 @@ class RouterPartialGroupTest extends \PHPUnit\Framework\TestCase $this->assertEquals('param2', $result2); } + /** + * Fixed issue with partial routes not loading child groups. + * Reported in issue: #456 + */ + public function testPartialGroupWithGroup() { + + $lang = null; + + $route1 = '/lang/da/test/'; + $route2 = '/lang/da/auth'; + $route3 = '/lang/da/auth/test'; + + TestRouter::partialGroup( + '/lang/{test}/', + function ($lang = 'en') use($route1, $route2, $route3) { + + TestRouter::get('/test/', function () use($route1) { + return $route1; + }); + + TestRouter::group(['prefix' => '/auth/'], function () use($route2, $route3) { + + TestRouter::get('/', function() use($route2) { + return $route2; + }); + + TestRouter::get('/test', function () use($route3){ + return $route3; + }); + + }); + + } + ); + + $test1 = TestRouter::debugOutput('/lang/da/test', 'get', false); + $test2 = TestRouter::debugOutput('/lang/da/auth', 'get', false); + $test3 = TestRouter::debugOutput('/lang/da/auth/test', 'get', false); + + $this->assertEquals($test1, $route1); + $this->assertEquals($test2, $route2); + $this->assertEquals($test3, $route3); + + } + } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/RouterRouteTest.php b/tests/Pecee/SimpleRouter/RouterRouteTest.php index 35b88af..e92bd37 100644 --- a/tests/Pecee/SimpleRouter/RouterRouteTest.php +++ b/tests/Pecee/SimpleRouter/RouterRouteTest.php @@ -101,7 +101,7 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testPathParamRegex() { - TestRouter::get('/{lang}/productscategories/{name}', 'DummyController@param', ['where' => ['lang' => '[a-z]+', 'name' => '[A-Za-z0-9\-]+']]); + TestRouter::get('/{lang}/productscategories/{name}', 'DummyController@param', ['where' => ['lang' => '[a-z]+', 'name' => '[A-Za-z0-9-]+']]); $response = TestRouter::debugOutput('/it/productscategories/system', 'get'); $this->assertEquals('it, system', $response); @@ -144,7 +144,7 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testRegEx() { - TestRouter::get('/my/{path}', 'DummyController@method1')->where(['path' => '[a-zA-Z\-]+']); + TestRouter::get('/my/{path}', 'DummyController@method1')->where(['path' => '[a-zA-Z-]+']); TestRouter::debug('/my/custom-path', 'get'); $this->assertTrue(true); @@ -182,7 +182,7 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testDefaultParameterRegex() { - TestRouter::get('/my/{path}', 'DummyController@param', ['defaultParameterRegex' => '[\w\-]+']); + TestRouter::get('/my/{path}', 'DummyController@param', ['defaultParameterRegex' => '[\w-]+']); $output = TestRouter::debugOutput('/my/custom-regex', 'get'); $this->assertEquals('custom-regex', $output); @@ -190,7 +190,7 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testDefaultParameterRegexGroup() { - TestRouter::group(['defaultParameterRegex' => '[\w\-]+'], function () { + TestRouter::group(['defaultParameterRegex' => '[\w-]+'], function () { TestRouter::get('/my/{path}', 'DummyController@param'); }); @@ -199,4 +199,15 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase $this->assertEquals('custom-regex', $output); } + public function testClassHint() + { + TestRouter::get('/my/test/url', ['DummyController', 'method1']); + TestRouter::all('/my/test/url', ['DummyController', 'method1']); + TestRouter::match(['put', 'get', 'post'], '/my/test/url', ['DummyController', 'method1']); + + TestRouter::debug('/my/test/url', 'get'); + + $this->assertTrue(true); + } + } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/RouterUrlTest.php b/tests/Pecee/SimpleRouter/RouterUrlTest.php index cd6c1a1..2aa4706 100644 --- a/tests/Pecee/SimpleRouter/RouterUrlTest.php +++ b/tests/Pecee/SimpleRouter/RouterUrlTest.php @@ -78,10 +78,11 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase public function testSimilarUrls() { // Match normal route on alias - TestRouter::resource('/url11', 'DummyController@method1'); - TestRouter::resource('/url1', 'DummyController@method1', ['as' => 'match']); + TestRouter::get('/url11', 'DummyController@method1'); + TestRouter::get('/url22', 'DummyController@method2'); + TestRouter::get('/url33', 'DummyController@method2')->name('match'); - TestRouter::debugNoReset('/url1', 'get'); + TestRouter::debugNoReset('/url33', 'get'); $this->assertEquals(TestRouter::getUrl('match'), TestRouter::getUrl()); @@ -170,4 +171,16 @@ class RouterUrlTest extends \PHPUnit\Framework\TestCase } + public function testCustomRegex() + { + TestRouter::request()->setHost('google.com'); + + TestRouter::get('/admin/', function() { + return 'match'; + })->setMatch('/^\/admin\/?(.*)/i'); + + $output = TestRouter::debugOutput('/admin/asd/bec/123', 'get'); + $this->assertEquals('match', $output); + } + } \ No newline at end of file diff --git a/tests/TestRouter.php b/tests/TestRouter.php index a6a5321..1a1b4c1 100644 --- a/tests/TestRouter.php +++ b/tests/TestRouter.php @@ -3,6 +3,11 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter { + public function __construct() + { + static::request()->setHost('testhost.com'); + } + public static function debugNoReset($testUrl, $testMethod = 'get') { $request = static::request(); @@ -13,7 +18,7 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter static::start(); } - public static function debug($testUrl, $testMethod = 'get') + public static function debug($testUrl, $testMethod = 'get', bool $reset = true) { try { static::debugNoReset($testUrl, $testMethod); @@ -22,19 +27,20 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter throw $e; } - static::router()->reset(); + if($reset === true) { + static::router()->reset(); + } } - public static function debugOutput($testUrl, $testMethod = 'get') + public static function debugOutput($testUrl, $testMethod = 'get', bool $reset = true) { $response = null; // Route request ob_start(); - static::debug($testUrl, $testMethod); - $response = ob_get_contents(); - ob_end_clean(); + static::debug($testUrl, $testMethod, $reset); + $response = ob_get_clean(); // Return response return $response;