:: Config_Registry (proposal)
API

$Date$

Table of Contents

  1. Applications
  2. Configurations
  3. Services

Package

    /**
     * Returns the current API version
     *
     * @return     float
     * @since      0.1
     * @access     public
     */
    function apiVersion()
    {
        return 0.1;
    }

    /**
     * Returns a reference to the global Registry object, only
     * creating it if it doesn't already exist.
     * This method must be invoked as: $registry = &Registry::singleton()
     *
     * @param      array     $files         (optional) files to read user-defined options from
     *                                      and/or to read system-wide defaults from
     *
     * @return     object                   The Registry instance.
     * @since      0.1
     * @access     public
     */
    function &singleton()
    {
        static $registry;

        if (!isset($registry)) {
            $registry = new Config_Registry();
        }

        return $registry;
    }

[Top]

Applications

getApplicationsRegistered

    /**
     * Return a list of the installed and registered applications.
     * If no applications are defined return a null array.
     *
     * @return     array                    List of applications registered
     * @since      0.1
     * @access     public
     * @see        registerApplication(), isApplicationRegistered()
     */
    function getApplicationsRegistered()
    {
        $apps = array();
        foreach ($this->applications as $app => $opt) {
            $apps[] = array('handler' => $app, 'name' => $opt['name']);
        }
        return $apps;
    }

[Top]

isApplicationRegistered

    /**
     * Returns whether or not the application is registered
     *
     * @param      string    $app           The desired application
     *
     * @return     boolean
     * @since      0.1
     * @access     public
     * @see        registerApplication(), getApplicationsRegistered()
     */
    function isApplicationRegistered($app)
    {
        return isset($this->applications[$app]);
    }

[Top]

registerApplication

    /**
     * Registers a new application. All configuration layers 
     * ('user', 'system', 'default') are reset.
     *
     * @param      string    $app          The desired application
     * @param      string    $name         (optional) The desired application name 
     * @param      string    $fileroot     (optional) The directory where we can find the application 
     *
     * @return     mixed                   PEAR::Error is failure, TRUE otherwise
     * @since      0.1
     * @access     public
     * @see        isApplicationRegistered(), getApplicationsRegistered(), registerApplication()
     */
    function registerApplication($app, $name = '', $fileroot = '.')
    {
        if (empty($app)) {
            return PEAR::raiseError('Application handler is required');
        }
        if ($this->isApplicationRegistered($app)) {
            return PEAR::raiseError('Application "'.$name.'" ('.$app.') is already defined');
        }
        if (!is_dir($fileroot)) {
            return PEAR::raiseError('Directory of application "'.$name.'" ('.$app.') is invalid');
        }
        $this->applications[$app]['fileroot'] = $fileroot;
        $this->applications[$app]['name'] = $name;

        $layers = $this->_getLayers(true);
        foreach ($layers as $layer) {
            $this->configurations[$app][$layer] = null;
        }
        return true;
    }

[Top]

unregisterApplication

    /**
     * Removes an old application with all configuration layers 
     * ('user', 'system', 'default') and registered services.
     *
     * @param      string    $app          The desired application
     *
     * @return     mixed                   PEAR::Error is failure, TRUE otherwise
     * @since      0.1
     * @access     public
     * @see        registerApplication(), isApplicationRegistered(), getApplicationsRegistered()
     */
    function unregisterApplication($app)
    {
        if (!$this->isApplicationRegistered($app)) {
            return PEAR::raiseError('Application "'.$app.'" is not registered');
        }
        $layers = $this->_getLayers(true);

        foreach ($layers as $layer) {
            // removes all configuration layers data 
            if (isset($this->configurations[$app][$layer])) {
                $this->configurations[$app][$layer] = null;
            }
            $this->resources[$layer] = null;  // marks last-used resource as empty
        }
        // removes all hooks for this application
        if (isset($this->services[$app])) {
            unset($this->services[$app]);
        }
        unset($this->applications[$app]);
        return true;
    }

[Top]

Configurations

getConfigLayer

    /**
     * Gets the configuration for a layer
     *
     * @param      string    $layer         Config layer name ('user', 'system' or 'default')
     * @param      string    $app           The desired application
     * @param      bool      $compress      (optional) set it to TRUE if you want serialized data
     *
     * @return     mixed                    The config layer on success and PEAR error on failure
     * @since      0.1
     * @access     public
     */

    function getConfigLayer($layer, $app, $compress = false)
    {
        $layers = $this->_getLayers(true);

        if (!in_array($layer, $layers)) {
            return PEAR::raiseError('unknown layer "'.$layer.'"');
        }
        if (!$this->isApplicationRegistered($app)) {
            return PEAR::raiseError('Application "' .$app. '" is not registered');
        }

        $cfg =& $this->configurations[$app][$layer];
        if ($compress) {
            return serialize($cfg->toArray());
        }
        return $cfg->toArray();
    }

