Pragmatism in the real world

Zend_Config Proposal v2.1: Akrabat_Config (7)

There’s been quite a lot of interesting discussion about my Zend_Config proposal this week. As a result it became clear that the method of extension to multiple sections that I chose in Akrabat_Config v0.6 was not what was envisaged.

I wrote up the four choices that I could see for doing inheritence into a post:

Of course, it all depends on what is meant by "nesting" in the comment
I've quoted  :)  It's likely that I have misunderstood, in which case,
I'll change the proposal.

As I see it the options are:
1. One nest only.
	i.e. can only extend to one section and that the parent
	section may not not extend to another section.
2. Single inheritance nesting.
	i.e. can extend to one section, but that parent section may
	also extend to a parent section.
3. Multiple inheritance, single depth nesting,
	i.e. can extend to many sections using comma separated list
	and each parent section may not extend to another section.
4. Multiple inheritance, multiple depth nesting,
	i.e. can extend to many sections using comma separated list
	and each parent section may also extend to another section.

My personal preference is for (2) as I feel that (1) may be too
limiting, (3) may end up being a debugging nightmare and (4) is a recipe
for confusion!

Fortunately Andi agreed with me, so I’ve updated the proposal to v2.1 and Akrabat_Config to v0.7 to support (2) rather than (3).

v2.1 of the Zend_Config proposal is:

Zend Framework Component Proposal


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


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


Revision
-----------------------------------------------
2.1 - 18 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 on v1 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/

Mailing list feedback on v2 is discussed here:
    http://www.zend.com/lists/fw-general/200605/msg00563.html


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 "."s in a key name to
provide additional nesting levels. 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". It is possible for the parent section to itself extend from
another section. Multiple inheritence such that a section can extend from
two or more sections is not permitted.

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
    db.password = pwd
    hostname = www.zend.com

    [development]
    include=all
    hostname=andi_box
    db.name=local

    [andi_development]
    include=development
    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 local
    print $obj->db->password; // prints pwd
?>




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.7 of Akrabat_Config implements this spec. Again, I’ve put both the code and tests into a single zip file: Akrabat_Config 0.7.

As always, thoughts and corrections welcome!

One thought on “Zend_Config Proposal v2.1: Akrabat_Config (7)

  1. Ahh I much prefer the single inheritance nesting :)

    I've always found multiple level inheritance tough to work with…

    Thanks for the hard work so far !

    Steven

Comments are closed.