I love my




http://www.flickr.com/photos/slushpup/3055613967/
Dennis Benkert
 Software Developer
     Coding
     Consulting
     Coaching
 Symfony User Groups
 Symfony Day Cologne
What mom never told you about
    Bundle configurations
parameters:
    global.page_size:        10
    gloabl.page_num:         5
    global.tracking.key:     Correct type?
                             ASFDIPSADPFIHwer234123QSD

   # Various configuration
   webservices.timeout:      100
   content_check.enabled:    true
   birthdates.start:         1950 # TODO: Delete after release
   logging.registrations:    true

   # Tracking Bundle Configuration
   trackingbundle.user:              rebum Only this bundle?!
   trackingbundle.api.version:       1_0
   trackingbundle.use_ssl:           true     # Do not touch this!!!
   trackingbundle.track_super_users: false
   infobundle.god_mode:              true # Ticket 123456234
   infobundle.level:                 42
   # Connection data for some service
   some_webservice.url:              http://example.com
   some_webservice.user:             api Multiple connections?!
   some_webservice.key:               Sdfihwef $5sdf” SAFAWEF
   some_webservice.ssl:              true
my_project_tracking:
    user:                  rebum
    api_version:           1_0
    use_ssl:               true    # Do not touch this!!!
    track_super_users:     false

my_project_info:
    god_mode:              true    # Ticket 123456234
    level:                 42

my_project_ webservice:
    connection:
        url:               http://example.com
        user:              api
        key:               Sdfihwef $5sdf” SAFAWEF
        ssl:               true

parameters:
    global.page_size:      10
    gloabl.page_num:       5
    global.tracking.key:   ASFDIPSADPFIHwer234123QSD

   # ...
Bad configuration
   Worse than bad code
Bad validation
    Even worse
Config Component




    http://www.flickr.com/photos/capcase/2735500813/
Cache               Locate

           Config
Validate            Load
Cache               Locate

           Config
Validate            Load
Locate & Load




      http://www.flickr.com/photos/onthespiral/3406281939/
$locator = new FileLocator(
    __DIR__.'/../Resources/config'
);
        Path(s) of resources
Available in path(s)?


$locator->locate('config.xml');
// src/YourBundle/DependencyInjection/
// YourBundleExtension.php
        Special Loader for DIC

$loader = new LoaderXmlFileLoader(
    $container,
    new FileLocator('...')
);
                   Path(s) of resources
Load configuration in DIC


$loader->load('services.xml');
$loader->load('services.xml');
$loader->load('other_services.xml');
Cache               Locate

           Config
Validate            Load
Validate




http://www.flickr.com/photos/jeremybrooks/3214838875/
your_bundle:
    enabled: true
// src/YourBundle/DependencyInjection/
// YourBundleExtension.php

public function load($configs, /*...*/)
{
     // processing and unprocessed
            Matched ...

    if (true === $config['enabled']) {
         $loader->load('services.xml');
    }             Processed and validated
}
your_bundle:
    enabled: true


               Validate
               Convert

array('enabled' => true);
Build a Config Tree




          http://www.flickr.com/photos/jlscha/6226656013/
// src/YourBundle/DependencyInjection/
// Configuration.php

                    The config tree
public function getConfigTreeBuilder()
{
    $treeBuilder = new TreeBuilder();
    $rootNode = $treeBuilder
        ->root('your_bundle');

    // tree definition ...
             Your root node

    return $treeBuilder;
}
your_bundle:
    enabled: true
$rootNode Node type           Node name
     ->children()
          ->booleanNode('enabled')
                ->defaultFalse()
          ->end()
     ->end()     More options
;
Scalar

Boolean            Array


          Node
Scalar

Boolean            Array


          Node
->booleanNode('enabled')->end()
your_bundle:
    enabled: troo
Type validation
Scalar

Boolean            Array


          Node
your_bundle:
    timeout: 60
All scalar types


->scalarNode('timeout')
     ->defaultValue(3600)
     ->isRequired()
     ->cannotBeEmpty()
->end()
              Validation options
your_bundle:
    timeout: sixty seconds
->scalarNode('timeout')
        Custom validation
     // ...                      Validation logic
     ->validate()
          ->ifTrue(function ($v) {
                return !is_int($v);
          })
          ->thenInvalid('No integer')
     ->end()
->end()                   Custom error
Custom error
Scalar

Boolean            Array


          Node
connection:
    url: http://example.com
    user: api
    key: $ome35ecre7Ke$
Group of nodes

->arrayNode('connection')
    ->children() Specific validation
        ->scalarNode('url')
            ->isRequired()
        ->end()
        ->scalarNode('user')->end()
        ->scalarNode('key')->end()
    ->end()
->end()
array('connection' =>
    array(
        'url' => 'http://example.com',
        'user' => 'api',
        'key' => '$ome35ecre7Ke$'
    )
);
connections:
     default:
          url:    http://example.com
          user:   api
          key:    $ome35ecre7Ke$
     fallback:
          url:    http://back.example.com
          user:   fallback_api
          key:    $ome35ecre7Ke$
->arrayNode('connections')
Prototype variations
    ->useAttributeAsKey('name')
    ->prototype('array')
        ->children()         Key generation
              ->scalarNode('url')
                  ->isRequired()
              ->end()
              ->scalarNode('user')->end()
              ->scalarNode('key')->end()
        ->end() validation
        Prototype
    ->end()
    ->requiresAtLeastOneElement()
->end()
array (
    'connections' => array (
        'default' => array (
            'url' => '...',
            'user' => 'api',
            'key' => '$ome35ecre7Ke$'
        ),
        'fallback' => array (
            'url' => '...',
            'user' => 'fallback_api',
            'key' => '$ome35ecre7Ke$'
        )
));
Build upon structure




      http://www.flickr.com/photos/victornuno/222145881/
// src/YourBundle/DependencyInjection/
// YourBundleExtension.php

public function load(/*...*/)
                 Load services
{
     if (true === $config['enabled']) {
          $loader->load('services.xml');
     }
}
public function load(/*...*/)
{
    if (true === $config['enabled']) {
        $loader->load('services.xml');
    }            Split services

    if (true === $config['add_extras']) {
        $loader->load('extras.xml');
    }
}
Manipulate DIC

$container->setParameter(
    'your_bundle.timeout',
    $config['timeout']
);
Testing Configuration




        http://www.flickr.com/photos/sidelong/246816211/
Get DIC

$container =Extension
  Register your $this->createContainer();

$container->registerExtension( Extension
                             Load your
    new YourBundleExtension());
$container->loadFromExtension(
   Process configuration array());
    'your_bundle',

$this->compileContainer($container);
$this->assertTrue(
    $container->getParameter('enabled')
);
/**             Custom error
 * @expectedException Exception
 * @expectedMessage No integer
 */
public function testConfiguration()
{}
Let‘s recap




http://www.flickr.com/photos/tine72/5464869042/
Configuration
needs structure
Configuration
needs validation
Configuration
needs conversion
Config Component
  is awesome
What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012

What mom never told you about bundle configurations - Symfony Live Paris 2012