diff --git a/.gitignore b/.gitignore index 9bed37c..59a1f8b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ composer.lock coverage/ *.sublime* clover.xml +phpcs.xml diff --git a/composer.json b/composer.json index 2be3e4b..9384c12 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,10 @@ "test-coverage:win": "del clover.xml && phpunit --coverage-html=coverage --coverage-clover=clover.xml && coverage-check clover.xml 100", "lint": "phpstan --no-progress -cphpstan.neon", "beautify": "phpcbf --standard=phpcs.xml", - "phpcs": "phpcs --standard=phpcs.xml -n" + "phpcs": "phpcs --standard=phpcs.xml -n", + "post-install-cmd": [ + "php -r \"if (!file_exists('phpcs.xml')) copy('phpcs.xml.dist', 'phpcs.xml');\"" + ] }, "suggest": { "latte/latte": "Latte template engine", diff --git a/flight/core/Dispatcher.php b/flight/core/Dispatcher.php index 6a167a8..1d40f95 100644 --- a/flight/core/Dispatcher.php +++ b/flight/core/Dispatcher.php @@ -4,10 +4,12 @@ declare(strict_types=1); namespace flight\core; -use Closure; use Exception; use flight\Engine; use InvalidArgumentException; +use Psr\Container\ContainerInterface; +use ReflectionFunction; +use Throwable; use TypeError; /** @@ -23,41 +25,54 @@ class Dispatcher { public const FILTER_BEFORE = 'before'; public const FILTER_AFTER = 'after'; - private const FILTER_TYPES = [self::FILTER_BEFORE, self::FILTER_AFTER]; - /** @var mixed $containerException Exception message if thrown by setting the container as a callable method */ - protected $containerException = null; + /** Exception message if thrown by setting the container as a callable method. */ + protected ?Throwable $containerException = null; - /** @var ?Engine $engine Engine instance */ + /** @var ?Engine $engine Engine instance. */ protected ?Engine $engine = null; - /** @var array Mapped events. */ + /** @var array Mapped events. */ protected array $events = []; /** * Method filters. * - * @var array &$params, mixed &$output): (void|false)>>> + * @var array &$params, mixed &$output): (void|false)>>> */ protected array $filters = []; /** * This is a container for the dependency injection. * - * @var callable|object|null + * @var null|ContainerInterface|(callable(string $classString, array $params): (null|object)) */ protected $containerHandler = null; /** * Sets the dependency injection container handler. * - * @param callable|object $containerHandler Dependency injection container + * @param ContainerInterface|(callable(string $classString, array $params): (null|object)) $containerHandler + * Dependency injection container. * - * @return void + * @throws InvalidArgumentException If $containerHandler is not a `callable` or instance of `Psr\Container\ContainerInterface`. */ public function setContainerHandler($containerHandler): void { - $this->containerHandler = $containerHandler; + $containerInterfaceNS = '\Psr\Container\ContainerInterface'; + + if ( + is_a($containerHandler, $containerInterfaceNS) + || is_callable($containerHandler) + ) { + $this->containerHandler = $containerHandler; + + return; + } + + throw new InvalidArgumentException( + "\$containerHandler must be of type callable or instance $containerInterfaceNS" + ); } public function setEngine(Engine $engine): void @@ -68,11 +83,11 @@ class Dispatcher /** * Dispatches an event. * - * @param string $name Event name + * @param string $name Event name. * @param array $params Callback parameters. * * @return mixed Output of callback - * @throws Exception If event name isn't found or if event throws an `Exception` + * @throws Exception If event name isn't found or if event throws an `Exception`. */ public function run(string $name, array $params = []) { @@ -110,7 +125,7 @@ class Dispatcher $requestedMethod = $this->get($eventName); if ($requestedMethod === null) { - throw new Exception("Event '{$eventName}' isn't found."); + throw new Exception("Event '$eventName' isn't found."); } return $this->execute($requestedMethod, $params); @@ -138,8 +153,8 @@ class Dispatcher /** * Assigns a callback to an event. * - * @param string $name Event name - * @param Closure(): (void|mixed) $callback Callback function + * @param string $name Event name. + * @param callable(): (void|mixed) $callback Callback function. * * @return $this */ @@ -153,9 +168,9 @@ class Dispatcher /** * Gets an assigned callback. * - * @param string $name Event name + * @param string $name Event name. * - * @return null|(Closure(): (void|mixed)) $callback Callback function + * @return null|(callable(): (void|mixed)) $callback Callback function. */ public function get(string $name): ?callable { @@ -165,9 +180,9 @@ class Dispatcher /** * Checks if an event has been set. * - * @param string $name Event name + * @param string $name Event name. * - * @return bool Event status + * @return bool If event exists or doesn't exists. */ public function has(string $name): bool { @@ -177,7 +192,7 @@ class Dispatcher /** * Clears an event. If no name is given, all events will be removed. * - * @param ?string $name Event name + * @param ?string $name Event name. */ public function clear(?string $name = null): void { @@ -188,27 +203,38 @@ class Dispatcher return; } - $this->events = []; - $this->filters = []; + $this->reset(); } /** * Hooks a callback to an event. * * @param string $name Event name - * @param 'before'|'after' $type Filter type - * @param Closure(array &$params, string &$output): (void|false) $callback + * @param 'before'|'after' $type Filter type. + * @param callable(array &$params, mixed &$output): (void|false)|callable(mixed &$output): (void|false) $callback * * @return $this */ public function hook(string $name, string $type, callable $callback): self { - if (!in_array($type, self::FILTER_TYPES, true)) { - $noticeMessage = "Invalid filter type '$type', use " . join('|', self::FILTER_TYPES); + static $filterTypes = [self::FILTER_BEFORE, self::FILTER_AFTER]; + + if (!in_array($type, $filterTypes, true)) { + $noticeMessage = "Invalid filter type '$type', use " . join('|', $filterTypes); trigger_error($noticeMessage, E_USER_NOTICE); } + if ($type === self::FILTER_AFTER) { + $callbackInfo = new ReflectionFunction($callback); + $parametersNumber = $callbackInfo->getNumberOfParameters(); + + if ($parametersNumber === 1) { + /** @disregard &$params in after filters are deprecated. */ + $callback = fn (array &$params, &$output) => $callback($output); + } + } + $this->filters[$name][$type][] = $callback; return $this; @@ -217,10 +243,10 @@ class Dispatcher /** * Executes a chain of method filters. * - * @param array &$params, mixed &$output): (void|false)> $filters - * Chain of filters- - * @param array $params Method parameters - * @param mixed $output Method output + * @param array &$params, mixed &$output): (void|false)> $filters + * Chain of filters. + * @param array $params Method parameters. + * @param mixed $output Method output. * * @throws Exception If an event throws an `Exception` or if `$filters` contains an invalid filter. */ @@ -242,16 +268,19 @@ class Dispatcher /** * Executes a callback function. * - * @param callable-string|(Closure(): mixed)|array{class-string|object, string} $callback - * Callback function - * @param array $params Function parameters + * @param callable-string|(callable(): mixed)|array{class-string|object, string} $callback + * Callback function. + * @param array $params Function parameters. * - * @return mixed Function results + * @return mixed Function results. * @throws Exception If `$callback` also throws an `Exception`. */ public function execute($callback, array &$params = []) { - if (is_string($callback) === true && (strpos($callback, '->') !== false || strpos($callback, '::') !== false)) { + if ( + is_string($callback) === true + && (strpos($callback, '->') !== false || strpos($callback, '::') !== false) + ) { $callback = $this->parseStringClassAndMethod($callback); } @@ -263,28 +292,26 @@ class Dispatcher * * @param string $classAndMethod Class and method * - * @return array{class-string|object, string} Class and method + * @return array{0: class-string|object, 1: string} Class and method */ public function parseStringClassAndMethod(string $classAndMethod): array { - $class_parts = explode('->', $classAndMethod); - if (count($class_parts) === 1) { - $class_parts = explode('::', $class_parts[0]); - } + $classParts = explode('->', $classAndMethod); - $class = $class_parts[0]; - $method = $class_parts[1]; + if (count($classParts) === 1) { + $classParts = explode('::', $classParts[0]); + } - return [ $class, $method ]; + return $classParts; } /** * Calls a function. * - * @param callable $func Name of function to call - * @param array &$params Function parameters + * @param callable $func Name of function to call. + * @param array &$params Function parameters. * - * @return mixed Function results + * @return mixed Function results. * @deprecated 3.7.0 Use invokeCallable instead */ public function callFunction(callable $func, array &$params = []) @@ -295,12 +322,12 @@ class Dispatcher /** * Invokes a method. * - * @param array{class-string|object, string} $func Class method - * @param array &$params Class method parameters + * @param array{0: class-string|object, 1: string} $func Class method. + * @param array &$params Class method parameters. * - * @return mixed Function results + * @return mixed Function results. * @throws TypeError For nonexistent class name. - * @deprecated 3.7.0 Use invokeCallable instead + * @deprecated 3.7.0 Use invokeCallable instead. */ public function invokeMethod(array $func, array &$params = []) { @@ -310,12 +337,12 @@ class Dispatcher /** * Invokes a callable (anonymous function or Class->method). * - * @param array{class-string|object, string}|Callable $func Class method - * @param array &$params Class method parameters + * @param array{0: class-string|object, 1: string}|callable $func Class method. + * @param array &$params Class method parameters. * - * @return mixed Function results + * @return mixed Function results. * @throws TypeError For nonexistent class name. - * @throws InvalidArgumentException If the constructor requires parameters + * @throws InvalidArgumentException If the constructor requires parameters. * @version 3.7.0 */ public function invokeCallable($func, array &$params = []) @@ -323,56 +350,46 @@ class Dispatcher // If this is a directly callable function, call it if (is_array($func) === false) { $this->verifyValidFunction($func); + return call_user_func_array($func, $params); } [$class, $method] = $func; - $resolvedClass = null; - // Only execute the container handler if it's not a Flight class - if ( - $this->containerHandler !== null && - ( - ( - is_object($class) === true && - strpos(get_class($class), 'flight\\') === false - ) || - is_string($class) === true - ) - ) { - $containerHandler = $this->containerHandler; - $resolvedClass = $this->resolveContainerClass($containerHandler, $class, $params); - if ($resolvedClass !== null) { + $mustUseTheContainer = $this->containerHandler !== null && ( + (is_object($class) === true && strpos(get_class($class), 'flight\\') === false) + || is_string($class) + ); + + if ($mustUseTheContainer === true) { + $resolvedClass = $this->resolveContainerClass($class, $params); + + if ($resolvedClass) { $class = $resolvedClass; } } - $this->verifyValidClassCallable($class, $method, $resolvedClass); + $this->verifyValidClassCallable($class, $method, $resolvedClass ?? null); // Class is a string, and method exists, create the object by hand and inject only the Engine - if (is_string($class) === true) { + if (is_string($class)) { $class = new $class($this->engine); } - return call_user_func_array([ $class, $method ], $params); + return call_user_func_array([$class, $method], $params); } /** * Handles invalid callback types. * - * @param callable-string|(Closure(): mixed)|array{class-string|object, string} $callback - * Callback function + * @param callable-string|(callable(): mixed)|array{0: class-string|object, 1: string} $callback + * Callback function. * - * @throws InvalidArgumentException If `$callback` is an invalid type + * @throws InvalidArgumentException If `$callback` is an invalid type. */ protected function verifyValidFunction($callback): void { - $isInvalidFunctionName = ( - is_string($callback) - && !function_exists($callback) - ); - - if ($isInvalidFunctionName) { + if (is_string($callback) && !function_exists($callback)) { throw new InvalidArgumentException('Invalid callback specified.'); } } @@ -381,84 +398,77 @@ class Dispatcher /** * Verifies if the provided class and method are valid callable. * - * @param string|object $class The class name. + * @param class-string|object $class The class name. * @param string $method The method name. * @param object|null $resolvedClass The resolved class. * * @throws Exception If the class or method is not found. - * - * @return void */ protected function verifyValidClassCallable($class, $method, $resolvedClass): void { - $final_exception = null; + $exception = null; // Final check to make sure it's actually a class and a method, or throw an error if (is_object($class) === false && class_exists($class) === false) { - $final_exception = new Exception("Class '$class' not found. Is it being correctly autoloaded with Flight::path()?"); + $exception = new Exception("Class '$class' not found. Is it being correctly autoloaded with Flight::path()?"); - // If this tried to resolve a class in a container and failed somehow, throw the exception - } elseif (isset($resolvedClass) === false && $this->containerException !== null) { - $final_exception = $this->containerException; + // If this tried to resolve a class in a container and failed somehow, throw the exception + } elseif (!$resolvedClass && $this->containerException !== null) { + $exception = $this->containerException; - // Class is there, but no method + // Class is there, but no method } elseif (is_object($class) === true && method_exists($class, $method) === false) { - $final_exception = new Exception("Class found, but method '" . get_class($class) . "::$method' not found."); + $classNamespace = get_class($class); + $exception = new Exception("Class found, but method '$classNamespace::$method' not found."); } - if ($final_exception !== null) { + if ($exception !== null) { $this->fixOutputBuffering(); - throw $final_exception; + + throw $exception; } } /** * Resolves the container class. * - * @param callable|object $container_handler Dependency injection container - * @param class-string $class Class name - * @param array &$params Class constructor parameters + * @param class-string $class Class name. + * @param array &$params Class constructor parameters. * - * @return object Class object + * @return ?object Class object. */ - protected function resolveContainerClass($container_handler, $class, array &$params) + protected function resolveContainerClass(string $class, array &$params) { - $class_object = null; - // PSR-11 if ( - is_object($container_handler) === true && - method_exists($container_handler, 'has') === true && - $container_handler->has($class) + is_a($this->containerHandler, '\Psr\Container\ContainerInterface') + && $this->containerHandler->has($class) ) { - $class_object = call_user_func([$container_handler, 'get'], $class); + return $this->containerHandler->get($class); + } // Just a callable where you configure the behavior (Dice, PHP-DI, etc.) - } elseif (is_callable($container_handler) === true) { - // This is to catch all the error that could be thrown by whatever container you are using + if (is_callable($this->containerHandler)) { + /* This is to catch all the error that could be thrown by whatever + container you are using */ try { - $class_object = call_user_func($container_handler, $class, $params); - } catch (Exception $e) { - // could not resolve a class for some reason - $class_object = null; + return ($this->containerHandler)($class, $params); + // could not resolve a class for some reason + } catch (Exception $exception) { // If the container throws an exception, we need to catch it // and store it somewhere. If we just let it throw itself, it // doesn't properly close the output buffers and can cause other // issues. - // This is thrown in the verifyValidClassCallable method - $this->containerException = $e; + // This is thrown in the verifyValidClassCallable method. + $this->containerException = $exception; } } - return $class_object; + return null; } - /** - * Because this could throw an exception in the middle of an output buffer, - * - * @return void - */ + /** Because this could throw an exception in the middle of an output buffer, */ protected function fixOutputBuffering(): void { // Cause PHPUnit has 1 level of output buffering by default diff --git a/flight/net/Response.php b/flight/net/Response.php index e1abaac..2971bd9 100644 --- a/flight/net/Response.php +++ b/flight/net/Response.php @@ -139,7 +139,7 @@ class Response */ public function status(?int $code = null) { - if (null === $code) { + if ($code === null) { return $this->status; } @@ -279,19 +279,22 @@ class Response */ public function cache($expires): self { - if (false === $expires) { + if ($expires === false) { $this->headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT'; + $this->headers['Cache-Control'] = [ 'no-store, no-cache, must-revalidate', 'post-check=0, pre-check=0', 'max-age=0', ]; + $this->headers['Pragma'] = 'no-cache'; } else { $expires = \is_int($expires) ? $expires : strtotime($expires); $this->headers['Expires'] = gmdate('D, d M Y H:i:s', $expires) . ' GMT'; $this->headers['Cache-Control'] = 'max-age=' . ($expires - time()); - if (isset($this->headers['Pragma']) && 'no-cache' == $this->headers['Pragma']) { + + if (isset($this->headers['Pragma']) && $this->headers['Pragma'] === 'no-cache') { unset($this->headers['Pragma']); } } @@ -307,7 +310,7 @@ class Response public function sendHeaders(): self { // Send status code header - if (false !== strpos(\PHP_SAPI, 'cgi')) { + if (strpos(\PHP_SAPI, 'cgi') !== false) { // @codeCoverageIgnoreStart $this->setRealHeader( sprintf( diff --git a/flight/net/Route.php b/flight/net/Route.php index 0b8b9d7..57ba2fc 100644 --- a/flight/net/Route.php +++ b/flight/net/Route.php @@ -105,7 +105,7 @@ class Route public function matchUrl(string $url, bool $case_sensitive = false): bool { // Wildcard or exact match - if ('*' === $this->pattern || $this->pattern === $url) { + if ($this->pattern === '*' || $this->pattern === $url) { return true; } @@ -120,8 +120,9 @@ class Route for ($i = 0; $i < $len; $i++) { if ($url[$i] === '/') { - $n++; + ++$n; } + if ($n === $count) { break; } @@ -153,24 +154,20 @@ class Route $regex ); - if ('/' === $last_char) { // Fix trailing slash - $regex .= '?'; - } else { // Allow trailing slash - $regex .= '/?'; - } + $regex .= $last_char === '/' ? '?' : '/?'; // Attempt to match route and named parameters - if (preg_match('#^' . $regex . '(?:\?[\s\S]*)?$#' . (($case_sensitive) ? '' : 'i'), $url, $matches)) { - foreach ($ids as $k => $v) { - $this->params[$k] = (\array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null; - } - - $this->regex = $regex; + if (!preg_match('#^' . $regex . '(?:\?[\s\S]*)?$#' . (($case_sensitive) ? '' : 'i'), $url, $matches)) { + return false; + } - return true; + foreach (array_keys($ids) as $k) { + $this->params[$k] = (\array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null; } - return false; + $this->regex = $regex; + + return true; } /** diff --git a/flight/template/View.php b/flight/template/View.php index d1bc07f..c43a35f 100644 --- a/flight/template/View.php +++ b/flight/template/View.php @@ -88,7 +88,7 @@ class View */ public function clear(?string $key = null): self { - if (null === $key) { + if ($key === null) { $this->vars = []; } else { unset($this->vars[$key]); @@ -169,7 +169,7 @@ class View $is_windows = \strtoupper(\substr(PHP_OS, 0, 3)) === 'WIN'; - if (('/' == \substr($file, 0, 1)) || ($is_windows === true && ':' == \substr($file, 1, 1))) { + if ((\substr($file, 0, 1) === '/') || ($is_windows && \substr($file, 1, 1) === ':')) { return $file; } diff --git a/flight/util/Collection.php b/flight/util/Collection.php index 6ffe0b5..e17ed37 100644 --- a/flight/util/Collection.php +++ b/flight/util/Collection.php @@ -95,7 +95,7 @@ class Collection implements ArrayAccess, Iterator, Countable, JsonSerializable #[\ReturnTypeWillChange] public function offsetSet($offset, $value): void { - if (null === $offset) { + if ($offset === null) { $this->data[] = $value; } else { $this->data[$offset] = $value; @@ -166,9 +166,7 @@ class Collection implements ArrayAccess, Iterator, Countable, JsonSerializable */ public function valid(): bool { - $key = key($this->data); - - return null !== $key; + return key($this->data) !== null; } /** diff --git a/flight/util/ReturnTypeWillChange.php b/flight/util/ReturnTypeWillChange.php new file mode 100644 index 0000000..1eba39e --- /dev/null +++ b/flight/util/ReturnTypeWillChange.php @@ -0,0 +1,8 @@ + - + - + - + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + flight/ tests/ diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9fa597c..e69de29 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,6 +0,0 @@ -parameters: - ignoreErrors: - - - message: "#^Parameter \\#2 \\$callback of method flight\\\\core\\\\Dispatcher\\:\\:set\\(\\) expects Closure\\(\\)\\: mixed, array\\{\\$this\\(flight\\\\Engine\\), literal\\-string&non\\-falsy\\-string\\} given\\.$#" - count: 1 - path: flight/Engine.php diff --git a/tests/DispatcherTest.php b/tests/DispatcherTest.php index a755666..418897d 100644 --- a/tests/DispatcherTest.php +++ b/tests/DispatcherTest.php @@ -37,9 +37,7 @@ class DispatcherTest extends TestCase public function testFunctionMapping(): void { - $this->dispatcher->set('map2', function (): string { - return 'hello'; - }); + $this->dispatcher->set('map2', fn (): string => 'hello'); $this->assertSame('hello', $this->dispatcher->run('map2')); } @@ -61,6 +59,9 @@ class DispatcherTest extends TestCase ->set('map-event', $customFunction) ->set('map-event-2', $anotherFunction); + $this->assertTrue($this->dispatcher->has('map-event')); + $this->assertTrue($this->dispatcher->has('map-event-2')); + $this->dispatcher->clear(); $this->assertFalse($this->dispatcher->has('map-event')); @@ -76,6 +77,9 @@ class DispatcherTest extends TestCase ->set('map-event', $customFunction) ->set('map-event-2', $anotherFunction); + $this->assertTrue($this->dispatcher->has('map-event')); + $this->assertTrue($this->dispatcher->has('map-event-2')); + $this->dispatcher->clear('map-event'); $this->assertFalse($this->dispatcher->has('map-event')); @@ -105,9 +109,7 @@ class DispatcherTest extends TestCase public function testBeforeAndAfter(): void { - $this->dispatcher->set('hello', function (string $name): string { - return "Hello, $name!"; - }); + $this->dispatcher->set('hello', fn (string $name): string => "Hello, $name!"); $this->dispatcher ->hook('hello', Dispatcher::FILTER_BEFORE, function (array &$params): void { @@ -124,6 +126,25 @@ class DispatcherTest extends TestCase $this->assertSame('Hello, Fred! Have a nice day!', $result); } + public function testBeforeAndAfterWithShortAfterFilterSyntax(): void + { + $this->dispatcher->set('hello', fn (string $name): string => "Hello, $name!"); + + $this->dispatcher + ->hook('hello', Dispatcher::FILTER_BEFORE, function (array &$params): void { + // Manipulate the parameter + $params[0] = 'Fred'; + }) + ->hook('hello', Dispatcher::FILTER_AFTER, function (string &$output): void { + // Manipulate the output + $output .= ' Have a nice day!'; + }); + + $result = $this->dispatcher->run('hello', ['Bob']); + + $this->assertSame('Hello, Fred! Have a nice day!', $result); + } + public function testInvalidCallback(): void { $this->expectException(Exception::class); @@ -245,7 +266,7 @@ class DispatcherTest extends TestCase public function testInvokeMethod(): void { $class = new TesterClass('param1', 'param2', 'param3', 'param4', 'param5', 'param6'); - $result = $this->dispatcher->invokeMethod([ $class, 'instanceMethod' ]); + $result = $this->dispatcher->invokeMethod([$class, 'instanceMethod']); $this->assertSame('param1', $class->param2); } @@ -271,7 +292,7 @@ class DispatcherTest extends TestCase public function testExecuteStringClassNoConstructArraySyntax(): void { - $result = $this->dispatcher->execute([ Hello::class, 'sayHi' ]); + $result = $this->dispatcher->execute([Hello::class, 'sayHi']); $this->assertSame('hello', $result); } @@ -298,7 +319,7 @@ class DispatcherTest extends TestCase $engine = new Engine(); $engine->set('test_me_out', 'You got it boss!'); $this->dispatcher->setEngine($engine); - $result = $this->dispatcher->execute([ ContainerDefault::class, 'testTheContainer' ]); + $result = $this->dispatcher->execute([ContainerDefault::class, 'testTheContainer']); $this->assertSame('You got it boss!', $result); } @@ -306,6 +327,6 @@ class DispatcherTest extends TestCase { $this->expectException(TypeError::class); $this->expectExceptionMessageMatches('#tests\\\\classes\\\\ContainerDefault::__construct\(\).+flight\\\\Engine, null given#'); - $result = $this->dispatcher->execute([ ContainerDefault::class, 'testTheContainer' ]); + $result = $this->dispatcher->execute([ContainerDefault::class, 'testTheContainer']); } } diff --git a/tests/RedirectTest.php b/tests/RedirectTest.php index e44186e..07698eb 100644 --- a/tests/RedirectTest.php +++ b/tests/RedirectTest.php @@ -21,7 +21,7 @@ class RedirectTest extends TestCase public function getBaseUrl($base, $url) { - if ('/' !== $base && false === strpos($url, '://')) { + if ($base !== '/' && strpos($url, '://') === false) { $url = preg_replace('#/+#', '/', $base . '/' . $url); } @@ -67,11 +67,7 @@ class RedirectTest extends TestCase public function testBaseOverride() { $url = 'login'; - if (null !== $this->app->get('flight.base_url')) { - $base = $this->app->get('flight.base_url'); - } else { - $base = $this->app->request()->base; - } + $base = $this->app->get('flight.base_url') ?? $this->app->request()->base; self::assertEquals('/testdir/login', $this->getBaseUrl($base, $url)); } diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 2314426..8906168 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -336,12 +336,14 @@ class RouterTest extends TestCase { $this->router->map('GET /api/intune/hey', [$this, 'ok']); + $error_description = 'error_description=AADSTS65004%3a+User+declined+to+consent+to+access+the'; + $error_description .= '+app.%0d%0aTrace+ID%3a+747c0cc1-ccbd-4e53-8e2f-48812eb24100%0d%0a'; + $error_description .= 'Correlation+ID%3a+362e3cb3-20ef-400b-904e-9983bd989184%0d%0a'; + $error_description .= 'Timestamp%3a+2022-09-08+09%3a58%3a12Z'; + $query_params = [ 'error=access_denied', - 'error_description=AADSTS65004%3a+User+declined+to+consent+to+access+the' - . '+app.%0d%0aTrace+ID%3a+747c0cc1-ccbd-4e53-8e2f-48812eb24100%0d%0a' - . 'Correlation+ID%3a+362e3cb3-20ef-400b-904e-9983bd989184%0d%0a' - . 'Timestamp%3a+2022-09-08+09%3a58%3a12Z', + $error_description, 'error_uri=https%3a%2f%2flogin.microsoftonline.com%2ferror%3fcode%3d65004', 'admin_consent=True', 'state=x2EUE0fcSj#'