Using ZEND_Config Part 2

Last Time

At the end of the last discussion on Zend_Config, I mentioned how it would be interesting to leverage the code already existing in Zend_Confing, instead of reinventing the wheel. Our main issue was how to use defined PHP constants in an ini file (or in an xml file).

View a better formatted version of the code in this post here

A Solution

We will extend Zend_Config_Ini with a custom method to find references to defined constants in a configuration object and resolve them. The resulting class should function exactly as Zend_Config_Ini would for “normal” ini files. First we need to know what are the constants already defined at the time Zend_Config_Ini parses our ini file. PHP does this easily
with the get_defined_constants() function (available in PHP 4 >= 4.1.0, PHP 5). Next we need to tell our extending class when it can find and replace defined PHP constants in a configuration object.

Extending Zend_Config_Ini

We will call our new class ConfigPhp (I’m sure you could find a better name):

class ConfigPhp extends Zend_Config_Ini{ //...}

Its constructor shall refer to Zend_Config_Ini constructor.

Tell ConfigPhp When To Parse Defined Constants

We need to tell ConfigPHP when our ini file contains PHP defined constants. The ConfigPhp constructor can be used:

public function __construct($filename, $section, $config = false)
{
if (is_array($config))
{
$this->php_defs = $config["php_defs"];
$this->allow_mods = $config["allow_mods"];
} //...
}

Where $config["php_defs"] is a boolean telling whether or not the ini file contains defined PHP constants.

Using Defined PHP Constants

When our ini file contains defined PHP constants, and we have told ConfigPhp by setting $config["php_defs"] to true, the constants are found in the configuration object and replaced with their real values.

if($this->php_defs)
{
$this->data = $this->toArray();
$this->parsePHPDefs();
}

The parsePHPDefs() method

The parsePHPDefs() is the heart of this extension. It will retrieve the data contained in the Zend_Config_Ini object and for all ocurences of defined PHP constants replaces them with their real value:

protected function parsePHPDefs()
{
$report_error = null;
$this->defined_constants = get_defined_constants(true);
if (is_array($this->defined_constants["user"]))
{
foreach($this->defined_constants["user"] as $key => $value)
{
foreach($this->data as $datakey => $dataval)
{
if($datakey == 'includes')
{
$report_error = ini_get('error_reporting');
error_reporting(0);
foreach($dataval as $inckey => $incval)
{
$pattern = '/(\.)?'.$key.'(\.)?/';
$incval = preg_replace($pattern , $value, $incval);
error_reporting($report_error);
$this->data["includes"][$inckey] = $incval;
$this->__set($inckey,$incval);
}
}

}
}
}
}


The
get_defined_constants(true) method will return an array of arrays. Each key in that array refers to the origin of the defined constants in the array it has as value. Thus
$this->defined_constants = get_defined_constants(true);
$this->defined_constants["user"]
will refer to the constants we have defined (at a point or another before this code is reached). For each defined constant we search each value in our
Zend_Config_Ini object and for each occurence of it replace it with the actual value of the constant. You would notice the line if($datakey == 'includes'). It just that the values I have in my ini file all start by the string “includes.”. It would be easy to parameterized this, and be able to replace arbitrary values in the ini file. In fact I use the Zend_Config_Ini object to set my include_path. I created a method to do so:

function makeIncludePath()
{
foreach($this->data as $datakey => $dataval)
{

if($datakey == 'includes')
{

foreach($dataval as $inckey => $incval)
{
$this->include_path.=$incval. DS.PATH_SEPARATOR;
}
}

}//....}

Advantages

Let’s say you are integrating a third party library to Zend and would like to make some constants defined in the library available to your Zend Application, but you want to use Zend_Config_Ini and … an ini file. This approach would allow you to access the third party library (or application) defined constants within your ini file.

Rationale

The approach itself might seem a bit of an overkill. I mean why use predefined constants in an ini? The point here is that it is possible, and someone out there might need such a feature. I do for once. I would use Zend_Config to help me in setting paths (Include_path) during the bootstrap process. To bootstrap, I need to define a minimal set of PHP constants. For example:

//Forced to have these declarations here as a minimum requirement for bootstrap to happen
if(!defined('DS'))
{
define('DS',DIRECTORY_SEPARATOR);
}
define ('MY_LIBS', dirname(dirname(dirname(__FILE__))).DS.'libs');
define ('MY_APP',dirname(dirname(dirname(__FILE__))).DS.'apps'.DS.'my_app'.DS.'application');
define('MY_ZEND_CORE',dirname(dirname(dirname(__FILE__))).DS.'libs'.DS.'zend'.DS.'library');
if (function_exists('ini_set')) {
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . MY_ZEND_CORE .DS . PATH_SEPARATOR . MY_APP . DS);

}
//end bootrap declarations

I keep my applications folders and library folders out of the server’s webroot. So these constants are needed so I don’t have to write long paths every time I refer to a file in my app or in the libraries. The settings in the ini file might need to “knnow” about these constants.

Improvement

I am aware that there is room for improvement. The Zend_Config class itself could be extended instead of the Zend_Config_Ini class, so that Zend_Config_XML benefits from the parsePHPDefs method. I will probably take more time to see what can be improved. Don’t hesitate to give your point of view on the matter. You might not even like this approach, please let me know why.

Explore posts in the same categories: Zend Framework

Tags:

You can comment below, or link to this permanent URL from your own site.

6 Comments on “Using ZEND_Config Part 2”


