ソースを参照

refs #10778 add router cache

Jorge González 8 年 前
コミット
03744a2b67

+ 50 - 0
src/Routing/Middleware/RoutingMiddleware.php

@@ -14,6 +14,9 @@
  */
 namespace Cake\Routing\Middleware;
 
+=======
+use Cake\Cache\Cache;
+use Cake\Core\Configure;
 use Cake\Core\PluginApplicationInterface;
 use Cake\Http\BaseApplication;
 use Cake\Http\MiddlewareQueue;
@@ -31,6 +34,16 @@ use Zend\Diactoros\Response\RedirectResponse;
 class RoutingMiddleware
 {
     /**
+     * Name of the default cache configuration name used to store routes collection
+     */
+    const DEFAULT_ROUTER_CACHE_CONFIG = '_cake_router_';
+
+    /**
+     * Key used to store the route collection in the cache engine
+     */
+    const ROUTE_COLLECTION_CACHE_KEY = 'routeCollection';
+
+    /**
      * The application that will have its routing hook invoked.
      *
      * @var \Cake\Http\BaseApplication
@@ -49,6 +62,7 @@ class RoutingMiddleware
 
     /**
      * Trigger the application's routes() hook if the application exists and Router isn't initialized.
+     * Uses the routes cache if enabled via configuration param "Router.cache"
      *
      * If the middleware is created without an Application, routes will be
      * loaded via the automatic route loading that pre-dates the routes() hook.
@@ -60,11 +74,46 @@ class RoutingMiddleware
         if (!$this->app) {
             return;
         }
+        $routeCollection = $this->buildRouteCollection();
+        Router::setRouteCollection($routeCollection);
+        // Prevent routes from being loaded again
+        Router::$initialized = true;
+    }
+
+    /**
+     * Check if route cache is enabled and use the configured Cache to 'remember' the route collection
+     *
+     * @return \Cake\Routing\RouteCollection
+     */
+    protected function buildRouteCollection()
+    {
+        $isRouterCacheEnabled = Configure::read('Router.cache');
+        if (Cache::enabled() && $isRouterCacheEnabled) {
+            $routesCacheConfig = Configure::read('Router.cacheConfig', self::DEFAULT_ROUTER_CACHE_CONFIG);
+
+            return Cache::remember(self::ROUTE_COLLECTION_CACHE_KEY, function () {
+
+                return $this->prepareRouteCollection();
+            }, $routesCacheConfig);
+        }
+
+        return $this->prepareRouteCollection();
+    }
+
+    /**
+     * Generate the route collection using the builder
+     *
+     * @return \Cake\Routing\RouteCollection
+     */
+    protected function prepareRouteCollection()
+    {
         $builder = Router::createRouteBuilder('/');
         $this->app->routes($builder);
         if ($this->app instanceof PluginApplicationInterface) {
             $this->app->pluginRoutes($builder);
         }
+
+        return Router::getRouteCollection();
     }
 
     /**
@@ -77,6 +126,7 @@ class RoutingMiddleware
      * @param \Psr\Http\Message\ResponseInterface $response The response.
      * @param callable $next The next middleware to call.
      * @return \Psr\Http\Message\ResponseInterface A response.
+     * @throws \Cake\Routing\InvalidArgumentException
      */
     public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
     {

+ 12 - 0
src/Routing/Router.php

@@ -1149,6 +1149,17 @@ class Router
     }
 
     /**
+     * Set the RouteCollection inside the Router
+     *
+     * @param RouteCollection $routeCollection
+     * @return void
+     */
+    public static function setRouteCollection($routeCollection)
+    {
+        static::$_collection = $routeCollection;
+    }
+
+    /**
      * Loads route configuration
      *
      * @deprecated 3.5.0 Routes will be loaded via the Application::routes() hook in 4.0.0
@@ -1159,4 +1170,5 @@ class Router
         static::$initialized = true;
         include CONFIG . 'routes.php';
     }
+
 }

+ 87 - 0
tests/TestCase/Routing/Middleware/RoutingMiddlewareTest.php

@@ -14,6 +14,8 @@
  */
 namespace Cake\Test\TestCase\Routing\Middleware;
 
+use Cake\Cache\Cache;
+use Cake\Core\Configure;
 use Cake\Routing\Middleware\RoutingMiddleware;
 use Cake\Routing\RouteBuilder;
 use Cake\Routing\Router;
@@ -454,4 +456,89 @@ class RoutingMiddlewareTest extends TestCase
             ['/api/version', ['second', 'last']],
         ];
     }
+
+    /**
+     * Test we store route collection in cache.
+     *
+     * @return void
+     */
+    public function testCacheRoutes()
+    {
+        Configure::write('Router.cache', true);
+        Cache::setConfig('_cake_router_', [
+            'engine' => 'File',
+            'path' => TMP,
+        ]);
+        Router::$initialized = false;
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/articles']);
+        $response = new Response();
+        $next = function ($req, $res) {
+            $routeCollection = Cache::read('routeCollection', '_cake_router_');
+            $this->assertInstanceOf('\Cake\Routing\RouteCollection', $routeCollection);
+            return $res;
+        };
+        $app = new Application(CONFIG);
+        $middleware = new RoutingMiddleware($app);
+        $middleware($request, $response, $next);
+
+        Cache::clear(false, '_cake_router_');
+        Cache::drop('_cake_router_');
+    }
+
+    /**
+     * Test we don't cache routes if cache is disabled.
+     *
+     * @return void
+     */
+    public function testCacheNotUsedIfCacheDisabled()
+    {
+        Configure::write('Router.cache', true);
+        Cache::disable();
+        Cache::setConfig('_cake_router_', [
+            'engine' => 'File',
+            'path' => TMP,
+        ]);
+        Router::$initialized = false;
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/articles']);
+        $response = new Response();
+        $next = function ($req, $res) {
+            $routeCollection = Cache::read('routeCollection', '_cake_router_');
+            $this->assertFalse($routeCollection);
+            return $res;
+        };
+        $app = new Application(CONFIG);
+        $middleware = new RoutingMiddleware($app);
+        $middleware($request, $response, $next);
+
+        Cache::drop('_cake_router_');
+        Cache::enable();
+    }
+
+    /**
+     * Test cache name is used
+     *
+     * @return void
+     * @expectedException InvalidArgumentException
+     * @expectedExceptionMessage The "notfound" cache configuration does not exist
+     */
+    public function testCacheConfigNotFound()
+    {
+        Configure::write('Router.cache', true);
+        Configure::write('Router.cacheConfig', 'notfound');
+        Cache::setConfig('_cake_router_', [
+            'engine' => 'File',
+            'path' => TMP,
+        ]);
+        Router::$initialized = false;
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/articles']);
+        $response = new Response();
+        $next = function ($req, $res) {
+            return $res;
+        };
+        $app = new Application(CONFIG);
+        $middleware = new RoutingMiddleware($app);
+        $middleware($request, $response, $next);
+
+        Cache::drop('_cake_router_');
+    }
 }