diff --git a/libraries/classes/Routing.php b/libraries/classes/Routing.php index afff4babf7..5318d5fb67 100644 --- a/libraries/classes/Routing.php +++ b/libraries/classes/Routing.php @@ -9,9 +9,9 @@ use FastRoute\Dispatcher; use FastRoute\Dispatcher\GroupCountBased as DispatcherGroupCountBased; use FastRoute\RouteCollector; use FastRoute\RouteParser\Std as RouteParserStd; +use PhpMyAdmin\Controllers\HomeController; use PhpMyAdmin\Http\ServerRequest; use Psr\Container\ContainerInterface; -use RuntimeException; use function __; use function file_exists; @@ -28,6 +28,7 @@ use function var_export; use const CACHE_DIR; use const E_USER_WARNING; +use const ROOT_PATH; /** * Class used to warm up the routing cache and manage routing. @@ -73,13 +74,11 @@ class Routing // If skip cache is enabled, do not try to read the file // If no cache skipping then read it and use it if (! $skipCache && file_exists(self::ROUTES_CACHE_FILE)) { - /** @psalm-suppress MissingFile, UnresolvableInclude */ + /** @psalm-suppress MissingFile, UnresolvableInclude, MixedAssignment */ $dispatchData = require self::ROUTES_CACHE_FILE; - if (! is_array($dispatchData)) { - throw new RuntimeException('Invalid cache file "' . self::ROUTES_CACHE_FILE . '"'); + if (self::isRoutesCacheFileValid($dispatchData)) { + return new DispatcherGroupCountBased($dispatchData); } - - return new DispatcherGroupCountBased($dispatchData); } $routeCollector = new RouteCollector( @@ -191,4 +190,19 @@ class Routing $controller = $container->get($controllerName); $controller($request, $vars); } + + /** + * @param mixed $dispatchData + * + * @psalm-assert-if-true array[] $dispatchData + */ + private static function isRoutesCacheFileValid($dispatchData): bool + { + return is_array($dispatchData) + && isset($dispatchData[0], $dispatchData[1]) + && is_array($dispatchData[0]) && is_array($dispatchData[1]) + && isset($dispatchData[0]['GET']) && is_array($dispatchData[0]['GET']) + && isset($dispatchData[0]['GET']['/']) && is_string($dispatchData[0]['GET']['/']) + && $dispatchData[0]['GET']['/'] === HomeController::class; + } } diff --git a/test/classes/RoutingTest.php b/test/classes/RoutingTest.php index 3408311bea..ef8c412843 100644 --- a/test/classes/RoutingTest.php +++ b/test/classes/RoutingTest.php @@ -8,6 +8,11 @@ use FastRoute\Dispatcher; use PhpMyAdmin\Controllers\HomeController; use PhpMyAdmin\Routing; +use function copy; + +use const CACHE_DIR; +use const ROOT_PATH; + /** * @covers \PhpMyAdmin\Routing */ @@ -26,6 +31,52 @@ class RoutingTest extends AbstractTestCase ); } + public function testGetDispatcherWithValidCacheFile(): void + { + $GLOBALS['cfg']['environment'] = null; + + $this->assertDirectoryIsWritable(CACHE_DIR); + $this->assertTrue(copy( + ROOT_PATH . 'test/test_data/routes/routes-valid.cache.txt', + CACHE_DIR . 'routes.cache.php' + )); + + $dispatcher = Routing::getDispatcher(); + $this->assertInstanceOf(Dispatcher::class, $dispatcher); + $this->assertSame( + [Dispatcher::FOUND, HomeController::class, []], + $dispatcher->dispatch('GET', '/') + ); + + $this->assertFileEquals( + CACHE_DIR . 'routes.cache.php', + ROOT_PATH . 'test/test_data/routes/routes-valid.cache.txt' + ); + } + + public function testGetDispatcherWithInvalidCacheFile(): void + { + $GLOBALS['cfg']['environment'] = null; + + $this->assertDirectoryIsWritable(CACHE_DIR); + $this->assertTrue(copy( + ROOT_PATH . 'test/test_data/routes/routes-invalid.cache.txt', + CACHE_DIR . 'routes.cache.php' + )); + + $dispatcher = Routing::getDispatcher(); + $this->assertInstanceOf(Dispatcher::class, $dispatcher); + $this->assertSame( + [Dispatcher::FOUND, HomeController::class, []], + $dispatcher->dispatch('GET', '/') + ); + + $this->assertFileNotEquals( + CACHE_DIR . 'routes.cache.php', + ROOT_PATH . 'test/test_data/routes/routes-invalid.cache.txt' + ); + } + /** * Test for Routing::getCurrentRoute */ diff --git a/test/test_data/routes/routes-invalid.cache.txt b/test/test_data/routes/routes-invalid.cache.txt new file mode 100644 index 0000000000..90196e860e --- /dev/null +++ b/test/test_data/routes/routes-invalid.cache.txt @@ -0,0 +1,34 @@ + + array ( + 'GET' => + array ( + '' => + array ( + 0 => 'PhpMyAdmin\\Controllers\\HomeController', + 1 => 'index', + ), + '/' => + array ( + 0 => 'PhpMyAdmin\\Controllers\\HomeController', + 1 => 'index', + ), + ), + 'POST' => + array ( + '' => + array ( + 0 => 'PhpMyAdmin\\Controllers\\HomeController', + 1 => 'index', + ), + '/' => + array ( + 0 => 'PhpMyAdmin\\Controllers\\HomeController', + 1 => 'index', + ), + ), + ), + 1 => + array ( + ), +); \ No newline at end of file diff --git a/test/test_data/routes/routes-valid.cache.txt b/test/test_data/routes/routes-valid.cache.txt new file mode 100644 index 0000000000..8f828cba0c --- /dev/null +++ b/test/test_data/routes/routes-valid.cache.txt @@ -0,0 +1,18 @@ + + array ( + 'GET' => + array ( + '' => 'PhpMyAdmin\\Controllers\\HomeController', + '/' => 'PhpMyAdmin\\Controllers\\HomeController', + ), + 'POST' => + array ( + '' => 'PhpMyAdmin\\Controllers\\HomeController', + '/' => 'PhpMyAdmin\\Controllers\\HomeController', + ), + ), + 1 => + array ( + ), +); \ No newline at end of file