  1. [...] I realise that by using my own object I lose the benefit of all the convenient methods defined in Zend_Conf_Ini, but for such a simple job, I might not feel the difference. Yet I am thinking about extending Zend_Conf_Ini with the method discussed above. I might be worth it. Find out more on the subject here: Using ZEND_Config Part 2 [...]


  2. can the zend_ini class save the configuration back to the file ?

  3. kanian77 Says:

    Hi Riki,
    I haven’t tried to save the config back to the file.
    Do you have a special reason to do so?

  4. pbijl Says:

    so why are you using .ini? why not swap to normal php config array and solve all ur problems and workarounds?

  5. David Chandra Says:

    If you would like to save have the config changed online, you need to save it back to file I guess….

  6. kanian77 Says:

    Hi pbijl, The thing was for me to use variables i need in my .ini config file. A problem that made sens at the time while using Zend_Config. I haven’t followed the recent evolutions of the Zend Framework so I don’t know if the issue as any relevance anymore.
    Also, now i use symfony, as i have less work to put into thinking the config. It’s just more straightforward for me.
    And u are right the solution was kind of convoluted, to say the least :-)
    With symfony the yml config is turned into an associative array. U do not access that array directly though, u use a method of the sfConfig object. For example:
    the following config file app.yml:

    all:
    pager:
    news_max: 100
    tag:
    cloud_max: 20

    will be transformed into a config_app.yml.php and saved in the cache:
    ‘/web’,
    ‘app_search_body_weight’ => 1,
    ‘app_search_title_weight’ => 2,
    ‘app_search_tag_weight’ => 3,
    ‘app_search_results_max’ => 3,
    ‘app_users_photo_thumbnails_dir_name’ => ‘users_photo_thumbnails’,
    ‘app_users_photo_thumbnails_prefix’ => ‘thumb_’,
    ‘app_pager_news_max’ => 100,
    ‘app_tag_cloud_max’ => 20,
    ));

    You can then access a config value where u need it using :

    $max = ($paramsArray['max']) ? $paramsArray['max'] : sfConfig::get(‘app_pager_news_max’);

    Then again if u are not using a framework (or using one that doesn’t give support for config management) I suppose using an associative array, as u suggest , would be a good solution.
    Do u use any php framework at the time?


Comment: