Development

HowToCacheCustomConfiguration

You must first sign up to be able to contribute.

Version 15 (modified by Kostas.Papadimitriou, 10 years ago)
--

How to cache custom configuration

Available in official documentation

Author's notes: This page was written before the official book release. Now there is official documentation coverage on this topic.

Introduction

Symfony offers a apps.yml file that you can put configurable values there. But you may sometime find yourself in need to have your own configuration file in your application. You probably know about parsing your own yaml file by:

$myarray = sfYaml::load('/tmp/myfile.yml');

(http://www.symfony-project.com/content/book/page/configuration_practice.html)

But this does no caching, unlike most of the symfony configuration files. This page demostrates a further step to make your confiuration files cached, in symfony way.

General information on caching

We will take a brief look on how caching works. You can open any files inside (note that path name may vary) [your_project_dir]\cache\[app_name]\dev\config. The cached files are named in underscore-seperated directory hierachy. So file [your_project_dir]\modules\cart\config\cart_config.yml will be named modules_cart_config_cart_config.yml.php. The files are nothing more than a normal php script file. The original "loading -> parsing -> processing" flow is converted to a simple php file include (or include_once), which saves time.

When you ask to load a configuration file through the config handling class the very first time (or when the configuation file is modified), the "loading -> parsing -> processing" flow is inevitable. After processing the source yaml configuration file, it is "compiled" into a php format which is ready for inclusion so that the config manager returns the compiled file path immediately when the same file is requested.

You now know the difference: instead of getting the array structure of the configuration file, you get the cached file path that can be included. The good thing is that the built-in configuration class already helps a lot so you won't need to take care of it from top to bottom.

You won't need to care about:

  • where to find the cache file
  • whether a cache file exist
  • when the config file should be compiled

But you still need to instruct symfony on:

  • how to parse the config file
  • how to generate compiled output

The file generated will like other cache configuration files -- you can clear the cache by calling "symfony cc" or "symfony clear-cache".

To tell symfony handles your configuration file properly you'll have to

  • Create a handler class for your configuration file
  • Tell symfony to handle the configuration file with your handler class

Pretty easy, huh? The magic is the handler class that you are going to write. In addition to the "sfYaml::load" and the parsing stuff, this class should also return a parsed structure which should be valid PHP code, that's why you can use "include" (or include_once) to the cached file. (This also means that if you write invalid codes, the application will fail!)

Caching configuration in practice

Let's start by telling symfony to handle your foo.yml with your handler class, say, myFooConfigHandler. You do this by creating a config_handlers.yml file in config directory. This can be under:

  1. Your project directory ([my_project]\config)
  2. Your application directory ([my_project]\apps\[application_name]\config), or
  3. Your module directory ([my_project]\apps\[application_name]\modules\[module_name]\config)

** you can specify config files for all your modules you can use modules/*/config/foo.yml

Note that this can affect where you can put the configuration file foo.yml. For example, if config_handlers.yml is put under the module directory, other modules will not use this configuration (and thus will not use the class you assign) to parse the yml file.

The content of config_handlers.yml is as below:

config/foo.yml:
  class:    myFooConfigHandler

It's quite self explanatory -- files with path config/foo.yml is to be handled by myFooConfigHandler class. config_handlers.yml is autoloaded automatically on every request.

Now it's time to write your handler class myFooConfigHandler. Just like other PHP class files, this is to be placed inside the lib/ directory. If you want all application in the project use this handler, save it inside [my_project]\lib with the name myFooConfigHandler.class.php.

Here is the content for myFooConfigHandler.class.php. This is actually taken from the symfony configuration handler source code. Look at the [symfony library path]\config" directory to see the working examples.

<?php

/**
 * Foo configuration file handler
 */
class myFooConfigHandler extends sfYamlConfigHandler
{
  /**
   * Execute this configuration handler.
   *
   * @param array An array of absolute filesystem path to a configuration file.
   *
   * @return string Data to be written to a cache file.
   *
   * @throws sfConfigurationException If a requested configuration file does not exist or is not readable.
   * @throws sfParseException If a requested configuration file is improperly formatted.
   */
  public function execute($configFiles)
  {
    // set our required categories list and initialize our handler
    $categories = array('required_categories' => array('bar1', 'bar2'));

    $this->initialize($categories);

    // parse the yaml
    $myConfig = $this->parseYamls($configFiles);

    // init our data array
    $data = array();

    // let's do our fancy work

    // ............ Do your parsing work. Here I assign a simple array for demostration purpose
    $myParsedDataStructure = array('some','configuration','data');

    // compile data
    $retval = sprintf("<?php\n".
                      "// auto-generated by %s\n".
                      "// date: %s\nsfConfig::set('my_app_foo', \n%s\n);\n?>",
                      __CLASS__, date('Y/m/d H:i:s'), var_export($myParsedDataStructure,true));

    return $retval;
  }
}

?>

This class extends from the sfYamlConfigHandler class. required_categories option is set so that the parent class will throw an exception if any required category is not found from the configuration file. parseYamls is called instead of the old $myarray = sfYaml::load('/config/foo.yml');, but they both return the same array structure, so you can mostly transfer your ordinary parsing code without pain. Finally, the "compiled" data $retval is returned and will be written to configuration cache by the parent class. And yes, you only need to return the code, others are done in behind!

In this example the content of the cache file will be:

sfConfig::set('my_app_foo', array('some','configuration','data'));

but you can virtually do anything you want. You may take a look at the sfViewConfigHandler.class.php in symfony library's config directory, in which you'll see the handler class not only assigns the data structure but also sets up the environment in the cache file.

Using cache file

Lastly, you can get the config file using the code

$path = sfConfigCache::getInstance()->checkConfig('modules/module_name/config/foo.yml');

Or simply include it by using

include_once (sfConfigCache::getInstance()->checkConfig('modules/module_name/config/foo.yml'));

And everything is done.

How to manage your own cross-apps/global configuration values

This section has moved here : HowToHandleCrossAppsConfigurationFiles