Skip to content

Commit

Permalink
Fixes yiisoft#3723: yii\filters\PageCache now supports caching resp…
Browse files Browse the repository at this point in the history
…onse headers as well as non-HTML response content
  • Loading branch information
qiangxue committed Feb 22, 2015
1 parent bec0d84 commit 07403d3
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 9 deletions.
3 changes: 2 additions & 1 deletion framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Yii Framework 2 Change Log
- Bug #7271: `ActiveRecord::populateRecord()` should be called in late binding approach (jlorente)
- Bug #7258: Response was sending HTML content type when formatter was set to JSON or XML, nulls were handled wrong (slavcodev, samdark)
- Bug #7358: Fix trimming PHPDoc prefix with TAB indent in `yii\console\Controller::parseDocCommentSummary()` (gugglegum)
- Enh #3168: Improved the performance of `yii\rbac\DbManager::checkAccess()` by caching mechanism (qiangxue)
- Enh #3168: Improved the performance of `yii\rbac\DbManager::checkAccess()` by caching mechanism (qiangxue)
- Enh #3723: `yii\filters\PageCache` now supports caching response headers as well as non-HTML response content (qiangxue)
- Enh #4710: Added `yii\web\AssetManager::appendTimestamp` to support cache busting for assets (qiangxue)
- Enh #5663: Added support for using `data-params` to specify additional form data to be submitted via the `data-method` approach (usualdesigner, qiangxue)
- Enh #6106: Added ability to specify `encode` for each item of `yii\widgets\Breadcrumbs` (samdark, aleksanderd)
Expand Down
88 changes: 81 additions & 7 deletions framework/filters/PageCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
use Yii;
use yii\base\ActionFilter;
use yii\base\Action;
use yii\caching\Cache;
use yii\caching\Dependency;
use yii\di\Instance;
use yii\web\Response;

/**
* PageCache implements server-side caching of whole pages.
Expand Down Expand Up @@ -53,7 +56,10 @@ class PageCache extends ActionFilter
*/
public $varyByRoute = true;
/**
* @var string the application component ID of the [[\yii\caching\Cache|cache]] object.
* @var Cache|array|string the cache object or the application component ID of the cache object.
* After the PageCache object is created, if you want to change this property,
* you should only assign it with a cache object.
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
*/
public $cache = 'cache';
/**
Expand Down Expand Up @@ -121,28 +127,96 @@ public function init()
*/
public function beforeAction($action)
{
if (!$this->enabled) {
return true;
}

$this->cache = Instance::ensure($this->cache, Cache::className());

$properties = [];
foreach (['cache', 'duration', 'dependency', 'variations', 'enabled'] as $name) {
foreach (['cache', 'duration', 'dependency', 'variations'] as $name) {
$properties[$name] = $this->$name;
}
$id = $this->varyByRoute ? $action->getUniqueId() : __CLASS__;
$response = Yii::$app->getResponse();
ob_start();
ob_implicit_flush(false);
if ($this->view->beginCache($id, $properties)) {
$response->on(Response::EVENT_AFTER_SEND, [$this, 'cacheResponse']);
return true;
} else {
Yii::$app->getResponse()->content = ob_get_clean();
$data = $this->cache->get($this->calculateCacheKey());
if (is_array($data)) {
$this->restoreResponse($response, $data);
}
$response->content = ob_get_clean();
return false;
}
}

/**
* @inheritdoc
* Restores response properties from the given data
* @param Response $response the response to be restored
* @param array $data the response property data
* @since 2.0.3
*/
public function afterAction($action, $result)
protected function restoreResponse($response, $data)
{
if (isset($data['format'])) {
$response->format = $data['format'];
}
if (isset($data['version'])) {
$response->version = $data['version'];
}
if (isset($data['statusCode'])) {
$response->statusCode = $data['statusCode'];
}
if (isset($data['statusText'])) {
$response->statusText = $data['statusText'];
}
if (isset($data['headers']) && is_array($data['headers'])) {
$response->getHeaders()->fromArray($data['headers']);
}
if (isset($data['cookies']) && is_array($data['cookies'])) {
$response->getCookies()->fromArray($data['cookies']);
}
}

/**
* Caches response properties.
* @since 2.0.3
*/
public function cacheResponse()
{
echo $result;
$this->view->endCache();
return ob_get_clean();
$response = Yii::$app->getResponse();
$data = [
'format' => $response->format,
'version' => $response->version,
'statusCode' => $response->statusCode,
'statusText' => $response->statusText,
'headers' => $response->getHeaders()->toArray(),
'cookies' => $response->getCookies()->toArray(),
];
$this->cache->set($this->calculateCacheKey(), $data);
echo ob_get_clean();
}

/**
* @return array the key used to cache response properties.
* @since 2.0.3
*/
protected function calculateCacheKey()
{
$key = [__CLASS__];
if ($this->varyByRoute) {
$key[] = Yii::$app->requestedRoute;
}
if (is_array($this->variations)) {
foreach ($this->variations as $value) {
$key[] = $value;
}
}
return $key;
}
}
10 changes: 10 additions & 0 deletions framework/web/CookieCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,16 @@ public function toArray()
return $this->_cookies;
}

/**
* Populates the cookie collection from an array.
* @param array $array the cookies to populate from
* @since 2.0.3
*/
public function fromArray(array $array)
{
$this->_cookies = $array;
}

/**
* Returns whether there is a cookie with the specified name.
* This method is required by the SPL interface `ArrayAccess`.
Expand Down
10 changes: 10 additions & 0 deletions framework/web/HeaderCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ public function toArray()
return $this->_headers;
}

/**
* Populates the header collection from an array.
* @param array $array the headers to populate from
* @since 2.0.3
*/
public function fromArray(array $array)
{
$this->_headers = $array;
}

/**
* Returns whether there is a header with the specified name.
* This method is required by the SPL interface `ArrayAccess`.
Expand Down
1 change: 0 additions & 1 deletion framework/web/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ protected function sendCookies()
}
setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
}
$this->getCookies()->removeAll();
}

/**
Expand Down

0 comments on commit 07403d3

Please sign in to comment.