Zend_Config Proposal v2: Akrabat_Config (6)

A couple of weeks ago I received feedback from the Zend Framework core team about my Zend_Config proposal v1 and have finally found the time to updated the proposal accordingly.

The key points with my comments interspersed were:

- The Zend_Config class shall never modify configuration information in its storage containers
That’s ok – I never planned to do writing to ini file anyway.
- The configuration information should only be allow to be modified in memory if a flag is passed to the constructor. This flag will not be allowed to be changed after the object is instantiated.
I have to admit that I hadn’t considered the idea of allowing any modification of config data. It’s not hard to do though :)
- A top-level section name must be passed to the constructor and the config object will then be locked on this section for its lifetime.
This means that multiple ini files cannot be loaded. i.e. only one ini file is allowed per config object. It simplifies the code too! Note that given that you can do in-memory modification of a Zend_Config object, you could load multiple ini files and overwrite into one Zend_Config object.
- For convenience, configuration information should be available by property overloading and not array access.
This was discussed previously and I hadn’t got around to doing it.
- The config object should implement Iterator for easily listing configuration information.
This follows on logically from using property overloading.
- The special inheritence keyword (“include” in the proposal) shall be “extends” as in PHP. Inheritence will be restricted to one section only, i.e. nesting “extends” will be prohibited. This will be detected and an exception raised on violation.
I like the idea of using “extends”. Personally, I would have preferred allowing nesting of extends as provides more power. Keeps life simple though.
- Additional config storage containers will be developed so that a variety of configuration file formats can be read. However, the Zend Framework itself will have only one format. This has yet to be decided and is not considered part of this proposal.
I’m sticking with INI for now. As I said in the proposal, extending to other file formats wouldn’t be difficult, and we could then use a factory to pick the right one depending on file exension.

v2 of the Zend_Config proposal is:

Zend Framework Component Proposal


Proposed Component Name
-----------------------------------------------
Zend_Config


