Commit cb66c926 by Qiang Xue

Finished caching guide [skip ci]

parent 972173a5
......@@ -114,7 +114,8 @@ Caching
* [Overview](caching-overview.md)
* [Data Caching](caching-data.md)
* [Content Caching](caching-content.md)
* [Fragment Caching](caching-fragment.md)
* [Page Caching](caching-page.md)
* [HTTP Caching](caching-http.md)
......
Fragment Caching
----------------
TBD: http://www.yiiframework.com/doc/guide/1.1/en/caching.fragment
### Caching Options
TBD: http://www.yiiframework.com/doc/guide/1.1/en/caching.fragment#caching-options
### Nested Caching
TBD: http://www.yiiframework.com/doc/guide/1.1/en/caching.fragment#nested-caching
Dynamic Content
---------------
TBD: http://www.yiiframework.com/doc/guide/1.1/en/caching.dynamic
Page Caching
------------
TBD: http://www.yiiframework.com/doc/guide/1.1/en/caching.page
### Output Caching
TBD: http://www.yiiframework.com/doc/guide/1.1/en/caching.page#output-caching
Fragment Caching
================
Fragment caching refers to caching a fragment of a Web page. For example, if a page displays a summary of
yearly sale in a table, you can store this table in cache to eliminate the time needed to generate this table
for each request. Fragment caching is built on top of [data caching](caching-data.md).
To use fragment caching, use the following construct in a [view](structure-views.md):
```php
if ($this->beginCache($id)) {
// ... generate content here ...
$this->endCache();
}
```
That is, enclose content generation logic in a pair of [[yii\base\View::beginCache()|beginCache()]] and
[[yii\base\View::endCache()|endCache()]] calls. If the content is found in the cache, [[yii\base\View::beginCache()|beginCache()]]
will render the cached content and return false, thus skip the content generation logic.
Otherwise, your content generation logic will be called, and when [[yii\base\View::endCache()|endCache()]]
is called, the generated content will be captured and stored in the cache.
Like [data caching](caching-data.md), a unique `$id` is needed to identify a content cache.
## Caching Options <a name="caching-options"></a>
You may specify additional options about fragment caching by passing the option array as the second
parameter to the [[yii\base\View::beginCache()|beginCache()]] method. Behind the scene, this option array
will be used to configure a [[yii\widgets\FragmentCache]] widget which implements the actual fragment caching
functionality.
### Duration <a name="duration"></a>
Perhaps the most commonly used option of fragment caching is [[yii\widgets\FragmentCache::duration|duration]].
It specifies for how many seconds the content can remain valid in a cache. The following code
caches the content fragment for at most one hour:
```php
if ($this->beginCache($id, ['duration' => 3600])) {
// ... generate content here ...
$this->endCache();
}
```
If the option is not set, it will take the default value 0, which means the cached content will never expire.
### Dependencies <a name="dependencies"></a>
Like [data caching](caching-data.md#cache-dependencies), content fragment being cached can also have dependencies.
For example, the content of a post being displayed depends on whether or not the post is modified.
To specify a dependency, set the [[yii\widgets\FragmentCache::dependency|dependency]] option, which can be
either an [[yii\caching\Dependency]] object or a configuration array for creating a dependency object. The
following code specifies that the fragment content depends on the change of the `updated_at` column value:
```php
$dependency = [
'class' => 'yii\caching\DbDependency',
'sql' => 'SELECT MAX(updated_at) FROM post',
];
if ($this->beginCache($id, ['dependency' => $dependency])) {
// ... generate content here ...
$this->endCache();
}
```
### Variations <a name="variations"></a>
Content being cached may be variated according to some parameters. For example, for a Web application
supporting multiple languages, the same piece of view code may generate the content in different languages.
Therefore, you may want to make the cached content variated according to the current application language.
To specify cache variations, set the [[yii\widgets\FragmentCache::variations|variations]] option, which
should be an array of scalar values, each representing a particular variation factor. For example,
to make the cached content variated by the language, you may use the following code:
```php
if ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {
// ... generate content here ...
$this->endCache();
}
```
### Toggling Caching <a name="toggling-caching"></a>
Sometimes you may want to enable fragment caching only when certain conditions are met. For example, for a page
displaying a form, you only want to cache the form when it is initially requested (via GET request). Any
subsequent display (via POST request) of the form should not be cached because the form may contain user input.
To do so, you may set the [[yii\widgets\FragmentCache::enabled|enabled]] option, like the following:
```php
if ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {
// ... generate content here ...
$this->endCache();
}
```
## Nested Caching <a name="nested-caching"></a>
Fragment caching can be nested. That is, a cached fragment can be enclosed within another fragment which is also cached.
For example, the comments are cached in an inner fragment cache, and they are cached together with the
post content in an outer fragment cache. The following code shows how two fragment caches can be nested:
```php
if ($this->beginCache($id1)) {
// ...content generation logic...
if ($this->beginCache($id2, $options2)) {
// ...content generation logic...
$this->endCache();
}
// ...content generation logic...
$this->endCache();
}
```
Different caching options can be set for the nested caches. For example, the inner caches and the outer caches
can use different cache duration values. Even when the data cached in the outer cache is invalidated, the inner
cache may still provide the valid inner fragment. However, it is not true vice versa. If the outer cache is
evaluated to be valid, it will continue to provide the same cached copy even after the content in the
inner cache has been invalidated. Therefore, you must be careful in setting the durations or the dependencies
of the nested caches, otherwise the outdated inner fragments may be kept in the outer fragment.
## Dynamic Content <a name="dynamic-content"></a>
When using fragment caching, you may encounter the situation where a large fragment of content is relatively
static except at one or a few places. For example, a page header may display the main menu bar together with
the name of the current user. Another problem is that the content being cached may contain PHP code that
must be executed for every request (e.g. the code for registering an asset bundle). Both problems can be solved
by the so-called *dynamic content* feature.
A dynamic content means a fragment of output that should not be cached even if it is enclosed within
a fragment cache. To make the content dynamic all the time, it has to be generated by executing some PHP code
for every request, even if the enclosing content is being served from cache.
You may call [[yii\base\View::renderDynamic()]] within a cached fragment to insert dynamic content
at the desired place, like the following,
```php
if ($this->beginCache($id1)) {
// ...content generation logic...
echo $this->renderDynamic('return Yii::$app->user->identity->name;');
// ...content generation logic...
$this->endCache();
}
```
The [[yii\base\View::renderDynamic()|renderDynamic()]] method takes a piece of PHP code as its parameter.
The return value of the PHP code is treated as the dynamic content. The same PHP code will be executed
for every request, no matter the enclosing fragment is being served from cached or not.
### HTTP Caching
HTTP Caching
============
Besides server-side caching that we have described in the previous sections, Web applications may
also exploit client-side caching to skip transmitting the same page content. To enable client-side
caching, you may use [[yii\filters\HttpCache]] which generates appropriate cache-related HTTP headers.
[[yii\filters\HttpCache|HttpCache]] is an [action filter](runtime-filtering.md) that can be used
like the following in a controller class:
```php
public function behaviors()
{
return [
[
'class' => 'yii\filters\HttpCache',
'only' => ['index'],
'lastModified' => function ($action, $params) {
$q = new \yii\db\Query();
return $q->from('post')->max('updated_at');
},
],
];
}
```
The above code states that HTTP caching should be enabled for the `index` action only. It should
generate a `Last-Modified` HTTP header based on the last update time of posts. When a browser visits
the `index` page for the first time, the page will be generated on the server and sent to the browser;
If the browser visits the same page again and there is no post being modified during the period,
the server will not re-generate the page, and the browser will use the cached version on the client side.
As a result, server-side rendering and page content transmission are both skipped.
[[yii\filters\HttpCache|HttpCache]] only works for `GET` and `HEAD` requests. It can handle
three kinds of cache-related HTTP headers for these requests:
* [[yii\filters\HttpCache::lastModified|Last-Modified]]
* [[yii\filters\HttpCache::etagSeed|Etag]]
* [[yii\filters\HttpCache::cacheControlHeader|Cache-Control]]
## `Last-Modified` Header <a name="last-modified"></a>
The `Last-Modified` header uses a timestamp to indicate if the page has been modified since the client caches it.
You may configure the [[yii\filters\HttpCache::lastModified]] property to enable sending
the `Last-Modified` header. The property should be a PHP callable returning a UNIX timestamp about
the page modification time. The signature of the PHP callable should be as follows,
```php
/**
* @param Action $action the action object that is being handled currently
* @param array $params the value of the "params" property
* @return integer a UNIX timestamp representing the page modification time
function ($action, $params)
```
## `ETag` Header <a name="etag"></a>
The "Entity Tag" (or `ETag` for short) header use a hash to represent the content of a page. If the page
is changed, the hash will be changed as well. By comparing the hash kept on the client side with the hash
generated on the server side, the cache may determine whether the page has been changed and should be re-transmitted.
You may configure the [[yii\filters\HttpCache::etagSeed]] property to enable sending the `ETag` header.
The property should be a PHP callable returning a seed for generating the ETag hash. The signature of the PHP callable
should be as follows,
```php
/**
* @param Action $action the action object that is being handled currently
* @param array $params the value of the "params" property
* @return string a string used as the seed for generating an ETag hash
function ($action, $params)
```
ETags allow more complex and/or more precise caching strategies than `Last-Modified` headers.
For instance, an ETag can be invalidated if the site has switched to another theme.
Expensive ETag generation may defeat the purpose of using `HttpCache` and introduce unnecessary overhead,
since they need to be re-evaluated on every request. Try to find a simple expression that invalidates
the cache if the page content has been modified.
> Note: In compliant to [RFC 2616, section 13.3.4](http://tools.ietf.org/html/rfc2616#section-13.3.4),
`HttpCache` will send out both `ETag` and `Last-Modified` headers if they are both configured.
Consequently, both will be used for cache validation if sent by the client.
## `Cache-Control` Header <a name="cache-control"></a>
The `Cache-Control` header specifies the general caching policy for pages. You may send it by configuring
the [[yii\filters\HttpCache::cacheControlHeader]] property with the header value.
## SEO Implications <a name="seo-implications"></a>
Search engine bots tend to respect cache headers. Since some crawlers have a limit on how many pages
per domain they process within a certain time span, introducing caching headers may help indexing your
site as they reduce the number of pages that need to be processed.
TBD: http://www.yiiframework.com/doc/guide/1.1/en/caching.page#http-caching
......@@ -7,12 +7,13 @@ required to generate the data from scratch every time.
Caching can occur at different levels and places in a Web application. On the server side, at the lower level,
cache may be used to store basic data, such as a list of most recent article information fetched from database;
and at the higher level, cache may be used to store the page content, such as the rendering result of the most
recent articles. On the client side, HTTP caching may be used to keep most recently visited page content in
and at the higher level, cache may be used to store fragments or whole of Web pages, such as the rendering result
of the most recent articles. On the client side, HTTP caching may be used to keep most recently visited page content in
the browser cache.
Yii supports all these caching mechanisms:
* [Data caching](caching-data.md)
* [Content caching](caching-content.md)
* [Fragment caching](caching-fragment.md)
* [Page caching](caching-page.md)
* [HTTP caching](caching-http.md)
Page Caching
============
Page caching refers to caching the content of a whole page on the server side. Later when the same page
is requested again, its content will be served from the cache instead of regenerating it from scratch.
Page caching is supported by [[yii\filters\PageCache]], an [action filter](runtime-filtering.md).
It can be used like the following in a controller class:
```php
public function behaviors()
{
return [
[
'class' => 'yii\filters\PageCache',
'only' => ['index'],
'duration' => 60,
'variations' => [
\Yii::$app->language,
],
'dependency' => [
'class' => 'yii\caching\DbDependency',
'sql' => 'SELECT COUNT(*) FROM post',
],
],
];
}
```
The above code states that page caching should be used only for the `index` action; the page content should
be cached for at most 60 seconds and should be variated by the current application language;
and the cached page should be invalidated if the total number of posts is changed.
As you can see, page caching is very similar to [fragment caching](caching-fragment.md). They both support options such
as `duration`, `dependencies`, `variations`, and `enabled`. Their main difference is that page caching is
implemented as an [action filter](runtime-filtering.md) while fragment caching a [widget](structure-widgets.md).
You can use [fragment caching](caching-fragment.md) as well as [dynamic content](caching-fragment.md#dynamic-content)
together with page caching.
......@@ -25,7 +25,7 @@ use yii\base\Action;
* {
* return [
* 'httpCache' => [
* 'class' => \yii\filters\HttpCache::className(),
* 'class' => 'yii\filters\HttpCache',
* 'only' => ['index'],
* 'lastModified' => function ($action, $params) {
* $q = new \yii\db\Query();
......
......@@ -28,16 +28,15 @@ use yii\caching\Dependency;
* {
* return [
* 'pageCache' => [
* 'class' => \yii\filters\PageCache::className(),
* 'only' => ['list'],
* 'class' => 'yii\filters\PageCache',
* 'only' => ['index'],
* 'duration' => 60,
* 'dependency' => [
* 'class' => 'yii\caching\DbDependency',
* 'sql' => 'SELECT COUNT(*) FROM post',
* ],
* 'variations' => [
* Yii::$app->language,
* Yii::$app->user->id
* \Yii::$app->language,
* ]
* ],
* ];
......@@ -71,7 +70,7 @@ class PageCache extends ActionFilter
* ~~~
* [
* 'class' => 'yii\caching\DbDependency',
* 'sql' => 'SELECT MAX(lastModified) FROM Post',
* 'sql' => 'SELECT MAX(updated_at) FROM post',
* ]
* ~~~
*
......
......@@ -42,7 +42,7 @@ class FragmentCache extends Widget
* ~~~
* [
* 'class' => 'yii\caching\DbDependency',
* 'sql' => 'SELECT MAX(lastModified) FROM Post',
* 'sql' => 'SELECT MAX(updated_at) FROM post',
* ]
* ~~~
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment