Archive for Zend Framework

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.

Using Zend_Config

There is a Zend Framework class to help in configuring your application. You can use it to save and retrieve configuration data. It’s called Zend_Config . For examples on how to use it see http://framework.zend.com/manual/en/zend.config.html.

It allows you to use an ‘.ini’ or an xml file to load your configuration settings. You can then access them as you would of any objects property. Thus if you have this line

include.path = 'a/path/'

in your ini file, after using Zend_Config_Ini, you would access it in this manner

$path = $config->include->path;

The process would be similar if you had used xml instead and Zend_Config_Xml.
While being really handy, this method still presents some challenges. An example seaks louder thhan words:

includes.APP_MODELS_DIR = MY_APP.DS.'models';

where MY_APP and DS are constants defined in my boostrap file, let’s say.
The
parse_ini_file() php function, used by Zend_Config_Ini does’nt resolve MY_APP and DS to their proper values. And don’t even try to use such a syntax

includes.APP_MODELS_DIR = <?php MODEL_DIR.DIRECTORY_SEPARATOR."somestring";?>

otherwise you would get an error. I mean it’s quite understandable. The parse_ini_file() function is expecting ini syntax and nothing else.
Since I use constants extensively when defining my paths (app,libs,controllers,vendors paths for example), I ‘ld rather create an ‘include’ object. For example I would have an ‘include.php’ file:

$includes = null;
$includes->MODELS_DIR = MY_APP.DS.'models';
$includes->APP_MODELS_DIR = MY_APP.DS.'models';
$includes->APP_CLASSES_DIR = MY_APP.DS.'classes';
$includes->PLUGINS_DIR = MY_LIBS.DS.'ZendPlugins';
$includes->VIEW_PATH = MY_APP.DS.'views'.DS.'scripts';

where I can use previously defined constants (they are defined in the bootstrap file as a necessity for the bootstrap to happen at all).
Then I write a utility function to
parse the object into an ‘include string’ that I append to my include path:

function makeIncludePath($includes)
{
$include_path='';
if($includes)
{
foreach($includes as $inc)
{$include_path.=$inc. DS.PATH_SEPARATOR;}
}
ini_set('include_path', ini_get('include_path') . DS.PATH_SEPARATOR.$include_path);
}


In a suitable place, before the dispatch process has started, I write

require_once 'config_utils.php';
require_once 'includes.php';
makeIncludePath($includes);

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

« Previous entries
Follow

Get every new post delivered to your Inbox.