Proposers
-----------------------------------------------
Rob Allen (rob@akrabat.com


Revision
-----------------------------------------------
2.0 - 12 May 2006: Updated following feedback


Overview
-----------------------------------------------
Zend_Config is a very simple configuration file reader.
It provides an easy means to read configuration files
and access the data within them as a set of key->value
pairs. It will support at least one nested level of data.
Initially providing support for ini files, it should
be easy to extend for other formats such as YAML.


References
-----------------------------------------------
Mailing list thread resurrected here:

http://www.zend.com/lists/fw-general/200604/msg00178.html.

Unfortunately, I could not find the original post by Andi Gutmans in the
archives.

Other discussion can be found here:

http://www.akrabat.com/index.php?s=Akrabat_Config

Feedback from Zend is discussed here:

http://www.zend.com/lists/fw-general/200605/msg00134.html

Prototype code can be found here:

http://www.akrabat.com/2006/05/13/zend_config-proposal-v2/

Requirements
-----------------------------------------------
* Ability to load configuration information from a single config file
  and provide access to the data as object properties.
* A top level section name must be specified for loading.
* Optional option to allow modification of the config data held in
  memory.
* No ability to modify the original data in the config file.
* For ini files, support for "namespaces" using the syntax:
        namespace.property = value
* Iterator is implemented for easily listing of configuration
  information.
* A special "inheritence keyword "extends" will be be used to allow for
  including additional sections within this section. For ini files,
  the syntax would be:
        extends = section


Dependencies on Other Framework Components
-----------------------------------------------
Zend_Exception


Theory of Operation
-----------------------------------------------
Zend_Config_Abstract provides common functionality for all concrete
implementations. It provides the standard retrieval functionality for
concrete classes and allows for programmatic setting of config values
if $allowModifications has been set to true when the object was
created.

Zend_Config_Ini is a concrete implementation that loads ini files. It
can either load an entire ini file or just a single section. It is
possible to load multiple ini files in which case, values for
duplicate keys will override the previously loaded value. This is useful
to allow for overriding a common config file with context specific
data such as for a staging or test server.

Zend_Config_Ini also supports using the first "." in a key name to
provide an additional nesting level. That is a key named db.name will
be represented as $config->db->name.

Zend_Config_ini also supports loading of keys from one section of the
ini file into another section. This is done using a special key called
"extends". This key takes a comma separated list of section names.

In the future, should there be demand for other config file types, a
Zend_Config class could be created at a later stage that has a factory
function that returns the correct concrete class based on the filename
of the config file supplied.  For example, myapp.ini would create and
return a Zend_Config_Ini object whereas myapp.yml would create and
return a Zend_Config_Yaml object.


Class Index
-----------------------------------------------
Zend_Config_Exception
Zend_Config_Abstract
Zend_Config_Ini


Use Cases
-----------------------------------------------
Given the following ini file:
    [all]
    namespace.property = example
    db.connection = foo
    db.name = bar
    hostname = www.zend.com

    [andi_development]
    include=all
    hostname=andi_box
    db.connection=localhost

    [staging]
    include=all
    hostname=dev.zend.com


Then we can do something like:

< ?php
    $obj=new Zend_Config("myapp.ini", "andi_development");
    print $obj->hostname;   // prints andi_box
    print $obj->db->connection; // prints localhost
    print $obj->db->name; // prints bar
?>


Class Skeletons
-----------------------------------------------
class Zend_Config_Exception extends Zend_Exception {}

class Zend_Config_Abstract implements ArrayAccess, IteratorAggregate
{
    protected $_config;
    protected $_allowModifications;

    function __construct($filename, $section, $allowModifications=false) {}

    /**
     * Load the section $section from the config file $filename.
     *
     * If any keys with $section are called "extends", then the section
     * pointed to by the "extends" is then included into the properties.
     * Note that the keys in $section will override any keys of the same
     * name in the sections that have been included via "extends".
     *
     * If any key includes a ".", then this will act as a separator to
     * create a sub-property.
     *
     * Only allow modifications after construction if $allowModifications
     * is set to true.
     *
     * @param string $filename
     * @param string $section
     * @param boolean $allowModifications
     */
    abstract public function load($filename, $section);

    /**
     * Retreive a value from the config key called $name
     *
     * @param string $name
     * @param mixed $default
     * @return mixed
     */
    public function get($name, $default=null) {}

    /**
     * Does the config key $name exist?
     *
     * @param string $name
     * @return boolean
     */
    public function exists($name) {}

    /**
     * magic function so that $obj->value will work.
     *
     * @param string $name
     * @return mixed
     */
    public function __get($name) {}

    /**
     * Only allow setting of a property if allowModifications
     * is true. Otherwise, throw an exception.
     *
     * @param string $name
     * @param mixed $value
     */
    public function __set($name, $value) {}

}


class Zend_Config_Ini extends Zend_Config_Abstract
{
    /**
     * Load $section from an ini file called $filename.
     *
     * @param string $filename
     * @param string $section
     */
    public function load($filename, $section) {}

    /**
     * Helper function to process each element in the section and handle
     * the "extends" inheritance keyword. Passes control to processKey()
     * to handle the "dot" sub-property syntax in each key.
     *
     * @param array $iniArray
     * @param string $section
     * @return array
     */
    protected function processExtends($iniArray, $section) {}

    /**
     * Assign the key's value to the property list. Handle the "dot"
     * notation for sub-properties by passing control to
     * processLevelsInKey().
     *
     * @param stdClass $config
     * @param string $key
     * @param string $value
     * @return stdClass
     */
    function processKey($config, $key , $value) {}

    /**
     * Helper function to handle the "dot" namespace syntax in the key.
     * Uses "." as the separator.
     *
     * @param stdClass $parent
     * @param string $key
     * @param string $value
     * @return stdClass
     */
    protected function processLevelsInKey($parent, $key, $value) {}

}

And v0.6 of Akrabat_Config implements this spec. This time, I’ve put both the code and tests into a single zip file: Akrabat_Config-0.6.zip.

As always, thoughts and corrections welcome!

4 thoughts on “Zend_Config Proposal v2: Akrabat_Config (6)

  1. I ran your tests under Windows/Xampp/PHP 5.1.3/Zend Framework from Subversion 05 May 2006. They all ran correctly. Keep up the good work.

  2. As usual Rob, looks like a neat solution to what must be a frequent problem — I certainly need this capability (which is how I found your posting) and looks like your class would handle Smarty config files out of the box too — which could be useful. Unfortunately the world of Zend Framework has largely passed me by up to now. Got any useful “How to” references you could point me at? Thanks Rob.

    Jonathan

  3. Hi Jonathan!

    The config has improved now and it’s worth looking at what’s in the Zend Framework incubator. THe class is now split differently: Zend_Config handles retriving values and is loaded via an array. I have written two classes that can load a file into Zend_Config: Zend_Config_Ini for ini files and Zend_Config_Array for standard PHP arrays. I’ll have an XML adapter uploaded soonish too.

    Look at http://framework.zend.com/developer/browser/trunk/incubator/library/Zend/Config.php and http://framework.zend.com/developer/browser/trunk/incubator/library/Zend/Config for the code. and http://framework.zend.com/developer/browser/trunk/incubator/tests/Zend for the tests.

    As far as the Zend Framework itself is concerned, the most useful places to look are:
    http://framework.zend.com/ – Official site. Check the manual!
    http://hades.phparch.com/ceres/public/article/index.php/art::zend_framework::tutorial – A good tutorial

Comments are closed.