Issue #2348679 by znerol, Wim Leers: Move the remaining procedural page cache code to the page cache stack middleware
parent
5d20c57be1
commit
1e985b0d32
|
@ -124,6 +124,7 @@ services:
|
|||
class: Drupal\Core\PageCache\ChainResponsePolicy
|
||||
tags:
|
||||
- { name: service_collector, tag: page_cache_response_policy, call: addPolicy}
|
||||
lazy: true
|
||||
page_cache_kill_switch:
|
||||
class: Drupal\Core\PageCache\ResponsePolicy\KillSwitch
|
||||
tags:
|
||||
|
@ -455,7 +456,7 @@ services:
|
|||
- { name: http_middleware, priority: 300 }
|
||||
http_middleware.page_cache:
|
||||
class: Drupal\Core\StackMiddleware\PageCache
|
||||
arguments: ['@kernel']
|
||||
arguments: ['@cache.render', '@page_cache_request_policy', '@page_cache_response_policy', '@content_negotiation']
|
||||
tags:
|
||||
- { name: http_middleware, priority: 200 }
|
||||
http_middleware.kernel_pre_handle:
|
||||
|
|
|
@ -68,25 +68,18 @@ const DRUPAL_BOOTSTRAP_CONFIGURATION = 0;
|
|||
const DRUPAL_BOOTSTRAP_KERNEL = 1;
|
||||
|
||||
/**
|
||||
* Third bootstrap phase: try to serve a cached page.
|
||||
* Third bootstrap phase: load code for subsystems and modules.
|
||||
*
|
||||
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
|
||||
*/
|
||||
const DRUPAL_BOOTSTRAP_PAGE_CACHE = 2;
|
||||
|
||||
/**
|
||||
* Fourth bootstrap phase: load code for subsystems and modules.
|
||||
*
|
||||
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
|
||||
*/
|
||||
const DRUPAL_BOOTSTRAP_CODE = 3;
|
||||
const DRUPAL_BOOTSTRAP_CODE = 2;
|
||||
|
||||
/**
|
||||
* Final bootstrap phase: initialize language, path, theme, and modules.
|
||||
*
|
||||
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
|
||||
*/
|
||||
const DRUPAL_BOOTSTRAP_FULL = 4;
|
||||
const DRUPAL_BOOTSTRAP_FULL = 3;
|
||||
|
||||
/**
|
||||
* Role ID for anonymous users; should match what's in the "role" table.
|
||||
|
@ -310,39 +303,6 @@ function drupal_get_path($type, $name) {
|
|||
return dirname(drupal_get_filename($type, $name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the page cache cid for this request.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request for this page.
|
||||
*
|
||||
* @return string
|
||||
* The cid for this request.
|
||||
*/
|
||||
function drupal_page_cache_get_cid(Request $request) {
|
||||
$cid_parts = array(
|
||||
$request->getUri(),
|
||||
\Drupal::service('content_negotiation')->getContentType($request),
|
||||
);
|
||||
return implode(':', $cid_parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current page from the cache.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request for this page.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* The response, if the page was found in the cache, NULL otherwise.
|
||||
*/
|
||||
function drupal_page_get_cache(Request $request) {
|
||||
$cache = \Drupal::cache('render')->get(drupal_page_cache_get_cid($request));
|
||||
if ($cache) {
|
||||
return $cache->data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an HTTP response header for the current page.
|
||||
*
|
||||
|
@ -426,72 +386,6 @@ function _drupal_set_preferred_header_name($name = NULL) {
|
|||
$header_names[strtolower($name)] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets HTTP headers in preparation for a cached page response.
|
||||
*
|
||||
* The headers allow as much as possible in proxies and browsers without any
|
||||
* particular knowledge about the pages. Modules can override these headers
|
||||
* using _drupal_add_http_header().
|
||||
*
|
||||
* If the request is conditional (using If-Modified-Since and If-None-Match),
|
||||
* and the conditions match those currently in the cache, a 304 Not Modified
|
||||
* response is sent.
|
||||
*/
|
||||
function drupal_serve_page_from_cache(Response $response, Request $request) {
|
||||
// Only allow caching in the browser and prevent that the response is stored
|
||||
// by an external proxy server when the following conditions apply:
|
||||
// 1. There is a session cookie on the request.
|
||||
// 2. The Vary: Cookie header is on the response.
|
||||
// 3. The Cache-Control header does not contain the no-cache directive.
|
||||
if ($request->cookies->has(session_name()) &&
|
||||
in_array('Cookie', $response->getVary()) &&
|
||||
!$response->headers->hasCacheControlDirective('no-cache')) {
|
||||
|
||||
$response->setPrivate();
|
||||
}
|
||||
|
||||
// Negotiate whether to use compression.
|
||||
if ($response->headers->get('Content-Encoding') == 'gzip' && extension_loaded('zlib')) {
|
||||
if (strpos($request->headers->get('Accept-Encoding'), 'gzip') !== FALSE) {
|
||||
// The response content is already gzip'ed, so make sure
|
||||
// zlib.output_compression does not compress it once more.
|
||||
ini_set('zlib.output_compression', '0');
|
||||
}
|
||||
else {
|
||||
// The client does not support compression. Decompress the content and
|
||||
// remove the Content-Encoding header.
|
||||
$content = $response->getContent();
|
||||
$content = gzinflate(substr(substr($content, 10), 0, -8));
|
||||
$response->setContent($content);
|
||||
$response->headers->remove('Content-Encoding');
|
||||
}
|
||||
}
|
||||
|
||||
// Perform HTTP revalidation.
|
||||
// @todo Use Response::isNotModified() as per https://drupal.org/node/2259489
|
||||
$last_modified = $response->getLastModified();
|
||||
if ($last_modified) {
|
||||
// See if the client has provided the required HTTP headers.
|
||||
$if_modified_since = $request->server->has('HTTP_IF_MODIFIED_SINCE') ? strtotime($request->server->get('HTTP_IF_MODIFIED_SINCE')) : FALSE;
|
||||
$if_none_match = $request->server->has('HTTP_IF_NONE_MATCH') ? stripslashes($request->server->get('HTTP_IF_NONE_MATCH')) : FALSE;
|
||||
|
||||
if ($if_modified_since && $if_none_match
|
||||
&& $if_none_match == $response->getEtag() // etag must match
|
||||
&& $if_modified_since == $last_modified->getTimestamp()) { // if-modified-since must match
|
||||
$response->setStatusCode(304);
|
||||
$response->setContent(NULL);
|
||||
|
||||
// In the case of a 304 response, certain headers must be sent, and the
|
||||
// remaining may not (see RFC 2616, section 10.3.5).
|
||||
foreach (array_keys($response->headers->all()) as $name) {
|
||||
if (!in_array($name, array('content-location', 'expires', 'cache-control', 'vary'))) {
|
||||
$response->headers->remove($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string to the current language or to a given language.
|
||||
*
|
||||
|
@ -830,10 +724,6 @@ function drupal_bootstrap($phase = NULL) {
|
|||
$kernel->boot();
|
||||
break;
|
||||
|
||||
case DRUPAL_BOOTSTRAP_PAGE_CACHE:
|
||||
$kernel->handlePageCache($request);
|
||||
break;
|
||||
|
||||
case DRUPAL_BOOTSTRAP_CODE:
|
||||
case DRUPAL_BOOTSTRAP_FULL:
|
||||
$kernel->prepareLegacyRequest($request);
|
||||
|
|
|
@ -1267,48 +1267,6 @@ function drupal_clear_js_cache() {
|
|||
\Drupal::service('asset.js.collection_optimizer')->deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current page in the cache.
|
||||
*
|
||||
* If page_compression is enabled, a gzipped version of the page is stored in
|
||||
* the cache to avoid compressing the output on each request. The cache entry
|
||||
* is unzipped in the relatively rare event that the page is requested by a
|
||||
* client without gzip support.
|
||||
*
|
||||
* Page compression requires the PHP zlib extension
|
||||
* (http://php.net/manual/ref.zlib.php).
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Response $response
|
||||
* The fully populated response.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request for this page.
|
||||
*/
|
||||
function drupal_page_set_cache(Response $response, Request $request) {
|
||||
// Check if the current page may be compressed.
|
||||
if (extension_loaded('zlib') && !$response->headers->get('Content-Encoding') &&
|
||||
\Drupal::config('system.performance')->get('response.gzip')) {
|
||||
|
||||
$content = $response->getContent();
|
||||
if ($content) {
|
||||
$response->setContent(gzencode($content, 9, FORCE_GZIP));
|
||||
$response->headers->set('Content-Encoding', 'gzip');
|
||||
}
|
||||
|
||||
// When page compression is enabled, ensure that proxy caches will record
|
||||
// and deliver different versions of a page depending on whether the
|
||||
// client supports gzip or not.
|
||||
$response->setVary('Accept-Encoding', FALSE);
|
||||
}
|
||||
|
||||
// Use the actual timestamp from an Expires header, if available.
|
||||
$date = $response->getExpires();
|
||||
$expire = ($date > (new DateTime())) ? $date->getTimestamp() : Cache::PERMANENT;
|
||||
|
||||
$cid = drupal_page_cache_get_cid($request);
|
||||
$tags = explode(' ', $response->headers->get('X-Drupal-Cache-Tags'));
|
||||
\Drupal::cache('render')->set($cid, $response, $expire, $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-render callback: Renders a link into #markup.
|
||||
*
|
||||
|
|
|
@ -76,6 +76,13 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
*/
|
||||
protected $booted = FALSE;
|
||||
|
||||
/**
|
||||
* Whether essential services have been set up properly by preHandle().
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $prepared = FALSE;
|
||||
|
||||
/**
|
||||
* Holds the list of enabled modules.
|
||||
*
|
||||
|
@ -474,42 +481,8 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
|
||||
// Override of Symfony's mime type guesser singleton.
|
||||
MimeTypeGuesser::registerWithSymfonyGuesser($this->container);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo Invoke proper request/response/terminate events.
|
||||
*/
|
||||
public function handlePageCache(Request $request) {
|
||||
$this->boot();
|
||||
|
||||
// Check for a cache mode force from settings.php.
|
||||
if (Settings::get('page_cache_without_database')) {
|
||||
$cache_enabled = TRUE;
|
||||
}
|
||||
else {
|
||||
$config = $this->getContainer()->get('config.factory')->get('system.performance');
|
||||
$cache_enabled = $config->get('cache.page.use_internal');
|
||||
}
|
||||
|
||||
$request_policy = \Drupal::service('page_cache_request_policy');
|
||||
if ($cache_enabled && $request_policy->check($request) === RequestPolicyInterface::ALLOW) {
|
||||
// Get the page from the cache.
|
||||
$response = drupal_page_get_cache($request);
|
||||
// If there is a cached page, display it.
|
||||
if ($response) {
|
||||
$response->headers->set('X-Drupal-Cache', 'HIT');
|
||||
|
||||
drupal_serve_page_from_cache($response, $request);
|
||||
|
||||
// We are done.
|
||||
$response->prepare($request);
|
||||
$response->send();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
$this->prepared = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -577,7 +550,9 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function terminate(Request $request, Response $response) {
|
||||
if (FALSE === $this->booted) {
|
||||
// Only run terminate() when essential services have been set up properly
|
||||
// by preHandle() before.
|
||||
if (FALSE === $this->prepared) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -93,16 +93,6 @@ interface DrupalKernelInterface extends HttpKernelInterface {
|
|||
*/
|
||||
public function updateModules(array $module_list, array $module_filenames = array());
|
||||
|
||||
/**
|
||||
* Attempts to serve a page from the cache.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function handlePageCache(Request $request);
|
||||
|
||||
/**
|
||||
* Prepare the kernel for handling a request without handling the request.
|
||||
*
|
||||
|
|
|
@ -136,17 +136,6 @@ class FinishResponseSubscriber implements EventSubscriberInterface {
|
|||
// header declaring the response as not cacheable.
|
||||
$this->setResponseNotCacheable($response, $request);
|
||||
}
|
||||
|
||||
// Currently it is not possible to cache some types of responses. Therefore
|
||||
// exclude binary file responses (generated files, e.g. images with image
|
||||
// styles) and streamed responses (files directly read from the disk).
|
||||
// see: https://github.com/symfony/symfony/issues/9128#issuecomment-25088678
|
||||
if ($is_cacheable && $this->config->get('cache.page.use_internal') && !($response instanceof BinaryFileResponse) && !($response instanceof StreamedResponse)) {
|
||||
// Store the response in the internal page cache.
|
||||
drupal_page_set_cache($response, $request);
|
||||
$response->headers->set('X-Drupal-Cache', 'MISS');
|
||||
drupal_serve_page_from_cache($response, $request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,8 +7,16 @@
|
|||
|
||||
namespace Drupal\Core\StackMiddleware;
|
||||
|
||||
use Drupal\Core\DrupalKernelInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\ContentNegotiation;
|
||||
use Drupal\Core\PageCache\RequestPolicyInterface;
|
||||
use Drupal\Core\PageCache\ResponsePolicyInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
|
@ -24,32 +32,321 @@ class PageCache implements HttpKernelInterface {
|
|||
protected $httpKernel;
|
||||
|
||||
/**
|
||||
* The main Drupal kernel.
|
||||
* The cache bin.
|
||||
*
|
||||
* @var \Drupal\Core\DrupalKernelInterface
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface.
|
||||
*/
|
||||
protected $drupalKernel;
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* A policy rule determining the cacheability of a request.
|
||||
*
|
||||
* @var \Drupal\Core\PageCache\RequestPolicyInterface
|
||||
*/
|
||||
protected $requestPolicy;
|
||||
|
||||
/**
|
||||
* A policy rule determining the cacheability of the response.
|
||||
*
|
||||
* @var \Drupal\Core\PageCache\ResponsePolicyInterface
|
||||
*/
|
||||
protected $responsePolicy;
|
||||
|
||||
/**
|
||||
* The content negotiation library.
|
||||
*
|
||||
* @var \Drupal\Core\ContentNegotiation
|
||||
*/
|
||||
protected $contentNegotiation;
|
||||
|
||||
/**
|
||||
* Constructs a ReverseProxyMiddleware object.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
|
||||
* The decorated kernel.
|
||||
* @param \Drupal\Core\DrupalKernelInterface $drupal_kernel
|
||||
* The main Drupal kernel.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
|
||||
* The cache bin.
|
||||
* @param \Drupal\Core\PageCache\RequestPolicyInterface $request_policy
|
||||
* A policy rule determining the cacheability of a request.
|
||||
* @param \Drupal\Core\PageCache\ResponsePolicyInterface $response_policy
|
||||
* A policy rule determining the cacheability of the response.
|
||||
* @param \Drupal\Core\ContentNegotiation $content_negotiation
|
||||
* The content negotiation library.
|
||||
*/
|
||||
public function __construct(HttpKernelInterface $http_kernel, DrupalKernelInterface $drupal_kernel) {
|
||||
public function __construct(HttpKernelInterface $http_kernel, CacheBackendInterface $cache, RequestPolicyInterface $request_policy, ResponsePolicyInterface $response_policy, ContentNegotiation $content_negotiation) {
|
||||
$this->httpKernel = $http_kernel;
|
||||
$this->drupalKernel = $drupal_kernel;
|
||||
$this->cache = $cache;
|
||||
$this->requestPolicy = $request_policy;
|
||||
$this->responsePolicy = $response_policy;
|
||||
$this->contentNegotiation = $content_negotiation;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
|
||||
$this->drupalKernel->handlePageCache($request);
|
||||
if ($type !== static::MASTER_REQUEST) {
|
||||
// Only allow page caching on master request.
|
||||
$cache_enabled = FALSE;
|
||||
}
|
||||
elseif (Settings::get('page_cache_without_database')) {
|
||||
// Check for a cache mode force from settings.php.
|
||||
$cache_enabled = TRUE;
|
||||
}
|
||||
else {
|
||||
$config = $this->config('system.performance');
|
||||
$cache_enabled = $config->get('cache.page.use_internal');
|
||||
}
|
||||
|
||||
if ($cache_enabled && $this->requestPolicy->check($request) === RequestPolicyInterface::ALLOW) {
|
||||
$response = $this->lookup($request, $type, $catch);
|
||||
}
|
||||
else {
|
||||
$response = $this->pass($request, $type, $catch);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sidesteps the page cache and directly forwards a request to the backend.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object.
|
||||
* @param int $type
|
||||
* The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
|
||||
* HttpKernelInterface::SUB_REQUEST)
|
||||
* @param bool $catch
|
||||
* Whether to catch exceptions or not
|
||||
*
|
||||
* @returns \Symfony\Component\HttpFoundation\Response $response
|
||||
* A response object.
|
||||
*/
|
||||
protected function pass(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
|
||||
return $this->httpKernel->handle($request, $type, $catch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a response from the cache or fetches it from the backend.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object.
|
||||
* @param int $type
|
||||
* The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
|
||||
* HttpKernelInterface::SUB_REQUEST)
|
||||
* @param bool $catch
|
||||
* Whether to catch exceptions or not
|
||||
*
|
||||
* @returns \Symfony\Component\HttpFoundation\Response $response
|
||||
* A response object.
|
||||
*/
|
||||
protected function lookup(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
|
||||
if ($response = $this->get($request)) {
|
||||
$response->headers->set('X-Drupal-Cache', 'HIT');
|
||||
}
|
||||
else {
|
||||
$response = $this->fetch($request, $type, $catch);
|
||||
}
|
||||
|
||||
// Only allow caching in the browser and prevent that the response is stored
|
||||
// by an external proxy server when the following conditions apply:
|
||||
// 1. There is a session cookie on the request.
|
||||
// 2. The Vary: Cookie header is on the response.
|
||||
// 3. The Cache-Control header does not contain the no-cache directive.
|
||||
if ($request->cookies->has(session_name()) &&
|
||||
in_array('Cookie', $response->getVary()) &&
|
||||
!$response->headers->hasCacheControlDirective('no-cache')) {
|
||||
|
||||
$response->setPrivate();
|
||||
}
|
||||
|
||||
// Negotiate whether to use compression.
|
||||
if (extension_loaded('zlib') && $response->headers->get('Content-Encoding') === 'gzip') {
|
||||
if (strpos($request->headers->get('Accept-Encoding'), 'gzip') !== FALSE) {
|
||||
// The response content is already gzip'ed, so make sure
|
||||
// zlib.output_compression does not compress it once more.
|
||||
ini_set('zlib.output_compression', '0');
|
||||
}
|
||||
else {
|
||||
// The client does not support compression. Decompress the content and
|
||||
// remove the Content-Encoding header.
|
||||
$content = $response->getContent();
|
||||
$content = gzinflate(substr(substr($content, 10), 0, -8));
|
||||
$response->setContent($content);
|
||||
$response->headers->remove('Content-Encoding');
|
||||
}
|
||||
}
|
||||
|
||||
// Perform HTTP revalidation.
|
||||
// @todo Use Response::isNotModified() as per https://drupal.org/node/2259489
|
||||
$last_modified = $response->getLastModified();
|
||||
if ($last_modified) {
|
||||
// See if the client has provided the required HTTP headers.
|
||||
$if_modified_since = $request->server->has('HTTP_IF_MODIFIED_SINCE') ? strtotime($request->server->get('HTTP_IF_MODIFIED_SINCE')) : FALSE;
|
||||
$if_none_match = $request->server->has('HTTP_IF_NONE_MATCH') ? stripslashes($request->server->get('HTTP_IF_NONE_MATCH')) : FALSE;
|
||||
|
||||
if ($if_modified_since && $if_none_match
|
||||
&& $if_none_match == $response->getEtag() // etag must match
|
||||
&& $if_modified_since == $last_modified->getTimestamp()) { // if-modified-since must match
|
||||
$response->setStatusCode(304);
|
||||
$response->setContent(NULL);
|
||||
|
||||
// In the case of a 304 response, certain headers must be sent, and the
|
||||
// remaining may not (see RFC 2616, section 10.3.5).
|
||||
foreach (array_keys($response->headers->all()) as $name) {
|
||||
if (!in_array($name, array('content-location', 'expires', 'cache-control', 'vary'))) {
|
||||
$response->headers->remove($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a response from the backend and stores it in the cache.
|
||||
*
|
||||
* If page_compression is enabled, a gzipped version of the page is stored in
|
||||
* the cache to avoid compressing the output on each request. The cache entry
|
||||
* is unzipped in the relatively rare event that the page is requested by a
|
||||
* client without gzip support.
|
||||
*
|
||||
* Page compression requires the PHP zlib extension
|
||||
* (http://php.net/manual/ref.zlib.php).
|
||||
*
|
||||
* @see drupal_page_header()
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object.
|
||||
* @param int $type
|
||||
* The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
|
||||
* HttpKernelInterface::SUB_REQUEST)
|
||||
* @param bool $catch
|
||||
* Whether to catch exceptions or not
|
||||
*
|
||||
* @returns \Symfony\Component\HttpFoundation\Response $response
|
||||
* A response object.
|
||||
*/
|
||||
protected function fetch(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
|
||||
$response = $this->httpKernel->handle($request, $type, $catch);
|
||||
|
||||
// Currently it is not possible to cache some types of responses. Therefore
|
||||
// exclude binary file responses (generated files, e.g. images with image
|
||||
// styles) and streamed responses (files directly read from the disk).
|
||||
// see: https://github.com/symfony/symfony/issues/9128#issuecomment-25088678
|
||||
if ($response instanceof BinaryFileResponse || $response instanceof StreamedResponse) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($this->responsePolicy->check($response, $request) === ResponsePolicyInterface::DENY) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Check if the current page may be compressed.
|
||||
if (extension_loaded('zlib') && !$response->headers->get('Content-Encoding') && $this->config('system.performance')->get('response.gzip')) {
|
||||
$content = $response->getContent();
|
||||
if ($content) {
|
||||
$response->setContent(gzencode($content, 9, FORCE_GZIP));
|
||||
$response->headers->set('Content-Encoding', 'gzip');
|
||||
}
|
||||
|
||||
// When page compression is enabled, ensure that proxy caches will record
|
||||
// and deliver different versions of a page depending on whether the
|
||||
// client supports gzip or not.
|
||||
$response->setVary('Accept-Encoding', FALSE);
|
||||
}
|
||||
|
||||
// Use the actual timestamp from an Expires header, if available.
|
||||
$date = $response->getExpires();
|
||||
$expire = ($date > (new \DateTime())) ? $date->getTimestamp() : Cache::PERMANENT;
|
||||
|
||||
$tags = explode(' ', $response->headers->get('X-Drupal-Cache-Tags'));
|
||||
$this->set($request, $response, $expire, $tags);
|
||||
|
||||
// Mark response as a cache miss.
|
||||
$response->headers->set('X-Drupal-Cache', 'MISS');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a response object from the page cache.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object.
|
||||
* @param bool $allow_invalid
|
||||
* (optional) If TRUE, a cache item may be returned even if it is expired or
|
||||
* has been invalidated. Such items may sometimes be preferred, if the
|
||||
* alternative is recalculating the value stored in the cache, especially
|
||||
* if another concurrent request is already recalculating the same value.
|
||||
* The "valid" property of the returned object indicates whether the item is
|
||||
* valid or not. Defaults to FALSE.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response|false
|
||||
* The cached response or FALSE on failure.
|
||||
*/
|
||||
protected function get(Request $request, $allow_invalid = FALSE) {
|
||||
$cid = $this->getCacheId($request);
|
||||
if ($cache = $this->cache->get($cid, $allow_invalid)) {
|
||||
return $cache->data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a response object in the page cache.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object.
|
||||
* @param \Symfony\Component\HttpFoundation\Response $response
|
||||
* The response to store in the cache.
|
||||
* @param int $expire
|
||||
* One of the following values:
|
||||
* - CacheBackendInterface::CACHE_PERMANENT: Indicates that the item should
|
||||
* not be removed unless it is deleted explicitly.
|
||||
* - A Unix timestamp: Indicates that the item will be considered invalid
|
||||
* after this time, i.e. it will not be returned by get() unless
|
||||
* $allow_invalid has been set to TRUE. When the item has expired, it may
|
||||
* be permanently deleted by the garbage collector at any time.
|
||||
* @param array $tags
|
||||
* An array of tags to be stored with the cache item. These should normally
|
||||
* identify objects used to build the cache item, which should trigger
|
||||
* cache invalidation when updated. For example if a cached item represents
|
||||
* a node, both the node ID and the author's user ID might be passed in as
|
||||
* tags. For example array('node' => array(123), 'user' => array(92)).
|
||||
*/
|
||||
protected function set(Request $request, Response $response, $expire, array $tags) {
|
||||
$cid = $this->getCacheId($request);
|
||||
$this->cache->set($cid, $response, $expire, $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the page cache ID for this request.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object.
|
||||
*
|
||||
* @return string
|
||||
* The cache ID for this request.
|
||||
*/
|
||||
protected function getCacheId(Request $request) {
|
||||
$cid_parts = array(
|
||||
$request->getUri(),
|
||||
$this->contentNegotiation->getContentType($request),
|
||||
);
|
||||
return implode(':', $cid_parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps Drupal::config().
|
||||
*
|
||||
* Config factory is not injected into this class in order to prevent
|
||||
* premature initialization of config storage (database).
|
||||
*
|
||||
* @see \Drupal::config()
|
||||
*/
|
||||
protected function config($name) {
|
||||
return \Drupal::config($name);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,13 +25,18 @@ class ServiceDestructionTest extends KernelTestBase {
|
|||
// Enable the test module to add it to the container.
|
||||
$this->enableModules(array('service_provider_test'));
|
||||
|
||||
$request = $this->container->get('request_stack')->getCurrentRequest();
|
||||
$kernel = $this->container->get('kernel');
|
||||
$kernel->preHandle($request);
|
||||
|
||||
// The service has not been destructed yet.
|
||||
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
|
||||
|
||||
// Call the class and then terminate the kernel
|
||||
$this->container->get('service_provider_test_class');
|
||||
|
||||
$response = new Response();
|
||||
$this->container->get('kernel')->terminate($this->container->get('request_stack')->getCurrentRequest(), $response);
|
||||
$kernel->terminate($request, $response);
|
||||
$this->assertTrue(\Drupal::state()->get('service_provider_test.destructed'));
|
||||
}
|
||||
|
||||
|
@ -42,13 +47,17 @@ class ServiceDestructionTest extends KernelTestBase {
|
|||
// Enable the test module to add it to the container.
|
||||
$this->enableModules(array('service_provider_test'));
|
||||
|
||||
$request = $this->container->get('request_stack')->getCurrentRequest();
|
||||
$kernel = $this->container->get('kernel');
|
||||
$kernel->preHandle($request);
|
||||
|
||||
// The service has not been destructed yet.
|
||||
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
|
||||
|
||||
// Terminate the kernel. The test class has not been called, so it should not
|
||||
// be destructed.
|
||||
$response = new Response();
|
||||
$this->container->get('kernel')->terminate($this->container->get('request_stack')->getCurrentRequest(), $response);
|
||||
$kernel->terminate($request, $response);
|
||||
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ foreach ($_SERVER as &$value) {
|
|||
$request = Request::createFromGlobals();
|
||||
$kernel = TestKernel::createFromRequest($request, $autoloader, 'testing', TRUE);
|
||||
$response = $kernel
|
||||
->handlePageCache($request)
|
||||
->handle($request)
|
||||
// Handle the response object.
|
||||
->prepare($request)->send();
|
||||
|
|
|
@ -28,7 +28,6 @@ foreach ($_SERVER as &$value) {
|
|||
$request = Request::createFromGlobals();
|
||||
$kernel = TestKernel::createFromRequest($request, $autoloader, 'testing', TRUE);
|
||||
$response = $kernel
|
||||
->handlePageCache($request)
|
||||
->handle($request)
|
||||
// Handle the response object.
|
||||
->prepare($request)->send();
|
||||
|
|
Loading…
Reference in New Issue