[Top]

getConfigResource

    /**
     * Gets the resource used for storing the layer configuration.
     *
     * @param      string    $layer         Config layer name ('user', 'system' or 'default')
     *
     * @return     mixed                    The resource if layer is valid and null otherwise
     * @since      0.1
     * @access     public
     */

    function getConfigResource($layer)
    {
        return (!empty($this->resources[$layer])) ? $this->resources[$layer] : null;
    }

[Top]

importConfig

    /**
     * Reads configuration data from an external resource.
     * All existing values in the application configuration layer
     * may be: 
     * - discarded and replaced with data from the external resource
     * - merged with existing config data.
     *
     * @param      string    $app           The target application
     * @param      string    $name          (optional) Resource to read from,
     *                                      if NULL or not specified,
     *                                      the last-used resource for the same layer is used
     * @param      string    $type          (optional) Type of data source (phpArray, Ini, XML, ...)
     * @param      array     $options       (optional) Options for the parser
     * @param      bool      $override      (optional) Whether to overwrite existing data 
     * @param      string    $layer         (optional) Config layer to insert data into
     *
     * @return     bool                     TRUE on success or a PEAR error on failure
     * @since      0.1
     * @access     public
     * @see        exportConfig(), setDefaults()
     */
    function importConfig($app, $name = null, $type = 'phpArray', $options = array(),
                          $override = true, $layer = 'user')
    {
        $layers = $this->_getLayers(true);

        if (!in_array($layer, $layers)) {
            return PEAR::raiseError('unknown config type '. $layer);
        }
        if ($name === null) {
            if (empty($this->resources[$layer])) {
                return PEAR::raiseError('none last-used config resource type '. $layer);
            } else {
                $name = $this->resources[$layer]['name'];
                $type = $this->resources[$layer]['type'];
                $options = $this->resources[$layer]['opts'];
            }
        }

        $conf = new Config();
        $reg = $conf->parseConfig($name, $type, $options);
        if (PEAR::isError($reg)) {
            return $reg;
        }
        $cfg =& $this->configurations[$app][$layer];
        
        if ($override || is_null($cfg)) {
            $this->configurations[$app][$layer] = $reg;
        } else {
            for ($i = 0; $i < $reg->countChildren('section'); $i++) {
                $c = $reg->getItem('section',null,null,null,$i);
                $cfg->additem($c);
            }
        }
        
        $this->resources[$layer] = array('name' => $name, 'type' => $type, 'opts' => $options);
        return true;
    }

[Top]

exportConfig

    /**
     * Writes data from an application configuration layer 
     * into external resource.
     *
     * @param      string    $app           The source application
     * @param      string    $name          (optional) Name of resource to write to 
     * @param      string    $type          (optional) Type of data source (phpArray, Ini, XML, ...)
     * @param      array     $options       (optional) Options for the parser
     * @param      string    $layer         (optional) Config layer to read data from
     *
     * @return     bool                     TRUE on success or a PEAR error on failure
     * @since      0.1
     * @access     public
     * @see        importConfig(), setDefaults()
     */
    function exportConfig($app, $name = null, $type = 'phpArray', $options = array(), 
                          $layer = 'user')
    {
        if (!$this->isApplicationRegistered($app)) {
            return PEAR::raiseError('Application ' .$app. ' is not registered');
        }

        $layers = $this->_getLayers(true);

        if (!in_array($layer, $layers)) {
            return PEAR::raiseError('unknown config type '. $layer);
        }
        if ($name === null) {
            if (empty($this->resources[$layer])) {
                return PEAR::raiseError('none last-used config resource type '. $layer);
            }
            $name = $this->resources[$layer]['name'];
            $this->resources[$layer]['type'] = $type;     // in case of file-type changed
            $this->resources[$layer]['opts'] = $options;  // in case of file-options changed too
        }

        $cfg =& $this->configurations[$app][$layer];
        return $cfg->writeDatasrc($name, $type, $options);
    }

[Top]

setDefaults

    /**
     * A convenience function for initialize 
     * a default configuration for an application.
     *
     * @param      string    $app           The target application
     * @param      string    $name          (optional) Resource to read from,
     *                                      if NULL or not specified,
     *                                      the last-used resource for the same layer is used
     * @param      string    $type          (optional) Type of data source (phpArray, Ini, XML, ...)
     * @param      array     $options       (optional) Options for the parser
     *
     * @return     mixed                    TRUE on success and PEAR error on failure
     * @since      0.1
     * @access     public
     * @see        importConfig()
     */
    function setDefaults($app, $name = null, $type = 'phpArray', $options = array())
    {
        return $this->importConfig($app, $name, $type, $options, true, 'default');
    }

[Top]

getParam

    /**
     * Returns the requested configuration parameter.
     * If no layer is specified, then configuration parameter 
     * will be search in layer order (if a 'user' value exists, 
     * it will be returned first, then 'system' and finally 'default').
     * However, if parameter is not present, we return null.
     *
     * @param      string    $parameter     The configuration value to retrieve.
     * @param      string    $app           The application to get the value for.
     * @param      string    $bloc          (optional) The section where to search for value.
     * @param      string    $layer         (optional) Config layer to get data from
     *
     * @return     string                   The requested parameter, or null if it is not set.
     * @since      0.1
     * @access     public
     * @see        searchParam()
     */
    function getParam($parameter, $app, $bloc = 'root', $layer = null)
    {
        if ($layer === null) {
            $layers = $this->_getLayers(true);
            foreach ($layers as $layer) {
                $cfg =& $this->configurations[$app][$layer];
                if ($this->_isConfig($cfg)) {
                    if (strtolower($bloc) == 'root') {
                        $section =& $cfg;
                    } else {
                        $section =& $cfg->getItem('section',$bloc);
                    }
                    if (is_object($section)) {
                        $param = $section->getItem('directive',$parameter);
                        if (is_object($param)) {
                            return $param->getContent();
                        }
                    }
                }
            }
        } else {
            $cfg =& $this->configurations[$app][$layer];
        	
            if (in_array($layer, $layers) && $this->_isConfig($cfg)) {
                if (strtolower($bloc) == 'root') {
                    $section =& $cfg;
                } else {
                    $section =& $cfg->getItem('section',$bloc);
                }
                if (is_object($section)) {
                    $param = $section->getItem('directive',$parameter);
                    if (is_object($param)) {
                        return $param->getContent();
                    }
                }
            }
        }
        return null;
    }

[Top]

setParam

    /**
     * Sets the given parameter to the specific value, if the parameter
     * of the configuration already exists.
     *
     * @param      string    $parameter     The configuration directive to set
     * @param      string    $value         The configuration parameter value
     * @param      string    $app           The application to set the value to
     * @param      string    $bloc          (optional) The section where to set the parameter.
     * @param      string    $layer         (optional) Config layer to set data to 
     *
     * @return     bool                     TRUE on success or a PEAR error on failure
     * @since      0.1
     * @access     public
     * @see        addParam(), removeParam()
     */
    function setParam($parameter, $value, $app, $bloc = 'root', $layer = 'user')
    {
        $layers = $this->_getLayers(true);
        $cfg =& $this->configurations[$app][$layer];

        if (!in_array($layer, $layers) || !$this->_isConfig($cfg)) {
            return PEAR::raiseError('unknown config resource type '.$layer);
        } else {
            if (strtolower($bloc) == 'root') {
                $section =& $cfg;
            } else {
                $section =& $cfg->getItem('section',$bloc);
            }
            if ($section === false) {
                return PEAR::raiseError('unknown section '.$bloc.' in layer '.$layer);
            } else {                  
                $param =& $section->getItem('directive',$parameter);
                if ($param === false) {
                    return PEAR::raiseError('unknown directive '.$parameter.' on section '.$bloc.' of layer '.$layer);
                } else {                  
                    $param->setContent($value);
                    return true;
                }
            }
        }
    }

[Top]

addParam

    /**
     * Add a new config parameter in a specific layer (defaults to 'user').
     *
     * @param      string    $parameter     The configuration directive to add
     * @param      string    $value         The configuration parameter value
     * @param      mixed     $attributes    The parameter (directive) attributes
     * @param      string    $app           The application to add the new value
     * @param      string    $bloc          (optional) The section where to add the parameter
     * @param      string    $layer         (optional) Config layer to set data to
     *
     * @return     bool                     TRUE on success or a PEAR error on failure
     * @since      0.1
     * @access     public
     * @see        setParam(), removeParam()
     */
    function addParam($parameter, $value, $attributes, $app, $bloc = 'root', $layer = 'user')
    {
        $layers = $this->_getLayers(true);
        $cfg =& $this->configurations[$app][$layer];

        if (!in_array($layer, $layers) || !$this->_isConfig($cfg)) {
            return PEAR::raiseError('unknown config resource type '.$layer);
        } else {
            if (strtolower($bloc) == 'root') {
                $section =& $cfg;
            } else {
                $section =& $cfg->getItem('section',$bloc);
            }
            if ($section === false) {
                // creates the $bloc if does not exists
                $section =& $cfg->createSection($bloc);
                if (PEAR::isError($section)) {
                    return $section;
                }
            }
            $param = $section->getItem('directive',$parameter);
            if ($param === false) {
                $param =& $section->createDirective($parameter, $value, $attributes);
            }
            if (PEAR::isError($param)) {
                return $param;
            }
            return true;
        }
    }

[Top]

removeParam

    /**
     * Remove a config parameter from a specific layer (defaults from 'user').
     *
     * @param      string    $parameter     The configuration parameter to remove
     * @param      string    $app           The application to remove the value from
     * @param      string    $bloc          (optional) The section where to remove the parameter
     * @param      string    $layer         (optional) Config layer to remove data
     *
     * @return     bool                     TRUE on success, FALSE on failure
     * @since      0.1
     * @access     public
     * @see        setParam(), addParam()
     */
    function removeParam($parameter, $app, $bloc = 'root', $layer = 'user')
    {
        $layers = $this->_getLayers(true);
        $cfg =& $this->configurations[$app][$layer];

        if (!in_array($layer, $layers) || !$this->_isConfig($cfg)) {
            return PEAR::raiseError('unknown config resource type '.$layer);
        } else {
            if (strtolower($bloc) == 'root') {
                $section =& $cfg;
            } else {
                $section =& $cfg->getItem('section',$bloc);
            }
            if ($section === false) {
                return PEAR::raiseError('unknown section '.$bloc.' in layer '.$layer);
            } else {                  
                $param =& $section->getItem('directive',$parameter);
                if ($param === false) {
                    return PEAR::raiseError('unknown directive '.$parameter.' on section '.$bloc.' of layer '.$layer);
                } else {                  
                    $param->removeItem();
                    return true;
                }
            }
        }
    }

[Top]

searchParam

    /**
     * Tells what config layer that gets to define a parameter.
     *
     * @param      string    $parameter     The configuration parameter to retrieve
     * @param      string    $app           The desired application
     * @param      string    $bloc          (optional) The section where to search the parameter
     *
     * @return     string                   The config layer, or NULL if not found
     * @since      0.1
     * @access     public
     * @see        getParam()
     */
    function searchParam($parameter, $app, $bloc = 'root')
    {
        $layers = $this->_getLayers(true);

        foreach ($layers as $layer) {
            $cfg =& $this->configurations[$app][$layer];
            if ($this->_isConfig($cfg)) {
                if (strtolower($bloc) == 'root') {
                    $section =& $cfg;
                } else {
                    $section =& $cfg->getItem('section',$bloc);
                }
                if (is_object($section)) {
                    $param = $section->getItem('directive',$parameter);
                    if (is_object($param)) {
                        return $layer;
                    }
                }
            }
        }
        return null;
    }

[Top]

Services

hasMethod

    /**
     * Determine if a method has been registered with the registry.
     * May be restrict to a specific handler (if second param not null).
     *
     * @param      string    $method        The full name of the method to check for
     * @param      string    $handler       (optional) Identify a specific handler
     *
     * @return     boolean                  Whether or not the method is registered
     * @since      0.1
     * @access     public
     * @see        getHandler(), callService(), registerService()
     */
    function hasMethod($method, $handler = null)
    {
        list($type, $subtype) = explode('/', $method);
        if (is_null($handler)) {
            return !empty($this->registry[$type][$subtype]);
        } else {
            return isset($this->services[$handler][$type][$subtype]);
        }
    }

[Top]

getHandler

    /**
     * Returns the handler for the given method.
     *
     * @param      string    $method        The full name of the method to check for
     *
     * @return     string                   The handler for the method
     * @since      0.1
     * @access     public
     * @see        hasMethod(), callService(), registerService()
     */
    function getHandler($method)
    {
        list($type, $subtype) = explode('/', $method);
        return !empty($this->registry[$type][$subtype]) ? $this->registry[$type][$subtype] : null;
    }

[Top]

callService

    /**
     * Returns the hook corresponding to the specific handler that
     * provides the functionality requested by the $method parameter.
     * $method is a string consisting of "packagetype/methodname".
     *
     * @param      string    $app          The desired application
     * @param      string    $method       The method to call
     * @param      array     $args         (optional) Arguments to the method
     * @param      mixed     $extra        (optional) Non-standard arguments to the method
     *
     * @return     mixed
     * @since      0.1
     * @access     public
     * @see        hasMethod(), getHandler(), registerService()
     */
    function callService($app, $method, $args = array(), $extra = null)
    {
        if (!$this->hasMethod($method, $app)) {
            // no callback defined
            return PEAR::raiseError('The method "'.$method.'" is not defined in the Registry');
        }

        list($type, $subtype) = explode('/', $method);
        $file = $this->services[$app][$type][$subtype]['file'];
        $function = $this->services[$app][$type][$subtype]['function'];

        if (!@is_readable($file)) {
            return PEAR::raiseError('The file "'.$file.'" is not readable.');
        }
        include_once $file;

        if (is_array($function) && count($function) == 2) {
            list($className, $methodName) = $function;
            $obj = new $className();
            if (!method_exists($obj, $methodName)) {
                return PEAR::raiseError('The method implementing "'.$method.'" ('.$methodName.') is not defined');
            }
        } elseif (is_string($function)) {
            if (!function_exists($function)) {
                return PEAR::raiseError('The function implementing "'.$method.'" ('.$methodName.') is not defined');
            }
        } else {
            return PEAR::raiseError('Callback should be either function or class-method');
        }

        if (!is_null($extra)) {
            // Add non-standard $extra to arguments if necessary before the call to the method
            if (is_array($extra)) {
                foreach ($extra as $item) {
                    array_push($args, $item);
                }
            } else {
                array_push($args, $extra);
            }
        }
        if (isset($className)) {
            return call_user_func_array(array(&$obj, $methodName), $args);
        } else {
            return call_user_func_array($function, $args);
        }
    }

[Top]

getServices

    /**
     * Return a list of the defined services for a specific application.
     *
     * @param      string    $app          The desired application
     *
     * @return     mixed                   List of services defined or PEAR_Error if failure
     * @since      0.1
     * @access     public
     * @see        callService()
     */
    function getServices($app)
    {
        if (!$this->isApplicationRegistered($app)) {
            return PEAR::raiseError('Application "' .$app. '" is not registered');
        }
        return empty($this->services[$app]) ? null : $this->services[$app];
    }

[Top]

registerService

    /**
     * Registers set of methods for communication between 
     * applications (as plugins).
     *
     * @param      string    $app           The desired application
     * @param      string    $method        The full name of the method to register
     * @param      string    $file          The filename where to find callbacks
     * @param      mixed     $func          The hook to declare (function or class-method)
     * @param      array     $args          (optional) Arguments list required for the service
     *
     * @return     mixed                    PEAR::Error is failure, TRUE otherwise
     * @since      0.1
     * @access     public
     * @see        hasMethod(), getHandler(), callService()
     */
    function registerService($app, $method, $file, $func, $args = array())
    {
        if (empty($app)) {
            return PEAR::raiseError('Application handler is required');
        }
        if (!$this->isApplicationRegistered($app)) {
            return PEAR::raiseError('Application "' .$app. '" is not registered');
        }
        @list($type, $subtype) = explode('/', $method);
        if (empty($type)) {
            return PEAR::raiseError('Type part of method "'.$method.'" is missing');
        }
        if (empty($subtype)) {
            return PEAR::raiseError('Subtype part of method "'.$method.'" is missing');
        }
        if (!is_file($file)) {
            return PEAR::raiseError('Resource file "'. $file .'" should be valid');
        }

        $this->services[$app][$type][$subtype] = array(
            'file' => $file,
            'function' => $func,
            'args' => $args
        );
        $this->registry[$type][$subtype] = $app;
        return true;
    }

[Top]

Private

    /**
     * Returns the layers defined
     *
     * @param      boolean   $default       (optional) Whether you want the 'default' layer
     *
     * @return     array                    List of the defined layers
     * @since      0.1
     * @access     private
     * @see        registerApplication(), getConfigLayer(), importConfig(), exportConfig(),
     *             getParam(), setParam(), addParam(), removeParam(), searchParam()
     */
    function _getLayers($default = false)
    {
        $layers = array('user', 'system', 'default');
        if (!$default) {
            array_pop($layers);
        }
        return $layers;
    }

    /**
     * Tell whether a value is an instance of Config_Container object.
     *
     * @param      mixed     $data          The value to test
     *
     * @return     bool                     TRUE if parameter is a Config_Container instance
     * @since      0.1
     * @access     private
     * @see        getParam(), setParam(), addParam(), removeParam(), searchParam()
     */
    function _isConfig($data)
    {
        if (is_object($data) && 
            (strtolower(get_class($data)) == 'config_container' || is_subclass_of($data))) {
            return true;
        } else {
            return false;
        }
    }

[Top]

continue on [Examples]

return to [Table of Contents]