[ Index ] |
|
Code source de Symfony 1.0.0 |
1 Chapter 12 - Caching 2 ==================== 3 4 One of the ways to speed up an application is to store chunks of generated HTML code, or even full pages, for future requests. This technique is known as caching, and it can be managed on the server side and on the client side. 5 6 Symfony offers a flexible server-caching system. It allows saving the full response, the result of an action, a partial, or a template fragment into a file, through a very intuitive setup based on YAML files. When the underlying data changes, you can easily clear selective parts of the cache with the command line or special action methods. Symfony also provides an easy way to control the client-side cache through HTTP 1.1 headers. This chapter deals with all these subjects, and gives you a few tips on monitoring the improvements that caching can bring to your applications. 7 8 Caching the Response 9 -------------------- 10 11 The principle of HTML caching is simple: Part or all of the HTML code that is sent to a user upon a request can be reused for a similar request. This HTML code is stored in a special place (the `cache/` folder in symfony), where the front controller will look for it before executing an action. If a cached version is found, it is sent without executing the action, thus greatly speeding up the process. If no cached version is found, the action is executed, and its result (the view) is stored in the cache folder for future requests. 12 13 As all the pages may contain dynamic information, the HTML cache is disabled by default. It is up to the site administrator to enable it in order to improve performance. 14 15 Symfony handles three different types of HTML caching: 16 17 * Cache of an action (with or without the layout) 18 * Cache of a partial, a component, or a component slot 19 * Cache of a template fragment 20 21 The first two types are handled with YAML configuration files. Template fragment caching is managed by calls to helper functions in the template. 22 23 ### Global Cache Settings 24 25 For each application of a project, the HTML cache mechanism can be enabled or disabled (the default), per environment, in the `cache` setting of the `settings.yml` file. Listing 12-1 demonstrates enabling the cache. 26 27 Listing 12-1 - Activating the Cache, in `myapp/config/settings.yml` 28 29 dev: 30 .settings: 31 cache: on 32 33 ### Caching an Action 34 35 Actions displaying static information (not depending on database or session-dependent data) or actions reading information from a database but without modifying it (typically, GET requests) are often ideal for caching. Figure 12-1 shows which elements of the page are cached in this case: either the action result (its template) or the action result together with the layout. 36 37 Figure 12-1 - Caching an action 38 39  40 41 For instance, consider a `user/list` action that returns the list of all users of a website. Unless a user is modified, added, or removed (and this matter will be discussed later in the "Removing Items from the Cache" section), this list always displays the same information, so it is a good candidate for caching. 42 43 Cache activation and settings, action by action, are defined in a cache.yml file located in the module `config/` directory. See Listing 12-2 for an example. 44 45 Listing 12-2 - Activating the Cache for an Action, in `myapp/modules/user/config/cache.yml` 46 47 list: 48 enabled: on 49 with_layout: false # Default value 50 lifetime: 86400 # Default value 51 52 This configuration stipulates that the cache is on for the list action, and that the layout will not be cached with the action (which is the default behavior). It means that even if a cached version of the action exists, the layout (together with its partials and components) is still executed. If the `with_layout` setting is set to `true`, the layout is cached with the action and not executed again. 53 54 To test the cache settings, call the action in the development environment from your browser. 55 56 http://myapp.example.com/myapp_dev.php/user/list 57 58 You will notice a border around the action area in the page. The first time, the area has a blue header, showing that it did not come from the cache. Refresh the page, and the action area will have a yellow header, showing that it did come from the cache (with a notable boost in response time). You will learn more about the ways to test and monitor caching later in this chapter. 59 60 >**NOTE** 61 >Slots are part of the template, and caching an action will also store the value of the slots defined in this action's template. So the cache works natively for slots. 62 63 The caching system also works for pages with arguments. The `user` module may have, for instance, a `show` action that expects an `id` argument to display the details of a user. Modify the `cache.yml` file to enable the cache for this action as well, as shown in Listing 12-3. 64 65 In order to organize your `cache.yml`, you can regroup the settings for all the actions of a module under the `all:` key, also shown in Listing 12-3. 66 67 Listing 12-3 - A Full `cache.yml` Example, in `myapp/modules/user/config/cache.yml` 68 69 list: 70 enabled: on 71 show: 72 enabled: on 73 74 all: 75 with_layout: false # Default value 76 lifetime: 86400 # Default value 77 78 Now, every call to the `user/show` action with a different `id` argument creates a new record in the cache. So the cache for this: 79 80 http://myapp.example.com/user/show/id/12 81 82 will be different than the cache for this: 83 84 http://myapp.example.com/user/show/id/25 85 86 >**CAUTION** 87 >Actions called with a POST method or with GET parameters are not cached. 88 89 The `with_layout` setting deserves a few more words. It actually determines what kind of data is stored in the cache. For the cache without layout, only the result of the template execution and the action variables are stored in the cache. For the cache with layout, the whole response object is stored. This means that the cache with layout is much faster than the cache without it. 90 91 If you can functionally afford it (that is, if the layout doesn't rely on session-dependent data), you should opt for the cache with layout. Unfortunately, the layout often contains some dynamic elements (for instance, the name of the user who is connected), so action cache without layout is the most common configuration. However, RSS feeds, pop-ups, and pages that don't depend on cookies can be cached with their layout. 92 93 ### Caching a Partial, Component, or Component Slot 94 95 Chapter 7 explained how to reuse code fragments across several templates, using the `include_partial()` helper. A partial is as easy to cache as an action, and its cache activation follows the same rules, as shown in Figure 12-2. 96 97 Figure 12-2 - Caching a partial, component, or component slot 98 99  100 101 For instance, Listing 12-4 shows how to edit the `cache.yml` file to enable the cache on a `_my_partial.php` partial located in the `user` module. Note that the `with_layout` setting doesn't make sense in this case. 102 103 Listing 12-4 - Caching a Partial, in `myapp/modules/user/config/cache.yml` 104 105 _my_partial: 106 enabled: on 107 list: 108 enabled: on 109 ... 110 111 Now all the templates using this partial won't actually execute the PHP code of the partial, but will use the cached version instead. 112 113 [php] 114 <?php include_partial('user/my_partial') ?> 115 116 Just as for actions, partial caching is also relevant when the result of the partial depends on parameters. The cache system will store as many versions of a template as there are different values of parameters. 117 118 [php] 119 <?php include_partial('user/my_other_partial', array('foo' => 'bar')) ?> 120 121 >**TIP** 122 >The action cache is more powerful than the partial cache, since when an action is cached, the template is not even executed; if the template contains calls to partials, these calls are not performed. Therefore, partial caching is useful only if you don't use action caching in the calling action or for partials included in the layout. 123 124 A little reminder from Chapter 7: A component is a light action put on top of a partial, and a component slot is a component for which the action varies according to the calling actions. These two inclusion types are very similar to partials, and support caching in the same way. For instance, if your global layout includes a component called `day` with `include_component('general/day')` in order to show the current date, set the `cache.yml` file of the `general` module as follows to enable the cache on this component: 125 126 _day: 127 enabled: on 128 129 When caching a component or a partial, you must decide whether to store a single version for all calling templates or a version for each template. By default, a component is stored independently of the template that calls it. But contextual components, such as a component that displays a different sidebar with each action, should be stored as many times as there are templates calling it. The caching system can handle this case, provided that you set the `contextual` parameter to `true`, as follows: 130 131 _day: 132 contextual: true 133 enabled: on 134 135 >**NOTE** 136 >Global components (the ones located in the application `templates/` directory) can be cached, provided that you declare their cache settings in the application `cache.yml`. 137 138 ### Caching a Template Fragment 139 140 Action caching applies to only a subset of actions. For the other actions--those that update data or display session-dependent information in the template--there is still room for cache improvement but in a different way. Symfony provides a third cache type, which is dedicated to template fragments and enabled directly inside the template. In this mode, the action is always executed, and the template is split into executed fragments and fragments in the cache, as illustrated in Figure 12-3. 141 142 Figure 12-3 - Caching a template fragment 143 144  145 146 For instance, you may have a list of users that shows a link of the last-accessed user, and this information is dynamic. The `cache()` helper defines the parts of a template that are to be put in the cache. See Listing 12-5 for details on the syntax. 147 148 Listing 12-5 - Using the `cache()` Helper, in `myapp/modules/user/templates/listSuccess.php` 149 150 [php] 151 <!-- Code executed each time --> 152 <?php echo link_to('last accessed user', 'user/show?id='.$last_accessed_user_id) ?> 153 154 <!-- Cached code --> 155 <?php if (!cache('users')): ?> 156 <?php foreach ($users as $user): ?> 157 <?php echo $user->getName() ?> 158 <?php endforeach; ?> 159 <?php cache_save() ?> 160 <?php endif; ?> 161 162 Here's how it works: 163 164 * If a cached version of the fragment named 'users' is found, it is used to replace the code between the <?php if (!cache($unique_fragment_name)): ?> and the <?php endif; ?> lines. 165 * If not, the code between these lines is processed and saved in the cache, identified with the unique fragment name. 166 167 The code not included between such lines is always processed and not cached. 168 169 >**CAUTION** 170 >The action (`list` in the example) must not have caching enabled, since this would bypass the whole template execution and ignore the fragment cache declaration. 171 172 The speed boost of using the template fragment cache is not as significant as with the action cache, since the action is always executed, the template is partially processed, and the layout is always used for decoration. 173 174 You can declare additional fragments in the same template; however, you need to give each of them a unique name so that the symfony cache system can find them afterwards. 175 176 As with actions and components, cached fragments can take a lifetime in seconds as a second argument of the call to the `cache()` helper. 177 178 [php] 179 <?php if (!cache('users', 43200)): ?> 180 181 The default cache lifetime (86400 seconds, or one day) is used if no parameter is given to the helper. 182 183 >**TIP** 184 >Another way to make an action cacheable is to insert the variables that make it vary into the action's routing pattern. For instance, if a home page displays the name of the connected user, it cannot be cached unless the URL contains the user nickname. Another example is for internationalized applications: If you want to enable caching on a page that has several translations, the language code must somehow be included in the URL pattern. This trick will multiply the number of pages in the cache, but it can be of great help to speed up heavily interactive applications. 185 186 ### Configuring the Cache Dynamically 187 188 The `cache.yml` file is one way to define cache settings, but it has the inconvenience of being invariant. However, as usual in symfony, you can use plain PHP rather than YAML, and that allows you to configure the cache dynamically. 189 190 Why would you want to change the cache settings dynamically? A good example is a page that is different for authenticated users and for anonymous ones, but the URL remains the same. Imagine an `article/show` page with a rating system for articles. The rating feature is disabled for anonymous users. For those users, rating links trigger the display of a login form. This version of the page can be cached. On the other hand, for authenticated users, clicking a rating link makes a POST request and creates a new rating. This time, the cache must be disabled for the page so that symfony builds it dynamically. 191 192 The right place to define dynamic cache settings is in a filter executed before the `sfCacheFilter`. Indeed, the cache is a filter in symfony, just like the web debug toolbar and the security features. In order to enable the cache for the `article/show` page only if the user is not authenticated, create a `conditionalCacheFilter` in the application `lib/` directory, as shown in Listing 12-6. 193 194 Listing 12-6 - Configuring the Cache in PHP, in `myapp/lib/conditionalCacheFilter.class.php` 195 196 [php] 197 class conditionalCacheFilter extends sfFilter 198 { 199 public function execute($filterChain) 200 { 201 $context = $this->getContext(); 202 if (!$context->getUser()->isAuthenticated()) 203 { 204 foreach ($this->getParameter('pages') as $page) 205 { 206 $context->getViewCacheManager()->addCache($page['module'], $page['action'],array('lifeTime' => 86400)); 207 } 208 } 209 210 // Execute next filter 211 $filterChain->execute(); 212 } 213 } 214 215 You must register this filter in the `filters.yml` file before the `sfCacheFilter`, as shown in Listing 12-7. 216 217 Listing 12-7 - Registering Your Custom Filter, in `myapp/config/filters.yml` 218 219 ... 220 security: ~ 221 222 conditionalCache: 223 class: conditionalCacheFilter 224 param: 225 pages: 226 - { module: article, action: show } 227 228 cache: ~ 229 ... 230 231 Clear the cache (to autoload the new filter class), and the conditional cache is ready. It will enable the cache of the pages defined in the pages parameter only for users who are not authenticated. 232 233 The `addCache()` method of the `sfViewCacheManager` object expects a module name, an action name, and an associative array with the same parameters as the ones you would define in a `cache.yml` file. For instance, if you want to define that the `article/show` action must be cached with the layout and with a lifetime of 3600 seconds, then write the following: 234 235 [php] 236 $context->getViewCacheManager()->addCache('article', 'show', array( 237 'withLayout' => true, 238 'lifeTime' => 3600, 239 )); 240 241 >**SIDEBAR** 242 >Alternative Caching storage 243 > 244 >By default, the symfony cache system stores data in files on the web server hard disk. You may want to store cache in memory (for instance, via memcache) or in a database (notably if you want to share your cache among several servers or speed up cache removal). You can easily alter symfony's default cache storage system because the cache class used by the symfony view cache manager is defined in `factories.yml`. 245 > 246 >The default view cache storage factory is the `sfFileCache` class: 247 > 248 > view_cache: 249 > class: sfFileCache 250 > param: 251 > automaticCleaningFactor: 0 252 > cacheDir: %SF_TEMPLATE_CACHE_DIR% 253 > 254 >You can replace the `class` with your own cache storage class or with one of the symfony alternative classes (`sfSQLiteCache` for instance). The parameters defined under the `param` key are passed to the `initialize()` method of your class as an associative array. Any view cache storage class must imple- ment all methods found in the abstract `sfCache` class. Refer to the API documentation ([http://www. symfony-project.com/api/symfony.html](http://www. symfony-project.com/api/symfony.html)) for more information on this subject. 255 256 ### Using the Super Fast Cache 257 258 Even a cached page involves some PHP code execution. For such a page, symfony still loads the configuration, builds the response, and so on. If you are really sure that a page is not going to change for a while, you can bypass symfony completely by putting the resulting HTML code directly into the `web/` folder. This works thanks to the Apache `mod_rewrite` settings, provided that your routing rule specifies a pattern ending without a suffix or with `.html`. 259 260 You can do this by hand, page by page, with a simple command-line call: 261 262 > curl http://myapp.example.com/user/list.html > web/user/list.html 263 264 After that, every time that the `user/list` action is requested, Apache finds the corresponding `list.html` page and bypasses symfony completely. The trade-off is that you can't control the page cache with symfony anymore (lifetime, automatic deletion, and so on), but the speed gain is very impressive. 265 266 Alternatively, you can use the `sfSuperCache` symfony plug-in, which automates the process and supports lifetime and cache clearing. Refer to Chapter 17 for more information about plug-ins. 267 268 >**SIDEBAR** 269 >Other Speedup tactics 270 > 271 >In addition to the HTML cache, symfony has two other cache mechanisms, which are completely automated and transparent to the developer. In the production environment, the configuration and the template translations are cached in files stored in the myproject/cache/config/ and myproject/cache/i18n/ directories without any intervention. 272 > 273 >PHP accelerators (eAccelerator, APC, XCache, and so on), also called opcode caching modules, increase performance of PHP scripts by caching them in a compiled state, so that the overhead of code parsing and compiling is almost completely eliminated. This is particularly effective for the Propel classes, which contain a great amount of code. These accelerators are compatible with symfony and can easily triple the speed of an application. They are recommended in production environments for any symfony application with a large audience. 274 > 275 >With a PHP accelerator, you can manually store persistent data in memory, to avoid doing the same processing for each request, with the `sfProcessCache` class. And if you want to store the result of a CPU-intensive function in a file, you will probably use the `sfFunctionCache` object. Refer to Chapter 18 for more information about these mechanisms. 276 277 Removing Items from the Cache 278 ----------------------------- 279 280 If the scripts or the data of your application change, the cache will contain outdated information. To avoid incoherence and bugs, you can remove parts of the cache in many different ways, according to your needs. 281 282 ### Clearing the Entire Cache 283 284 The `clear-cache` task of the symfony command line erases the cache (HTML, configuration, and i18n cache). You can pass it arguments to erase only a subset of the cache, as shown in Listing 12-8. Remember to call it only from the root of a symfony project. 285 286 Listing 12-8 - Clearing the Cache 287 288 // Erase the whole cache 289 > symfony clear-cache 290 291 // Short syntax 292 > symfony cc 293 294 // Erase only the cache of the myapp application 295 > symfony clear-cache myapp 296 297 // Erase only the HTML cache of the myapp application 298 > symfony clear-cache myapp template 299 300 // Erase only the configuration cache of the myapp application 301 > symfony clear-cache myapp config 302 303 ### Clearing Selective Parts of the Cache 304 305 When the database is updated, the cache of the actions related to the modified data must be cleared. You could clear the whole cache, but that would be a waste for all the existing cached actions that are unrelated to the model change. This is where the `remove()` method of the `sfViewCacheManager` object applies. It expects an internal URI as argument (the same kind of argument you would provide to a `link_to()`), and removes the related action cache. 306 307 For instance, imagine that the `update` action of the `user` module modifies the columns of a `User` object. The cached versions of the `list` and `show` actions need to be cleared, or else the old versions, which contain erroneous data, are displayed. To handle this, use the `remove()` method, as shown in Listing 12-9. 308 309 Listing 12-9 - Clearing the Cache for a Given Action, in `modules/user/actions/actions.class.php` 310 311 [php] 312 public function executeUpdate() 313 { 314 // Update a user 315 $user_id = $this->getRequestParameter('id'); 316 $user = UserPeer::retrieveByPk($user_id); 317 $this->foward404Unless($user); 318 $user->setName($this->getRequestParameter('name')); 319 ... 320 $user->save(); 321 322 // Clear the cache for actions related to this user 323 $cacheManager = $this->getContext()->getViewCacheManager(); 324 $cacheManager->remove('user/list'); 325 $cacheManager->remove('user/show?id='.$user_id); 326 ... 327 } 328 329 Removing cached partials, components, and component slots is a little trickier. As you can pass them any type of parameter (including objects), it is almost impossible to identify their cached version after the fact. Let's focus on partials, as the explanation is the same for the other template components. Symfony identifies a cached partial with a special prefix (`sf_cache_partial`), the name of the module, and the name of the partial, plus a hash of all the parameters used to call it, as follows: 330 331 [php] 332 // A partial called by 333 <?php include_partial('user/my_partial', array('user' => $user) ?> 334 335 // Is identified in the cache as 336 /sf_cache_partial/user/_my_partial/sf_cache_key/bf41dd9c84d59f3574a5da244626dcc8 337 338 In theory, you could remove a cached partial with the `remove()` method if you knew the value of the parameters hash used to identify it, but this is very impracticable. Fortunately, if you add a `sf_cache_key` parameter to the `include_partial()` helper call, you can identify the partial in the cache with something that you know. As you can see in Listing 12-10, clearing a single cached partial--for instance, to clean up the cache from the partial based on a modified `User`--becomes easy. 339 340 Listing 12-10 - Clearing Partials from the Cache 341 342 [php] 343 <?php include_partial('user/my_partial', array( 344 'user' => $user, 345 'sf_cache_key' => $user->getId() 346 ) ?> 347 348 // Is identified in the cache as 349 /sf_cache_partial/user/_my_partial/sf_cache_key/12 350 351 // Clear _my_partial for a specific user in the cache with $cacheManager->remove('@sf_cache_partial?module=user&action=_my_partial&sf_cache_key='.$user->getId()); 352 353 You cannot use this method to clear all occurrences of a partial in the cache. You will learn how to clear these in the "Clearing the Cache Manually" section later in this chapter. 354 355 To clear template fragments, use the same `remove()` method. The key identifying the fragment in the cache is composed of the same `sf_cache_partial` prefix, the module name, the action name, and the `sf_cache_key` (the unique name of the cache fragment included by the `cache()` helper). Listing 12-11 shows an example. 356 357 Listing 12-11 - Clearing Template Fragments from the Cache 358 359 [php] 360 <!-- Cached code --> 361 <?php if (!cache('users')): ?> 362 ... // Whatever 363 <?php cache_save() ?> 364 <?php endif; ?> 365 366 // Is identified in the cache as 367 /sf_cache_partial/user/list/sf_cache_key/users 368 369 // Clear it with 370 $cacheManager->remove('@sf_cache_partial?module=user&action=list&sf_cache_key=users'); 371 372 >**SIDEBAR** 373 >Selective Cache Clearing Can damage your Brain 374 > 375 >The trickiest part of the cache-clearing job is to determine which actions are influenced by a data update. 376 > 377 >For instance, imagine that the current application has a `publication` module where publications are listed (`list` action) and described (`show` action), along with the details of their author (an instance of the `User` class). Modifying one User record will affect all the descriptions of the user's publications and the list of publications. This means that you need to add to the `update` action of the `user` module, something like this: 378 > 379 > [php] 380 > $c = new Criteria(); 381 > $c->add(PublicationPeer::AUTHOR_ID, $this->getRequestParameter('id')); 382 > $publications = PublicationPeer::doSelect($c); 383 > 384 > $cacheManager = sfContext::getInstance()->getViewCacheManager(); 385 > foreach ($publications as $publication) 386 > { 387 > $cacheManager->remove('publication/show?id='.$publication->getId()); 388 > } 389 > $cacheManager->remove('publication/list'); 390 > 391 >When you start using the HTML cache, you need to keep a clear view of the dependencies between the model and the actions, so that new errors don't appear because of a misunderstood relationship. Keep in mind that all the actions that modify the model should probably contain a bunch of calls to the `remove()` method if the HTML cache is used somewhere in the application. 392 > 393 >And, if you don't want to damage your brain with too difficult an analysis, you can always clear the whole cache each time you update the database . . . 394 395 ### Cache Directory Structure 396 397 The `cache/` directory of your application has the following structure: 398 399 cache/ # sf_root_cache_dir 400 [APP_NAME]/ # sf_base_cache_dir 401 [ENV_NAME]/ # sf_cache_dir 402 config/ # sf_config_cache_dir 403 i18n/ # sf_i18n_cache_dir 404 modules/ # sf_module_cache_dir 405 template/ # sf_template_cache_dir 406 [HOST_NAME]/ 407 all/ 408 409 Cached templates are stored under the `[HOST_NAME]` directory (where dots are replaced by underscores for compatibility with file systems), in a directory structure corresponding to their URL. For instance, the template cache of a page called with: 410 411 http://www.myapp.com/user/show/id/12 412 413 is stored in: 414 415 cache/myapp/prod/template/www_myapp_com/all/user/show/id/12.cache 416 417 You should not write file paths directly in your code. Instead, you can use the file path constants. For instance, to retrieve the absolute path to the `template/` directory of the current application in the current environment, use `sfConfig::get('sf_template_cache_dir')`. 418 419 Knowing this directory structure will help you deal with manual cache clearing. 420 421 ### Clearing the Cache Manually 422 423 Clearing the cache across applications can be a problem. For instance, if an administrator modifies a record in the `user` table in a `backend` application, all the actions depending on this user in the `frontend` application need to be cleared from the cache. The `remove()` method expects an internal URI, but applications don't know other application's routing rules (applications are isolated from each other), so you cannot use the `remove()` method to clear the cache of another application. 424 425 The solution is to manually remove the files from the `cache/` directory, based on a file path. For instance, if the `backend` application needs to clear the cache of the `user/show` action in the `frontend` application for the user of `id` `12`, it can use the following: 426 427 [php] 428 $sf_root_cache_dir = sfConfig::get('sf_root_cache_dir'); 429 $cache_dir = $sf_root_cache_dir.'/frontend/prod/template/www_myapp_com/all'; 430 unlink($cache_dir.'/user/show/id/12.cache'); 431 432 But this is not very satisfactory. This command will erase only the cache of the current environment, and it forces you to write the environment name and the current host name in the file path. To bypass these limitations, you can use the `sfToolkit::clearGlob()` method. It takes a file pattern as a parameter and accepts wildcards. For instance, you can clear the same cache files as in the preceding example, regardless of host and environment, with this: 433 434 [php] 435 $cache_dir = $sf_root_cache_dir.'/frontend/*/template/*/all'; 436 sfToolkit::clearGlob($cache_dir.'/user/show/id/12.cache'); 437 438 This method is also of great use when you need to erase a cached action regardless of certain parameters. For instance, if your application handles several languages, you may have chosen to insert the language code in all URLs. So the link to a user profile page should look like this: 439 440 http://www.myapp.com/en/user/show/id/12 441 442 To remove the cached profile of the user having an `id` of `12` in all languages, you can simply do this: 443 444 [php] 445 sfToolkit::clearGlob($cache_dir.'/*/user/show/id/12.cache'); 446 447 Testing and Monitoring Caching 448 ------------------------------ 449 450 HTML caching, if not properly handled, can create incoherence in displayed data. Each time you disable the cache for an element, you should test it thoroughly and monitor the execution boost to tweak it. 451 452 ### Building a Staging Environment 453 454 The caching system is prone to new errors in the production environment that can't be detected in the development environment, since the HTML cache is disabled by default in development. If you enable the HTML cache for some actions, you should add a new environment, called staging in this section, with the same settings as the `prod` environment (thus, with cache enabled) but with `web_debug` set to `on`. 455 456 To set it up, edit the `settings.yml` file of your application and add the lines shown in Listing 12-12 at the top. 457 458 Listing 12-12 - Settings for a `staging` Environment, in `myapp/config/settings.yml` 459 460 staging: 461 .settings: 462 web_debug: on 463 cache: on 464 465 In addition, create a new front controller by copying the production one (probably `myproject/web/index.php`) to a new `myapp_staging.php`. Edit it to change the `SF_ENVIRONMENT` and `SF_DEBUG` values, as follows: 466 467 [php] 468 define('SF_ENVIRONMENT', 'staging'); 469 define('SF_DEBUG', true); 470 471 That's it--you have a new environment. Use it by adding the front controller name after the domain name: 472 473 http://myapp.example.com/myapp_staging.php/user/list 474 475 >**TIP** 476 >Instead of copying an existing one, you can create a new front controller with the `symfony` command line. For instance, to create a `staging` environment for the `myapp` application, called `myapp_staging.php` and where `SF_DEBUG` is `true`, just call `symfony init-controller myapp staging myapp_staging.php true`. 477 478 ### Monitoring Performance 479 480 Chapter 16 will explore the web debug toolbar and its contents. However, as this toolbar offers valuable information about cached elements, here is a brief description of its cache features. 481 482 When you browse to a page that contains cacheable elements (action, partials, fragments, and so on), the web debug toolbar (in the top-right corner of the window) shows an ignore cache button (a green, rounded arrow), as shown in Figure 12-4. This button reloads the page and forces the processing of cached elements. Be aware that it does not clear the cache. 483 484 The last number on the right side of the debug toolbar is the duration of the request execution. If you enable cache on a page, this number should decrease the second time you load the page, since symfony uses the data from the cache instead of reprocessing the scripts. You can easily monitor the cache improvements with this indicator. 485 486 Figure 12-4 - Web debug toolbar for pages using caching 487 488  489 490 The debug toolbar also shows the number of database queries executed during the processing of the request, and the detail of the durations per category (click the total duration to display the detail). Monitoring this data, in conjunction with the total duration, will help you do fine measures of the performance improvements brought by the cache. 491 492 ### Benchmarking 493 494 The debug mode greatly decreases the speed of your application, since a lot of information is logged and made available to the web debug toolbar. So the processed time displayed when you browse in the `staging` environment is not representative of what it will be in production, where the debug mode is turned `off`. 495 496 To get a better view of the process time of each request, you should use benchmarking tools, like Apache Bench or JMeter. These tools allow load testing and provide two important pieces of information: the average loading time of a specific page and the maximum capacity of your server. The average loading time data is very useful for monitoring performance improvements due to cache activation. 497 498 ### Identifying Cache Parts 499 500 When the web debug toolbar is enabled, the cached elements are identified in a page with a red frame, each having a cache information box on the top left, as shown in Figure 12-5. The box has a blue background if the element has been executed, or a yellow background if it comes from the cache. Clicking the cache information link displays the identifier of the cache element, its lifetime, and the elapsed time since its last modification. This will help you identify problems when dealing with out-of-context elements, to see when the element was created and which parts of a template you can actually cache. 501 502 Figure 12-5 - Identification for cached elements in a page 503 504  505 506 HTTP 1.1 and Client-Side Caching 507 -------------------------------- 508 509 The HTTP 1.1 protocol defines a bunch of headers that can be of great use to further speed up an application by controlling the browser's cache system. 510 511 The HTTP 1.1 specifications of the World Wide Web Consortium (W3C, [http://www. w3.org/Protocols/rfc2616/rfc2616-sec14.html](http://www. w3.org/Protocols/rfc2616/rfc2616-sec14.html)) describe these headers in detail. If an action has caching enabled, and it uses the `with_layout` option, it can use one or more of the mechanisms described in the following sections. 512 513 Even if some of the browsers of your website's users may not support HTTP 1.1, there is no risk in using the HTTP 1.1 cache features. A browser receiving headers that it doesn't understand simply ignores them, so you are advised to set up the HTTP 1.1 cache mechanisms. 514 515 In addition, HTTP 1.1 headers are also understood by proxies and caching servers. Even if a user's browser doesn't understand HTTP 1.1, there can be a proxy in the route of the request to take advantage of it. 516 517 ### Adding an ETag Header to Avoid Sending Unchanged Content 518 519 When the ETag feature is enabled, the web server adds to the response a special header containing a signature of the response itself. 520 521 ETag: 1A2Z3E4R5T6Y7U 522 523 The user's browser will store this signature, and send it again together with the request the next time it needs the same page. If the new signature shows that the page didn't change since the first request, the browser doesn't send the response back. Instead, it just sends a `304: Not modified` header. It saves CPU time (if gzipping is enabled for example) and bandwidth (page transfer) for the server, and time (page transfer) for the client. Overall, pages in a cache with an ETag are even faster to load than pages in a cache without an ETag. 524 525 In symfony, you enable the ETag feature for the whole application in `settings.yml`. Here is the default ETag setting: 526 527 all: 528 .settings: 529 etag: on 530 531 For actions in a cache with layout, the response is taken directly from the `cache/` directory, so the process is even faster. 532 533 ### Adding a Last-Modified Header to Avoid Sending Still Valid Content 534 535 When the server sends the response to the browser, it can add a special header to specify when the data contained in the page was last changed: 536 537 Last-Modified: Sat, 23 Nov 2006 13:27:31 GMT 538 539 Browsers can understand this header and, when requesting the page again, add an `If-Modified` header accordingly: 540 541 If-Modified-Since: Sat, 23 Nov 2006 13:27:31 GMT 542 543 The server can then compare the value kept by the client and the one returned by its application. If they match, the server returns a `304: Not modified` header, saving bandwidth and CPU time, just as with ETags. 544 545 In symfony, you can set the `Last-Modified` response header just as you would for another header. For instance, you can use it like this in an action: 546 547 [php] 548 $this->getResponse()->setHttpHeader('Last-Modified', $this->getResponse()->getDate($timestamp)); 549 550 This date can be the actual date of the last update of the data used in the page, given from your database or your file system. The `getDate()` method of the `sfResponse` object converts a timestamp to a formatted date in the format needed for the `Last-Modified` header (RFC1123). 551 552 ### Adding Vary Headers to Allow Several Cached Versions of a Page 553 554 Another HTTP 1.1 header is `Vary`. It defines which parameters a page depends on, and is used by browsers and proxies to build cache keys. For example, if the content of a page depends on cookies, you can set its `Vary` header as follows: 555 556 Vary: Cookie 557 558 Most often, it is difficult to enable caching on actions because the page may vary according to the cookie, the user language, or something else. If you don't mind expanding the size of your cache, set the `Vary` header of the response properly. This can be done for the whole application or on a per-action basis, using the `cache.yml` configuration file or the `sfResponse` related method as follows: 559 560 [php] 561 $this->getResponse()->addVaryHttpHeader('Cookie'); 562 $this->getResponse()->addVaryHttpHeader('User-Agent'); 563 $this->getResponse()->addVaryHttpHeader('Accept-Language'); 564 565 Symfony will store a different version of the page in the cache for each value of these parameters. This will increase the size of the cache, but whenever the server receives a request matching these headers, the response is taken from the cache instead of being processed. This is a great performance tool for pages that vary only according to request headers. 566 567 ### Adding a Cache-Control Header to Allow Client-Side Caching 568 569 Up to now, even by adding headers, the browser keeps sending requests to the server even if it holds a cached version of the page. You can avoid that by adding `Cache-Control` and `Expires` headers to the response. These headers are disabled by default in PHP, but symfony can override this behavior to avoid unnecessary requests to your server. 570 571 As usual, you trigger this behavior by calling a method of the `sfResponse` object. In an action, define the maximum time a page should be cached (in seconds): 572 573 [php] 574 $this->getResponse()->addCacheControlHttpHeader('max_age=60'); 575 576 You can also specify under which conditions a page may be cached, so that the provider's cache does not keep a copy of private data (like bank account numbers): 577 578 [php] 579 $this->getResponse()->addCacheControlHttpHeader('private=True'); 580 581 Using `Cache-Control` HTTP directives, you get the ability to fine-tune the various cache mechanisms between your server and the client's browser. For a detailed review of these directives, see the W3C `Cache-Control` specifications. 582 583 One last header can be set through symfony: the `Expires` header: 584 585 [php] 586 $this->getResponse()->setHttpHeader('Expires', $this->getResponse()->getDate($timestamp)); 587 588 >**CAUTION** 589 >The major consequence of turning on the `Cache-Control` mechanism is that your server logs won't show all the requests issued by the users, but only the ones actually received. If the performance gets better, the apparent popularity of the site may decrease in the statistics. 590 591 Summary 592 ------- 593 594 The cache system provides variable performance boosts according to the cache type selected. From the best gain to the least, the cache types are as follows: 595 596 * Super cache 597 * Action cache with layout 598 * Action cache without layout 599 * Fragment cache in the template 600 601 In addition, partials and components can be cached as well. 602 603 If changing data in the model or in the session forces you to erase the cache for the sake of coherence, you can do it with a fine granularity for optimum performance--erase only the elements that have changed, and keep the others. 604 605 Remember to test all the pages where caching is enabled with extra care, as new bugs may appear if you cache the wrong elements or if you forget to clear the cache when you update the underlying data. A staging environment, dedicated to cache testing, is of great use for that purpose. 606 607 Finally, make the best of the HTTP 1.1 protocol with symfony's advanced cache-tweaking features, which will involve the client in the caching task and provide even more performance gains.
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |