Skip to content

Commit 03744a2

Browse files
committed
refs #10778 add router cache
1 parent 48b500e commit 03744a2

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

src/Routing/Middleware/RoutingMiddleware.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
*/
1515
namespace Cake\Routing\Middleware;
1616

17+
=======
18+
use Cake\Cache\Cache;
19+
use Cake\Core\Configure;
1720
use Cake\Core\PluginApplicationInterface;
1821
use Cake\Http\BaseApplication;
1922
use Cake\Http\MiddlewareQueue;
@@ -30,6 +33,16 @@
3033
*/
3134
class RoutingMiddleware
3235
{
36+
/**
37+
* Name of the default cache configuration name used to store routes collection
38+
*/
39+
const DEFAULT_ROUTER_CACHE_CONFIG = '_cake_router_';
40+
41+
/**
42+
* Key used to store the route collection in the cache engine
43+
*/
44+
const ROUTE_COLLECTION_CACHE_KEY = 'routeCollection';
45+
3346
/**
3447
* The application that will have its routing hook invoked.
3548
*
@@ -49,6 +62,7 @@ public function __construct(BaseApplication $app = null)
4962

5063
/**
5164
* Trigger the application's routes() hook if the application exists and Router isn't initialized.
65+
* Uses the routes cache if enabled via configuration param "Router.cache"
5266
*
5367
* If the middleware is created without an Application, routes will be
5468
* loaded via the automatic route loading that pre-dates the routes() hook.
@@ -60,11 +74,46 @@ protected function loadRoutes()
6074
if (!$this->app) {
6175
return;
6276
}
77+
$routeCollection = $this->buildRouteCollection();
78+
Router::setRouteCollection($routeCollection);
79+
// Prevent routes from being loaded again
80+
Router::$initialized = true;
81+
}
82+
83+
/**
84+
* Check if route cache is enabled and use the configured Cache to 'remember' the route collection
85+
*
86+
* @return \Cake\Routing\RouteCollection
87+
*/
88+
protected function buildRouteCollection()
89+
{
90+
$isRouterCacheEnabled = Configure::read('Router.cache');
91+
if (Cache::enabled() && $isRouterCacheEnabled) {
92+
$routesCacheConfig = Configure::read('Router.cacheConfig', self::DEFAULT_ROUTER_CACHE_CONFIG);
93+
94+
return Cache::remember(self::ROUTE_COLLECTION_CACHE_KEY, function () {
95+
96+
return $this->prepareRouteCollection();
97+
}, $routesCacheConfig);
98+
}
99+
100+
return $this->prepareRouteCollection();
101+
}
102+
103+
/**
104+
* Generate the route collection using the builder
105+
*
106+
* @return \Cake\Routing\RouteCollection
107+
*/
108+
protected function prepareRouteCollection()
109+
{
63110
$builder = Router::createRouteBuilder('/');
64111
$this->app->routes($builder);
65112
if ($this->app instanceof PluginApplicationInterface) {
66113
$this->app->pluginRoutes($builder);
67114
}
115+
116+
return Router::getRouteCollection();
68117
}
69118

70119
/**
@@ -77,6 +126,7 @@ protected function loadRoutes()
77126
* @param \Psr\Http\Message\ResponseInterface $response The response.
78127
* @param callable $next The next middleware to call.
79128
* @return \Psr\Http\Message\ResponseInterface A response.
129+
* @throws \Cake\Routing\InvalidArgumentException
80130
*/
81131
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
82132
{

src/Routing/Router.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,17 @@ public static function getRouteCollection()
11481148
return static::$_collection;
11491149
}
11501150

1151+
/**
1152+
* Set the RouteCollection inside the Router
1153+
*
1154+
* @param RouteCollection $routeCollection
1155+
* @return void
1156+
*/
1157+
public static function setRouteCollection($routeCollection)
1158+
{
1159+
static::$_collection = $routeCollection;
1160+
}
1161+
11511162
/**
11521163
* Loads route configuration
11531164
*
@@ -1159,4 +1170,5 @@ protected static function _loadRoutes()
11591170
static::$initialized = true;
11601171
include CONFIG . 'routes.php';
11611172
}
1173+
11621174
}

tests/TestCase/Routing/Middleware/RoutingMiddlewareTest.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
*/
1515
namespace Cake\Test\TestCase\Routing\Middleware;
1616

17+
use Cake\Cache\Cache;
18+
use Cake\Core\Configure;
1719
use Cake\Routing\Middleware\RoutingMiddleware;
1820
use Cake\Routing\RouteBuilder;
1921
use Cake\Routing\Router;
@@ -454,4 +456,89 @@ public function scopedMiddlewareUrlProvider()
454456
['/api/version', ['second', 'last']],
455457
];
456458
}
459+
460+
/**
461+
* Test we store route collection in cache.
462+
*
463+
* @return void
464+
*/
465+
public function testCacheRoutes()
466+
{
467+
Configure::write('Router.cache', true);
468+
Cache::setConfig('_cake_router_', [
469+
'engine' => 'File',
470+
'path' => TMP,
471+
]);
472+
Router::$initialized = false;
473+
$request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/articles']);
474+
$response = new Response();
475+
$next = function ($req, $res) {
476+
$routeCollection = Cache::read('routeCollection', '_cake_router_');
477+
$this->assertInstanceOf('\Cake\Routing\RouteCollection', $routeCollection);
478+
return $res;
479+
};
480+
$app = new Application(CONFIG);
481+
$middleware = new RoutingMiddleware($app);
482+
$middleware($request, $response, $next);
483+
484+
Cache::clear(false, '_cake_router_');
485+
Cache::drop('_cake_router_');
486+
}
487+
488+
/**
489+
* Test we don't cache routes if cache is disabled.
490+
*
491+
* @return void
492+
*/
493+
public function testCacheNotUsedIfCacheDisabled()
494+
{
495+
Configure::write('Router.cache', true);
496+
Cache::disable();
497+
Cache::setConfig('_cake_router_', [
498+
'engine' => 'File',
499+
'path' => TMP,
500+
]);
501+
Router::$initialized = false;
502+
$request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/articles']);
503+
$response = new Response();
504+
$next = function ($req, $res) {
505+
$routeCollection = Cache::read('routeCollection', '_cake_router_');
506+
$this->assertFalse($routeCollection);
507+
return $res;
508+
};
509+
$app = new Application(CONFIG);
510+
$middleware = new RoutingMiddleware($app);
511+
$middleware($request, $response, $next);
512+
513+
Cache::drop('_cake_router_');
514+
Cache::enable();
515+
}
516+
517+
/**
518+
* Test cache name is used
519+
*
520+
* @return void
521+
* @expectedException InvalidArgumentException
522+
* @expectedExceptionMessage The "notfound" cache configuration does not exist
523+
*/
524+
public function testCacheConfigNotFound()
525+
{
526+
Configure::write('Router.cache', true);
527+
Configure::write('Router.cacheConfig', 'notfound');
528+
Cache::setConfig('_cake_router_', [
529+
'engine' => 'File',
530+
'path' => TMP,
531+
]);
532+
Router::$initialized = false;
533+
$request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/articles']);
534+
$response = new Response();
535+
$next = function ($req, $res) {
536+
return $res;
537+
};
538+
$app = new Application(CONFIG);
539+
$middleware = new RoutingMiddleware($app);
540+
$middleware($request, $response, $next);
541+
542+
Cache::drop('_cake_router_');
543+
}
457544
}

0 commit comments

Comments
 (0)