The definitive guide of Symfony 1.0

12.2. Removing Items from the Cache

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.

12.2.1. Clearing the Entire Cache

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.

Listing 12-8 - Clearing the Cache

// Erase the whole cache
> symfony clear-cache

// Short syntax
> symfony cc

// Erase only the cache of the myapp application
> symfony clear-cache myapp

// Erase only the HTML cache of the myapp application
> symfony clear-cache myapp template

// Erase only the configuration cache of the myapp application
> symfony clear-cache myapp config

12.2.2. Clearing Selective Parts of the Cache

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.

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.

Listing 12-9 - Clearing the Cache for a Given Action, in modules/user/actions/actions.class.php

public function executeUpdate()
{
  // Update a user
  $user_id = $this->getRequestParameter('id');
  $user = UserPeer::retrieveByPk($user_id);
  $this->foward404Unless($user);
  $user->setName($this->getRequestParameter('name'));
  ...
  $user->save();

  // Clear the cache for actions related to this user
  $cacheManager = $this->getContext()->getViewCacheManager();
  $cacheManager->remove('user/list');
  $cacheManager->remove('user/show?id='.$user_id);
  ...
}

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:

// A partial called by
<?php include_partial('user/my_partial', array('user' => $user) ?>

// Is identified in the cache as
/sf_cache_partial/user/_my_partial/sf_cache_key/bf41dd9c84d59f3574a5da244626dcc8

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.

Listing 12-10 - Clearing Partials from the Cache

<?php include_partial('user/my_partial', array(
  'user'         => $user,
  'sf_cache_key' => $user->getId()
) ?>

// Is identified in the cache as
/sf_cache_partial/user/_my_partial/sf_cache_key/12

// 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());

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.

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.

Listing 12-11 - Clearing Template Fragments from the Cache

<!-- Cached code -->
<?php if (!cache('users')): ?>
  ... // Whatever
  <?php cache_save() ?>
<?php endif; ?>

// Is identified in the cache as
/sf_cache_partial/user/list/sf_cache_key/users

// Clear it with
$cacheManager->remove('@sf_cache_partial?module=user&action=list&sf_cache_key=users');

12.2.3. Cache Directory Structure

The cache/ directory of your application has the following structure:

cache/                 # sf_root_cache_dir
  [APP_NAME]/          # sf_base_cache_dir
    [ENV_NAME]/        # sf_cache_dir
      config/          # sf_config_cache_dir
      i18n/            # sf_i18n_cache_dir
      modules/         # sf_module_cache_dir
      template/        # sf_template_cache_dir
        [HOST_NAME]/
          all/

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:

http://www.myapp.com/user/show/id/12

is stored in:

cache/myapp/prod/template/www_myapp_com/all/user/show/id/12.cache

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').

Knowing this directory structure will help you deal with manual cache clearing.

12.2.4. Clearing the Cache Manually

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.

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:

$sf_root_cache_dir = sfConfig::get('sf_root_cache_dir');
$cache_dir = $sf_root_cache_dir.'/frontend/prod/template/www_myapp_com/all';
unlink($cache_dir.'/user/show/id/12.cache');

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:

$cache_dir = $sf_root_cache_dir.'/frontend/*/template/*/all';
sfToolkit::clearGlob($cache_dir.'/user/show/id/12.cache');

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:

http://www.myapp.com/en/user/show/id/12

To remove the cached profile of the user having an id of 12 in all languages, you can simply do this:

sfToolkit::clearGlob($cache_dir.'/*/user/show/id/12.cache');