The Symfony2 Config Component provides several classes to help you find, load, combine, autofill and validate configuration values of any kind, whatever their source may be (Yaml, XML, INI files, or for instance a database).
Locating resources
Loading the configuration normally starts with a search for resources - in most cases: files. This can be done with FileLocator:
use Symfony\Component\Config\FileLocator;
$configDirectories = array(__DIR__ . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config');
$locator = new FileLocator($configDirectories);
$yamlUserFiles = $locator->locate('users.yml', null, false);
The locator receives a collection of locations where it should look for files. The first argument of locate()
is the name of the file to look for. The second argument may be the current path and when supplied, the locator will look in this directory first.
The third argument indicates whether or not the locator should return the first file it has found, or an array containing all matches.
Resource loaders
For each type of resource (Yaml, XML, annotation, etc.) a loader must be defined. Each loader should implement LoaderInterface or extend the abstract FileLoader class, which allows for recursively importing other resources.
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Yaml\Yaml;
class YamlUserLoader extends FileLoader
{
public function load($resource, $type = null)
{
$configValues = Yaml::parse($resource);
// handle the config values
// maybe import some other resource:
// $this->import('extra_users.yml');
}
public function supports($resource, $type = null)
{
return is_string($resource) && 'yml' === pathinfo($resource, PATHINFO_EXTENSION);
}
}
Find the right loader
The LoaderResolver receives as it's first constructor argument a collection of loaders. When asked to find the right loader for some resource, the resolver asks each loader if it knows how to handle the resource, by calling it's supports()
method.
The DelegatingLoader makes use of the LoaderResolver. When it is asked to load a resource, it delegates this question to the LoaderResolver. In case the resolver has found a loader that supports this particular resource, it returns the loader. Finally, this loader will be asked to load the resource.
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Loader\DelegatingLoader;
$loaderResolver = new LoaderResolver(array(new YamlUserLoader));
$delegatingLoader = new DelegatingLoader($loaderResolver);
$delegatingLoader->load(__DIR__ . DIRECTORY_SEPARATOR . '/users.yml');
// the YamlUserLoader will be used to load this resource, since it supports files with a "yml" extension
Caching things based on resources
When all configuration resources are loaded, you may want to process the configuration values and combine them all in one file. This file acts like a cache. It's contents don't have to be regenerated every time the application runs - only when the configuration resources are modified.
For example, the Symfony2 Routing component allows you to load all routes, and then dump a URL matcher or a URL generator based on these routes. In this case, when one of the resources is modified (and you are working in a development environment), the generated file should be invalidated and regenerated. This can be accomplished by making use of the ConfigCache class.
The example below shows you how to collect resources, then generate some code based on the resources that were loaded, and write this code to the cache. The cache also receives the collection of resources that were used for generating the code. By looking at the "last modified" timestamp of these resources, the cache can tell if it is still fresh or that it's contents should be regenerated.
// $yamlUserFiles is filled before with an array of 'users.yml' file paths
$resources = array();
foreach ($yamlUserFiles as $yamlUserFile) {
$resources[] = new FileResource($yamlUserFile);
}
$cachePath = __DIR__ . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'appUserMatcher.php';
$userMatcherCache = new ConfigCache($cachePath, true);
// the second constructor argument indicates whether or not we are in debug mode
if (!$userMatcherCache->isFresh()) {
foreach ($resources as $resource) {
$delegatingLoader->load($resource->getResource());
}
// The code for the UserMatcher is generated elsewhere
// $code = ...;
$userMatcherCache->write($code, $resources);
}
// you may want to require the cached code:
require $cachePath;
A ".meta" file is created in the same directory as the cache file itself. This ".meta" file contains the serialized resources, for later reference.
Defining a configuration structure
When all the configuration values are collected, from all the different configuration resources, we need a way to validate them, and also to define which values are required and if they are optional, what their default values are. We can use the Definition part of the Config Component for this purpose. I have already written about this (in the context of configuring Symfony2 bundles), so I hereby refer to my post Symfony2: define your bundle's configuration values using the TreeBuilder.
Hi Matthias - I'm asking myself what exactly you mean with "The code for the UserMatcher is generated elsewhere"?
$cachePath is a php file and you include it via require $cachePath; How do you create a PHP File from different YAML Files?
At so I found the following post - http://stackoverflow.com/qu...
At the bottom line - serialize / unserialize seems to be the fastest.
Regards Marcus
What's the point in copying the symfony official documentation ?
Hi Tens,
I wrote the official documentation for the Symfony Config Component ;) Then I contributed it and I must say, I should remove it from this site, since the documentation on symfony.com will be actively maintained, but this post won't. I will replace this article with a link to the official documentation.