[ Index ]
 

Code source de Symfony 1.0.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/doc/ -> 12-Caching.txt (source)

   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  ![Caching an action](/images/book/F1201.png "Caching an action")
  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  ![Caching a partial, component, or component slot](/images/book/F1202.png "Caching a partial, component, or component slot")
 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  ![Caching a template fragment](/images/book/F1203.png "Caching a template fragment")
 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  ![Web debug toolbar for pages using caching](/images/book/F1204.png "Web debug toolbar for pages using caching")
 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  ![Identification for cached elements in a page](/images/book/F1205.png "Identification for cached elements in a page")
 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.


Généré le : Fri Mar 16 22:42:14 2007 par Balluche grâce à PHPXref 0.7