Configuration files in Yaml

The border detection of arrays and subarrays in a php configuration file is a real headache to me. Especially when pretty coding is not in the top priorities, which is the case for the most of the time.

I met Yaml in Ruby on Rails config files and I really appreciated when I was first encountered the Yii’s ones (With no offense sir Qiang ;D )

I don’t know if it’s the optimal way to do this but I crafted a solution with Symfony YAML that produces a double interface consisting:

  1. A php config array to Yii

  2. A yml structure to user

This is possible just by adding a php tag on the top of configuration file




<?php

    Yii::import('system.vendors.SymfonyComponents.YAML.*');

    list(,$caller) = debug_backtrace(false);

    if($caller['file'] !== __FILE__)

      return sfYaml::load(__FILE__);

?>



All the necessary files to make it happen are included here 1417

Symfony Yaml for Yii.zip

Installation process

1.(optional)Install Symfony YAML as described here

2.Go to the folder \PHP_installation_folder\PEAR (Yes I’m on Windows) and copy the folder SymfonyComponents or take it from the attachment

3.Paste it to Yii_path\framework\vendors

4.Create a new Yii web app and copy yml attachment files to your_new_web_app\protected\config folder

For new files to get involved, these should simply be renamed as php (remove the yml extension) or, mainly for code highlight reasons, change the $config assignment adding the ".yml" extension at the end of the reference path in the following files of your_new_web_app folder

  1. index.php line 5

  2. index-test.php line 9

  3. protected\yiic.php line 5

  4. protected\tests\bootstrap.php line 5

Please read the The YAML Format, an excellent single page documentation, for further details about the topic.

I’ll be happy to know your opinion about this (or my English <_< )

Happy coding!!

[CORRECTION]

In file main.php.yml and line 45 please replace db file name testdrive1.db with testdrive.db (remove the ‘1’)

[font="Times"][size="2"][font="arial, verdana, tahoma, sans-serif"][size="2"]Nice idea, simple and elegant implementation.

One possible problem:

Parsing a YAML file on every request may have significant impact on application’s performance. It’s worth caching the result array in a php file by using var_export() and parse the original YAML file again only when it changes.[/size][/font][/size][/font]

Yes indeed, actually in my system this approach is 66 times slower.

So based on your suggestion:




<?php

    @include $tmp = __FILE__ . '.cch';

    if(@filemtime($tmp) < filemtime(__FILE__))

    {

      file_put_contents($tmp, '<?php $config = null;');

      Yii::import('system.vendors.SymfonyComponents.YAML.*');

      file_put_contents($tmp, '<?php $config='. var_export($config=sfYaml::load(__FILE__), true).';');

    }

    if($config !== null) 

      return $config;

?>



This makes importing only 2 times slower than plain php (practically is about 6 additional secs after 10000 requests).

I suppose this should be replaced only in main.php.yml and the cache file ‘main.php.yml.cch’ should not be deployed on a destination server.

Thank you phtamas :)

Any other suggestions are welcomed

How about creating cli command to generate configs in production mode ?? (so it could be run as post-deploy action)

And what about icluded configs? (like we have one general config which is merged with local seettings)

How about using APC to avoid reading YAML file every time?

index.php




<?php


// Set constants based on environment

define('YII_ENV', getenv('YII_ENV') ?: 'production');

define('YII_DEBUG', filter_var(getenv('YII_DEBUG'), FILTER_VALIDATE_BOOLEAN));


// Specify how many levels of call stack should be shown in each log message

defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);


// Required files

$yii = dirname(__FILE__) . '/../framework/yii.php';

$config = dirname(__FILE__) . '/protected/config/main.php';


// This is the main Web application configuration. Any writable

// CWebApplication properties can be configured here.

require_once($yii);

Yii::createWebApplication($config)->run();



main.php




<?php


// Uncomment the following to define a path alias

$path = realpath(dirname(__FILE__) . '/..');

Yii::setPathOfAlias('local', $path);


// Try to retrieve from cache first

if (!YII_DEBUG && function_exists('apc_fetch')) {

    $apcKey = 'app_config_' . YII_ENV;

    $config = apc_fetch($apcKey);

}


// If not cached, parse config file

if (YII_DEBUG || empty($config)) {

    // Use namespaced library

    Yii::setPathOfAlias('Symfony', $path . '/vendor/Symfony');

    $yaml = \Symfony\Component\Yaml\Yaml::parse($path . '/config/main.yml');

    $config = $yaml[YII_ENV];

    // And cache it

    if (!empty($apcKey)) {

        apc_store($apcKey, $config);

    }

}


// This is the main Web application configuration. Any writable

// CWebApplication properties can be configured here.


return $config;



Notice that now is possible to have configuration per environment (devel, production, etc) and inheritance too.

main.yml




production: &default

  ...

development:

  <<: *default

  ...



I hope this would be helpful for YAML fans.

Cheers.

Pepe

PS: I have Yaml component at vendor/Symfony/Component/Yaml folder.

What’s the profit?

For me


foo: bar

baz: qux

and


'foo' => 'bar'

'baz' => 'qux'

are pretty much the same.

It’s just more clean. You don’t have to write [font=“Courier New”]array()[/font] all the time. Nothing more.

Ah, and I forgot to mention the inline inheritance. It’s very useful when you have lots of environments. In my case, I’ve all the configuration in one file.

main.yml




## YAML Template.

production: &default

  ...

    

development: &devel

  <<: *default

  ...

        

console:

  <<: *devel


test:

  <<: *devel

  ...



console.php




<?php


define('YII_ENV', 'console');

$config = require_once dirname(__FILE__) . '/main.php';

return $config;



test.php




<?php


define('YII_ENV', 'test');

$config = require_once dirname(__FILE__) . '/main.php';

return $config;