diff --git a/composer.json b/composer.json index 7b18724..43e4a5f 100644 --- a/composer.json +++ b/composer.json @@ -31,8 +31,12 @@ "ext-json": "*" }, "require-dev": { - "phpunit/phpunit": "^7", - "mockery/mockery": "^1" + "phpunit/phpunit": "^8", + "mockery/mockery": "^1", + "phpstan/phpstan": "^0", + "phpstan/phpstan-phpunit": "^0", + "phpstan/phpstan-deprecation-rules": "^0", + "phpstan/phpstan-strict-rules": "^0" }, "scripts": { "test": [ @@ -44,4 +48,4 @@ "Pecee\\": "src/Pecee/" } } -} +} \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..3719f4a --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,22 @@ +parameters: + level: 6 + paths: + - src + fileExtensions: + - php + bootstrapFiles: + - ./vendor/autoload.php + ignoreErrors: + reportUnmatchedIgnoredErrors: true + checkMissingIterableValueType: false + checkGenericClassInNonGenericObjectType: false + parallel: + processTimeout: 300.0 + jobSize: 10 + maximumNumberOfProcesses: 4 + minimumNumberOfJobsPerProcess: 4 +includes: + - vendor/phpstan/phpstan-strict-rules/rules.neon + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon + - vendor/phpstan/phpstan-deprecation-rules/rules.neon \ No newline at end of file diff --git a/src/Pecee/Http/Input/IInputItem.php b/src/Pecee/Http/Input/IInputItem.php index 0c851c8..430b557 100644 --- a/src/Pecee/Http/Input/IInputItem.php +++ b/src/Pecee/Http/Input/IInputItem.php @@ -13,8 +13,14 @@ interface IInputItem public function setName(string $name): self; + /** + * @return mixed + */ public function getValue(); + /** + * @param mixed $value + */ public function setValue($value): self; public function __toString(): string; diff --git a/src/Pecee/Http/Input/InputHandler.php b/src/Pecee/Http/Input/InputHandler.php index e909382..8cbfae7 100644 --- a/src/Pecee/Http/Input/InputHandler.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -115,7 +115,7 @@ class InputHandler // Handle array input if (is_array($value['name']) === false) { - $values['index'] = $parentKey ?? $key; + $values = ['index' => $parentKey ?? $key]; try { $list[$key] = InputFile::createFromArray($values + $value); @@ -161,7 +161,7 @@ class InputHandler try { $file = InputFile::createFromArray([ - 'index' => (empty($key) === true && empty($originalIndex) === false) ? $originalIndex : $key, + 'index' => ($key === '' && $originalIndex !== '') ? $originalIndex : $key, 'name' => $original['name'][$key], 'error' => $original['error'][$key], 'tmp_name' => $original['tmp_name'][$key], diff --git a/src/Pecee/Http/Request.php b/src/Pecee/Http/Request.php index 4c5ee42..12ede38 100644 --- a/src/Pecee/Http/Request.php +++ b/src/Pecee/Http/Request.php @@ -137,7 +137,7 @@ class Request public function isSecure(): bool { - return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443; + return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || (int)$this->getHeader('server-port') === 443; } /** diff --git a/src/Pecee/Http/Url.php b/src/Pecee/Http/Url.php index d856a54..3df21e2 100644 --- a/src/Pecee/Http/Url.php +++ b/src/Pecee/Http/Url.php @@ -387,7 +387,7 @@ class Url implements JsonSerializable { $encodedUrl = preg_replace_callback( '/[^:\/@?&=#]+/u', - static function ($matches) { + static function ($matches): string { return urlencode($matches[0]); }, $url @@ -414,7 +414,7 @@ class Url implements JsonSerializable if (count($getParams) !== 0) { if ($includeEmpty === false) { - $getParams = array_filter($getParams, static function ($item) { + $getParams = array_filter($getParams, static function ($item): bool { return (trim($item) !== ''); }); } @@ -458,7 +458,7 @@ class Url implements JsonSerializable $port = $this->port !== null ? ':' . $this->port : ''; $user = $this->username ?? ''; $pass = $this->password !== null ? ':' . $this->password : ''; - $pass = ($user || $pass) ? $pass . '@' : ''; + $pass = ($user !== '' || $pass !== '') ? $pass . '@' : ''; return $scheme . $user . $pass . $host . $port . $this->getRelativeUrl($includeParams); } @@ -466,7 +466,7 @@ class Url implements JsonSerializable /** * 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, + * @return string data which can be serialized by json_encode, * which is a value of any type other than a resource. * @since 5.4.0 */ diff --git a/src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php b/src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php index 92ff158..37eae16 100644 --- a/src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php +++ b/src/Pecee/SimpleRouter/Handlers/DebugEventHandler.php @@ -17,7 +17,7 @@ class DebugEventHandler implements IEventHandler public function __construct() { - $this->callback = static function (EventArgument $argument) { + $this->callback = static function (EventArgument $argument): void { // todo: log in database }; } diff --git a/src/Pecee/SimpleRouter/Route/IControllerRoute.php b/src/Pecee/SimpleRouter/Route/IControllerRoute.php index f5c879b..35e065e 100644 --- a/src/Pecee/SimpleRouter/Route/IControllerRoute.php +++ b/src/Pecee/SimpleRouter/Route/IControllerRoute.php @@ -2,7 +2,7 @@ namespace Pecee\SimpleRouter\Route; -interface IControllerRoute extends IRoute +interface IControllerRoute extends ILoadableRoute { /** * Get controller class-name diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 62da265..0932f31 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -67,7 +67,7 @@ abstract class Route implements IRoute /* Filter parameters with null-value */ if ($this->filterEmptyParams === true) { - $parameters = array_filter($parameters, static function ($var) { + $parameters = array_filter($parameters, static function ($var): bool { return ($var !== null); }); } @@ -82,6 +82,7 @@ abstract class Route implements IRoute } /* When the callback is a function */ + return $router->getClassLoader()->loadClosure($callback, $parameters); } @@ -280,7 +281,7 @@ abstract class Route implements IRoute } /** - * @return string|callable + * @return string|callable|null */ public function getCallback() { @@ -337,6 +338,11 @@ abstract class Route implements IRoute */ public function setNamespace(string $namespace): IRoute { + // Do not set namespace when class-hinting is used + if (is_array($this->callback) === true) { + return $this; + } + $ns = $this->getNamespace(); if ($ns !== null) { diff --git a/src/Pecee/SimpleRouter/Route/RouteController.php b/src/Pecee/SimpleRouter/Route/RouteController.php index 6c269b7..88c6597 100644 --- a/src/Pecee/SimpleRouter/Route/RouteController.php +++ b/src/Pecee/SimpleRouter/Route/RouteController.php @@ -52,7 +52,7 @@ class RouteController extends LoadableRoute implements IControllerRoute 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); + $found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, true); if ($found !== false) { $method = (string)$found; } @@ -67,7 +67,7 @@ class RouteController extends LoadableRoute implements IControllerRoute foreach (Request::$requestTypes as $requestType) { if (stripos($method, $requestType) === 0) { - $method = (string)substr($method, strlen($requestType)); + $method = substr($method, strlen($requestType)); break; } } diff --git a/src/Pecee/SimpleRouter/Route/RouteResource.php b/src/Pecee/SimpleRouter/Route/RouteResource.php index 5bbd41e..f8be0b7 100644 --- a/src/Pecee/SimpleRouter/Route/RouteResource.php +++ b/src/Pecee/SimpleRouter/Route/RouteResource.php @@ -54,7 +54,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute /* Remove method/type */ if (strpos($name, '.') !== false) { - $name = (string)substr($name, 0, strrpos($name, '.')); + $name = substr($name, 0, strrpos($name, '.')); } return (strtolower($this->name) === strtolower($name)); @@ -68,7 +68,7 @@ class RouteResource extends LoadableRoute implements IControllerRoute */ public function findUrl(?string $method = null, $parameters = null, ?string $name = null): string { - $url = array_search($name, $this->names, false); + $url = array_search($name, $this->names, true); if ($url !== false) { return rtrim($this->url . $this->urls[$url], '/') . '/'; } diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 6225479..37b979a 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -44,7 +44,7 @@ class Router /** * List of processed routes - * @var array + * @var array|ILoadableRoute[] */ protected $processedRoutes = []; @@ -63,7 +63,7 @@ class Router /** * Csrf verifier class - * @var BaseCsrfVerifier + * @var BaseCsrfVerifier|null */ protected $csrfVerifier; @@ -107,7 +107,7 @@ class Router /** * Class loader instance - * @var ClassLoader + * @var IClassLoader */ protected $classLoader; @@ -215,7 +215,7 @@ class Router $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()->getOriginalUrl() === '') { $this->debug('Halted route-processing as no valid route was found'); return; @@ -575,7 +575,6 @@ class Router 'name' => $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. */ @@ -593,7 +592,7 @@ class Router } /* Using @ is most definitely a controller@method or alias@method */ - if (is_string($name) === true && strpos($name, '@') !== false) { + if (strpos($name, '@') !== false) { [$controller, $method] = array_map('strtolower', explode('@', $name)); if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) { @@ -605,7 +604,7 @@ class Router /* Check if callback matches (if it's not a function) */ $callback = $route->getCallback(); - if (is_string($name) === true && is_string($callback) === true && is_callable($callback) === false && strpos($name, '@') !== false && strpos($callback, '@') !== false) { + if (is_string($callback) === true && is_callable($callback) === false && strpos($name, '@') !== false && strpos($callback, '@') !== false) { /* Check if the entire callback is matching */ if (strpos($callback, $name) === 0 || strtolower($callback) === strtolower($name)) { @@ -656,10 +655,6 @@ class Router 'getParams' => $getParams, ]); - if ($getParams !== null && is_array($getParams) === false) { - throw new InvalidArgumentException('Invalid type for getParams. Must be array or null'); - } - if ($name === '' && $parameters === '') { return new Url('/'); } @@ -703,21 +698,21 @@ class Router /* Loop through all the routes to see if we can find a match */ /* @var $route ILoadableRoute */ - foreach ($this->processedRoutes as $route) { + foreach ($this->processedRoutes as $processedRoute) { /* Check if the route contains the name/alias */ - if ($route->hasName($controller) === true) { + if ($processedRoute->hasName($controller) === true) { return $this->request ->getUrlCopy() - ->setPath($route->findUrl($method, $parameters, $name)) + ->setPath($processedRoute->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)) { + if ($processedRoute instanceof IControllerRoute && strtolower($processedRoute->getController()) === strtolower($controller)) { return $this->request ->getUrlCopy() - ->setPath($route->findUrl($method, $parameters, $name)) + ->setPath($processedRoute->findUrl($method, $parameters, $name)) ->setParams($getParams); } @@ -842,7 +837,7 @@ class Router /** * Get class loader * - * @return ClassLoader + * @return IClassLoader */ public function getClassLoader(): IClassLoader { diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 187a398..36aa73f 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -22,6 +22,7 @@ use Pecee\SimpleRouter\Exceptions\HttpException; use Pecee\SimpleRouter\Handlers\CallbackExceptionHandler; use Pecee\SimpleRouter\Handlers\IEventHandler; use Pecee\SimpleRouter\Route\IGroupRoute; +use Pecee\SimpleRouter\Route\ILoadableRoute; use Pecee\SimpleRouter\Route\IPartialGroupRoute; use Pecee\SimpleRouter\Route\IRoute; use Pecee\SimpleRouter\Route\RouteController; @@ -173,7 +174,7 @@ class SimpleRouter */ public static function redirect(string $where, string $to, int $httpCode = 301): IRoute { - return static::get($where, function () use ($to, $httpCode) { + return static::get($where, static function () use ($to, $httpCode): void { static::response()->redirect($to, $httpCode); }); } @@ -185,7 +186,7 @@ class SimpleRouter * @param string|array|Closure $callback * @param array|null $settings * - * @return RouteUrl + * @return RouteUrl|IRoute */ public static function get(string $url, $callback, array $settings = null): IRoute { @@ -198,7 +199,7 @@ class SimpleRouter * @param string $url * @param string|array|Closure $callback * @param array|null $settings - * @return RouteUrl + * @return RouteUrl|IRoute */ public static function post(string $url, $callback, array $settings = null): IRoute { @@ -211,7 +212,7 @@ class SimpleRouter * @param string $url * @param string|array|Closure $callback * @param array|null $settings - * @return RouteUrl + * @return RouteUrl|IRoute */ public static function put(string $url, $callback, array $settings = null): IRoute { @@ -224,7 +225,7 @@ class SimpleRouter * @param string $url * @param string|array|Closure $callback * @param array|null $settings - * @return RouteUrl + * @return RouteUrl|IRoute */ public static function patch(string $url, $callback, array $settings = null): IRoute { @@ -237,7 +238,7 @@ class SimpleRouter * @param string $url * @param string|array|Closure $callback * @param array|null $settings - * @return RouteUrl + * @return RouteUrl|IRoute */ public static function options(string $url, $callback, array $settings = null): IRoute { @@ -250,7 +251,7 @@ class SimpleRouter * @param string $url * @param string|array|Closure $callback * @param array|null $settings - * @return RouteUrl + * @return RouteUrl|IRoute */ public static function delete(string $url, $callback, array $settings = null): IRoute { @@ -262,15 +263,11 @@ class SimpleRouter * * @param array $settings * @param Closure $callback - * @return RouteGroup + * @return RouteGroup|IGroupRoute * @throws InvalidArgumentException */ public static function group(array $settings, Closure $callback): IGroupRoute { - if (is_callable($callback) === false) { - throw new InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); - } - $group = new RouteGroup(); $group->setCallback($callback); $group->setSettings($settings); @@ -287,15 +284,11 @@ class SimpleRouter * @param string $url * @param Closure $callback * @param array $settings - * @return RoutePartialGroup + * @return RoutePartialGroup|IPartialGroupRoute * @throws InvalidArgumentException */ public static function partialGroup(string $url, Closure $callback, array $settings = []): IPartialGroupRoute { - if (is_callable($callback) === false) { - throw new InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); - } - $settings['prefix'] = $url; $group = new RoutePartialGroup(); @@ -313,7 +306,7 @@ class SimpleRouter * @param string $url * @param string|array|Closure $callback * @param array|null $settings - * @return RouteUrl + * @return RouteUrl|IRoute * @see SimpleRouter::form */ public static function basic(string $url, $callback, array $settings = null): IRoute @@ -328,7 +321,7 @@ class SimpleRouter * @param string $url * @param string|array|Closure $callback * @param array|null $settings - * @return RouteUrl + * @return RouteUrl|IRoute * @see SimpleRouter::form */ public static function form(string $url, $callback, array $settings = null): IRoute @@ -499,7 +492,7 @@ class SimpleRouter /** * Prepends the default namespace to all new routes added. * - * @param IRoute $route + * @param ILoadableRoute|IRoute $route * @return IRoute */ public static function addDefaultNamespace(IRoute $route): IRoute diff --git a/tests/Pecee/SimpleRouter/Dummy/NSController.php b/tests/Pecee/SimpleRouter/Dummy/NSController.php new file mode 100644 index 0000000..fe8e33e --- /dev/null +++ b/tests/Pecee/SimpleRouter/Dummy/NSController.php @@ -0,0 +1,11 @@ +assertTrue(true); } + public function testDefaultNameSpaceOverload() + { + TestRouter::setDefaultNamespace('DefaultNamespace\\Controllers'); + TestRouter::get('/test', [\MyNamespace\NSController::class, 'method']); + + $result = TestRouter::debugOutput('/test'); + + $this->assertTrue( (bool)$result); + } + public function testSameRoutes() { TestRouter::get('/recipe', 'DummyController@method1')->name('add'); diff --git a/tests/TestRouter.php b/tests/TestRouter.php index 1a1b4c1..79509df 100644 --- a/tests/TestRouter.php +++ b/tests/TestRouter.php @@ -22,12 +22,14 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter { try { static::debugNoReset($testUrl, $testMethod); - } catch(\Exception $e) { + } catch (\Exception $e) { + static::$defaultNamespace = null; static::router()->reset(); throw $e; } - if($reset === true) { + if ($reset === true) { + static::$defaultNamespace = null; static::router()->reset(); }