diff --git a/src/Illuminate/Foundation/Console/RouteCacheCommand.php b/src/Illuminate/Foundation/Console/RouteCacheCommand.php index 9b8632af50b2..843a6909ee68 100644 --- a/src/Illuminate/Foundation/Console/RouteCacheCommand.php +++ b/src/Illuminate/Foundation/Console/RouteCacheCommand.php @@ -5,6 +5,7 @@ use Illuminate\Console\Command; use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract; use Illuminate\Filesystem\Filesystem; +use Illuminate\Routing\Route; use Illuminate\Routing\RouteCollection; use Symfony\Component\Console\Attribute\AsCommand; @@ -59,9 +60,7 @@ public function handle() return $this->components->error("Your application doesn't have any routes."); } - foreach ($routes as $route) { - $route->prepareForSerialization(); - } + $routes = $this->prepareRoutes($routes); $this->files->put( $this->laravel->getCachedRoutesPath(), $this->buildRouteCacheFile($routes) @@ -70,6 +69,21 @@ public function handle() $this->components->info('Routes cached successfully.'); } + protected function prepareRoutes(RouteCollection $routes) + { + $collection = new RouteCollection; + + foreach ($routes as $route) { + $collection->add(transform(clone $route, static function (Route $route) { + $route->prepareForSerialization(); + + return $route; + })); + } + + return $collection; + } + /** * Boot a fresh copy of the application and get the routes. * diff --git a/tests/Integration/Foundation/Console/Fixtures/app.php b/tests/Integration/Foundation/Console/Fixtures/app.php new file mode 100644 index 000000000000..75802102133d --- /dev/null +++ b/tests/Integration/Foundation/Console/Fixtures/app.php @@ -0,0 +1,18 @@ +create(); +} + +return AppCache::$app ??= Application::configure(basePath: __DIR__)->create(); diff --git a/tests/Integration/Foundation/Console/Fixtures/bootstrap/cache/.gitignore b/tests/Integration/Foundation/Console/Fixtures/bootstrap/cache/.gitignore new file mode 100644 index 000000000000..d6b7ef32c847 --- /dev/null +++ b/tests/Integration/Foundation/Console/Fixtures/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Integration/Foundation/Console/Fixtures/cache/.gitignore b/tests/Integration/Foundation/Console/Fixtures/cache/.gitignore new file mode 100644 index 000000000000..d6b7ef32c847 --- /dev/null +++ b/tests/Integration/Foundation/Console/Fixtures/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Integration/Foundation/Console/RouteCacheCommandTest.php b/tests/Integration/Foundation/Console/RouteCacheCommandTest.php new file mode 100644 index 000000000000..00408e3b3d6f --- /dev/null +++ b/tests/Integration/Foundation/Console/RouteCacheCommandTest.php @@ -0,0 +1,121 @@ +app->useBootstrapPath(__DIR__.'/Fixtures'); + + $app = (static function () { + $refresh = true; + + return require __DIR__.'/Fixtures/app.php'; + })(); + + $app['router']->get('/posts', [RouteCacheCommandTestController::class, 'index'])->name('posts.index'); + + $this->artisan('route:cache') + ->assertSuccessful() + ->expectsOutputToContain('Routes cached successfully.'); + + $this->assertFileExists(__DIR__.'/Fixtures/cache/routes-v7.php'); + + foreach ($app['router']->getRoutes() as $route) { + try { + $route->getController(); + $route->gatherMiddleware(); + } catch (LogicException $exception) { + $this->fail(sprintf( + 'Route [%s] is no longer analyzable after route:cache: %s', + $route->uri(), + $exception->getMessage(), + )); + } + } + } + + public function testOptimizeCanRunTasksThatAnalyzeRoutesAfterRouteCache(): void + { + $this->withoutDeprecationHandling(); + + $this->app->useBootstrapPath(__DIR__.'/Fixtures'); + + $app = (static function () { + $refresh = true; + + return require __DIR__.'/Fixtures/app.php'; + })(); + + $app['router']->get('/posts', [RouteCacheCommandTestController::class, 'index'])->name('posts.index'); + + $this->app->register(RouteAnalysisOptimizeServiceProvider::class); + + $this->artisan('optimize', ['--except' => 'config,events,views']) + ->assertSuccessful() + ->expectsOutputToContain('route analysis succeeded'); + } +} + +class RouteCacheCommandTestController extends Controller +{ + public function index(): string + { + return 'posts'; + } +} + +class RouteAnalysisOptimizeServiceProvider extends ServiceProvider +{ + public function boot(): void + { + $this->commands([ + RouteAnalysisOptimizeCommand::class, + ]); + + $this->optimizes( + optimize: 'test:route-analysis', + key: 'route-analysis', + ); + } +} + +class RouteAnalysisOptimizeCommand extends Command +{ + protected $signature = 'test:route-analysis'; + + protected $description = 'Analyze routes after route:cache'; + + public function handle(): int + { + foreach (AppCache::$app['router']->getRoutes() as $route) { + $route->gatherMiddleware(); + } + + $this->components->info('route analysis succeeded'); + + return self::SUCCESS; + } +}