home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Config / Container.php
Encoding:
PHP Script  |  2008-07-02  |  26.3 KB  |  777 lines

  1. <?php
  2. // +---------------------------------------------------------------------+
  3. // | PHP Version 4                                                       |
  4. // +---------------------------------------------------------------------+
  5. // | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group            |
  6. // +---------------------------------------------------------------------+
  7. // | This source file is subject to version 2.0 of the PHP license,      |
  8. // | that is bundled with this package in the file LICENSE, and is       |
  9. // | available at through the world-wide-web at                          |
  10. // | http://www.php.net/license/2_02.txt.                                |
  11. // | If you did not receive a copy of the PHP license and are unable to  |
  12. // | obtain it through the world-wide-web, please send a note to         |
  13. // | license@php.net so we can mail you a copy immediately.              |
  14. // +---------------------------------------------------------------------+
  15. // | Author: Bertrand Mansion <bmansion@mamasam.com>                     |
  16. // +---------------------------------------------------------------------+
  17. //
  18. // $Id: Container.php,v 1.41 2006/05/30 06:37:28 aashley Exp $
  19.  
  20. require_once 'Config.php';
  21.  
  22. /**
  23. * Interface for Config containers
  24. *
  25. * @author   Bertrand Mansion <bmansion@mamasam.com>
  26. * @package  Config
  27. */
  28. class Config_Container {
  29.  
  30.     /**
  31.     * Container object type
  32.     * Ex: section, directive, comment, blank
  33.     * @var  string
  34.     */
  35.     var $type;
  36.  
  37.     /**
  38.     * Container object name
  39.     * @var  string
  40.     */
  41.     var $name = '';
  42.  
  43.     /**
  44.     * Container object content
  45.     * @var  string
  46.     */
  47.     var $content = '';
  48.  
  49.     /**
  50.     * Container object children
  51.     * @var  array
  52.     */
  53.     var $children = array();
  54.  
  55.     /**
  56.     * Reference to container object's parent
  57.     * @var  object
  58.     */
  59.     var $parent;
  60.     
  61.     /**
  62.     * Array of attributes for this item
  63.     * @var  array
  64.     */
  65.     var $attributes;
  66.  
  67.     /**
  68.     * Unique id to differenciate nodes
  69.     *
  70.     * This is used to compare nodes
  71.     * Will not be needed anymore when this class will use ZendEngine 2
  72.     *
  73.     * @var  int
  74.     */
  75.     var $_id;
  76.  
  77.     /**
  78.     * Constructor
  79.     *
  80.     * @param  string  $type       Type of container object
  81.     * @param  string  $name       Name of container object
  82.     * @param  string  $content    Content of container object
  83.     * @param  array   $attributes Array of attributes for container object
  84.     */
  85.     function Config_Container($type = 'section', $name = '', $content = '', $attributes = null)
  86.     {
  87.         $this->type       = $type;
  88.         $this->name       = $name;
  89.         $this->content    = $content;
  90.         $this->attributes = $attributes;
  91.         $this->parent     = null;
  92.         if (version_compare(PHP_VERSION, '5.0.0', 'gt')) {
  93.             $this->_id    = uniqid($name.$type, true);
  94.         } else {
  95.             $this->_id    = uniqid(substr($name.$type, 0, 114), true);
  96.         }
  97.     } // end constructor
  98.  
  99.     /**
  100.     * Create a child for this item.
  101.     * @param  string  $type       type of item: directive, section, comment, blank...
  102.     * @param  mixed   $item       item name
  103.     * @param  string  $content    item content
  104.     * @param  array   $attributes item attributes
  105.     * @param  string  $where      choose a position 'bottom', 'top', 'after', 'before'
  106.     * @param  object  $target     needed if you choose 'before' or 'after' for where
  107.     * @return object  reference to new item or Pear_Error
  108.     */
  109.     function &createItem($type, $name, $content, $attributes = null, $where = 'bottom', $target = null)
  110.     {
  111.         $item =& new Config_Container($type, $name, $content, $attributes);
  112.         $result =& $this->addItem($item, $where, $target);
  113.         return $result;
  114.     } // end func &createItem
  115.     
  116.     /**
  117.     * Adds an item to this item.
  118.     * @param  object   $item      a container object
  119.     * @param  string   $where     choose a position 'bottom', 'top', 'after', 'before'
  120.     * @param  object   $target    needed if you choose 'before' or 'after' in $where
  121.     * @return mixed    reference to added container on success, Pear_Error on error
  122.     */
  123.     function &addItem(&$item, $where = 'bottom', $target = null)
  124.     {
  125.         if ($this->type != 'section') {
  126.             return PEAR::raiseError('Config_Container::addItem must be called on a section type object.', null, PEAR_ERROR_RETURN);
  127.         }
  128.         if (is_null($target)) {
  129.             $target =& $this;
  130.         }
  131.         if (strtolower(get_class($target)) != 'config_container') {
  132.             return PEAR::raiseError('Target must be a Config_Container object in Config_Container::addItem.', null, PEAR_ERROR_RETURN);
  133.         }
  134.  
  135.         switch ($where) {
  136.             case 'before':
  137.                 $index = $target->getItemIndex();
  138.                 break;
  139.             case 'after':
  140.                 $index = $target->getItemIndex()+1;
  141.                 break;
  142.             case 'top':
  143.                 $index = 0;
  144.                 break;
  145.             case 'bottom':
  146.                 $index = -1;
  147.                 break;
  148.             default:
  149.                 return PEAR::raiseError('Use only top, bottom, before or after in Config_Container::addItem.', null, PEAR_ERROR_RETURN);
  150.         }
  151.         if (isset($index) && $index >= 0) {
  152.             array_splice($this->children, $index, 0, 'tmp');
  153.         } else {
  154.             $index = count($this->children);
  155.         }
  156.         $this->children[$index] =& $item;
  157.         $this->children[$index]->parent =& $this;
  158.  
  159.         return $item;
  160.     } // end func addItem
  161.  
  162.     /**
  163.     * Adds a comment to this item.
  164.     * This is a helper method that calls createItem
  165.     *
  166.     * @param  string    $content        Object content
  167.     * @param  string    $where          Position : 'top', 'bottom', 'before', 'after'
  168.     * @param  object    $target         Needed when $where is 'before' or 'after'
  169.     * @return object  reference to new item or Pear_Error
  170.     */
  171.     function &createComment($content = '', $where = 'bottom', $target = null)
  172.     {
  173.         return $this->createItem('comment', null, $content, null, $where, $target);
  174.     } // end func &createComment
  175.  
  176.     /**
  177.     * Adds a blank line to this item.
  178.     * This is a helper method that calls createItem
  179.     *
  180.     * @return object  reference to new item or Pear_Error
  181.     */
  182.     function &createBlank($where = 'bottom', $target = null)
  183.     {
  184.         return $this->createItem('blank', null, null, null, $where, $target);
  185.     } // end func &createBlank
  186.  
  187.     /**
  188.     * Adds a directive to this item.
  189.     * This is a helper method that calls createItem
  190.     *
  191.     * @param  string    $name           Name of new directive
  192.     * @param  string    $content        Content of new directive
  193.     * @param  mixed     $attributes     Directive attributes
  194.     * @param  string    $where          Position : 'top', 'bottom', 'before', 'after'
  195.     * @param  object    $target         Needed when $where is 'before' or 'after'
  196.     * @return object  reference to new item or Pear_Error
  197.     */
  198.     function &createDirective($name, $content, $attributes = null, $where = 'bottom', $target = null)
  199.     {
  200.         return $this->createItem('directive', $name, $content, $attributes, $where, $target);
  201.     } // end func &createDirective
  202.  
  203.     /**
  204.     * Adds a section to this item.
  205.     *
  206.     * This is a helper method that calls createItem
  207.     * If the section already exists, it won't create a new one. 
  208.     * It will return reference to existing item.
  209.     *
  210.     * @param  string    $name           Name of new section
  211.     * @param  array     $attributes     Section attributes
  212.     * @param  string    $where          Position : 'top', 'bottom', 'before', 'after'
  213.     * @param  object    $target         Needed when $where is 'before' or 'after'
  214.     * @return object  reference to new item or Pear_Error
  215.     */
  216.     function &createSection($name, $attributes = null, $where = 'bottom', $target = null)
  217.     {
  218.         return $this->createItem('section', $name, null, $attributes, $where, $target);
  219.     } // end func &createSection
  220.  
  221.     /**
  222.     * Tries to find the specified item(s) and returns the objects.
  223.     *
  224.     * Examples:
  225.     * $directives =& $obj->getItem('directive');
  226.     * $directive_bar_4 =& $obj->getItem('directive', 'bar', null, 4);
  227.     * $section_foo =& $obj->getItem('section', 'foo');
  228.     *
  229.     * This method can only be called on an object of type 'section'.
  230.     * Note that root is a section.
  231.     * This method is not recursive and tries to keep the current structure.
  232.     * For a deeper search, use searchPath()
  233.     *
  234.     * @param  string    $type        Type of item: directive, section, comment, blank...
  235.     * @param  mixed     $name        Item name
  236.     * @param  mixed     $content     Find item with this content
  237.     * @param  array     $attributes  Find item with attribute set to the given value
  238.     * @param  int       $index       Index of the item in the returned object list. If it is not set, will try to return the last item with this name.
  239.     * @return mixed  reference to item found or false when not found
  240.     * @see &searchPath()
  241.     */
  242.     function &getItem($type = null, $name = null, $content = null, $attributes = null, $index = -1)
  243.     {
  244.         if ($this->type != 'section') {
  245.             return PEAR::raiseError('Config_Container::getItem must be called on a section type object.', null, PEAR_ERROR_RETURN);
  246.         }
  247.         if (!is_null($type)) {
  248.             $testFields[] = 'type';
  249.         }
  250.         if (!is_null($name)) {
  251.             $testFields[] = 'name';
  252.         }
  253.         if (!is_null($content)) {
  254.             $testFields[] = 'content';
  255.         }
  256.         if (!is_null($attributes) && is_array($attributes)) {
  257.             $testFields[] = 'attributes';
  258.         }
  259.  
  260.         $itemsArr = array();
  261.         $fieldsToMatch = count($testFields);
  262.         for ($i = 0, $count = count($this->children); $i < $count; $i++) {
  263.             $match = 0;
  264.             reset($testFields);
  265.             foreach ($testFields as $field) {
  266.                 if ($field != 'attributes') {
  267.                     if ($this->children[$i]->$field == ${$field}) {
  268.                         $match++;
  269.                     }
  270.                 } else {
  271.                     // Look for attributes in array
  272.                     $attrToMatch = count($attributes);
  273.                     $attrMatch = 0;
  274.                     foreach ($attributes as $key => $value) {
  275.                         if (isset($this->children[$i]->attributes[$key]) &&
  276.                             $this->children[$i]->attributes[$key] == $value) {
  277.                             $attrMatch++;
  278.                         }
  279.                     }
  280.                     if ($attrMatch == $attrToMatch) {
  281.                         $match++;
  282.                     }
  283.                 }
  284.             }
  285.             if ($match == $fieldsToMatch) {
  286.                 $itemsArr[] =& $this->children[$i];
  287.             }
  288.         }
  289.         if ($index >= 0) {
  290.             if (isset($itemsArr[$index])) {
  291.                 return $itemsArr[$index];
  292.             } else {
  293.                 $return = false;
  294.                 return $return;
  295.             }
  296.         } else {
  297.             if ($count = count($itemsArr)) {
  298.                 return $itemsArr[$count-1];
  299.             } else {
  300.                 $return = false;
  301.                 return $return;
  302.             }
  303.         }
  304.     } // end func &getItem
  305.  
  306.     /**
  307.     * Finds a node using XPATH like format.
  308.     * 
  309.     * The search format is an array:
  310.     * array(item1, item2, item3, ...)
  311.     *
  312.     * Each item can be defined as the following:
  313.     * item = 'string' : will match the container named 'string'
  314.     * item = array('string', array('name' => 'xyz'))
  315.     * will match the container name 'string' whose attribute name is equal to "xyz"
  316.     * For example : <string name="xyz">
  317.     * 
  318.     * @param    mixed   Search path and attributes
  319.     * 
  320.     * @return   mixed   Config_Container object, array of Config_Container objects or false on failure.
  321.     * @access   public
  322.     */
  323.     function &searchPath($args)
  324.     {
  325.         if ($this->type != 'section') {
  326.             return PEAR::raiseError('Config_Container::searchPath must be called on a section type object.', null, PEAR_ERROR_RETURN);
  327.         }
  328.  
  329.         $arg = array_shift($args);
  330.  
  331.         if (is_array($arg)) {
  332.             $name = $arg[0];
  333.             $attributes = $arg[1];
  334.         } else {
  335.             $name = $arg;
  336.             $attributes = null;
  337.         }
  338.         // find all the matches for first..
  339.         $match =& $this->getItem(null, $name, null, $attributes);
  340.  
  341.         if (!$match) {
  342.             $return = false;
  343.             return $return;
  344.         }
  345.         if (!empty($args)) {
  346.             return $match->searchPath($args);
  347.         }
  348.         return $match;
  349.     } // end func &searchPath
  350.  
  351.     /**
  352.     * Return a child directive's content.
  353.     * 
  354.     * This method can use two different search approach, depending on
  355.     * the parameter it is given. If the parameter is an array, it will use
  356.     * the {@link Config_Container::searchPath()} method. If it is a string, 
  357.     * it will use the {@link Config_Container::getItem()} method.
  358.     *
  359.     * Example:
  360.     * <code>
  361.     * require_once 'Config.php';
  362.     * $ini = new Config();
  363.     * $conf =& $ini->parseConfig('/path/to/config.ini', 'inicommented');
  364.     *
  365.     * // Will return the value found at :
  366.     * // [Database]
  367.     * // host=localhost
  368.     * echo $conf->directiveContent(array('Database', 'host')));
  369.     *
  370.     * // Will return the value found at :
  371.     * // date="dec-2004"
  372.     * echo $conf->directiveContent('date');
  373.     *
  374.     * </code>
  375.     *
  376.     * @param    mixed   Search path and attributes or a directive name
  377.     * @param    int     Index of the item in the returned directive list.
  378.     *                   Eventually used if args is a string.
  379.     * 
  380.     * @return   mixed   Content of directive or false if not found.
  381.     * @access   public
  382.     */
  383.     function directiveContent($args, $index = -1)
  384.     {
  385.         if (is_array($args)) {
  386.             $item =& $this->searchPath($args);
  387.         } else {
  388.             $item =& $this->getItem('directive', $args, null, null, $index);
  389.         }
  390.         if ($item) {
  391.             return $item->getContent();
  392.         }
  393.         return false;
  394.     } // end func getDirectiveContent
  395.  
  396.     /**
  397.     * Returns how many children this container has
  398.     *
  399.     * @param  string    $type    type of children counted
  400.     * @param  string    $name    name of children counted
  401.     * @return int  number of children found
  402.     */
  403.     function countChildren($type = null, $name = null)
  404.     {
  405.         if (is_null($type) && is_null($name)) {
  406.             return count($this->children);
  407.         }
  408.         $count = 0;
  409.         if (isset($name) && isset($type)) {
  410.             for ($i = 0, $children = count($this->children); $i < $children; $i++) {
  411.                 if ($this->children[$i]->name === $name && 
  412.                     $this->children[$i]->type == $type) {
  413.                     $count++;
  414.                 }
  415.             }
  416.             return $count;
  417.         }
  418.         if (isset($type)) {
  419.             for ($i = 0, $children = count($this->children); $i < $children; $i++) {
  420.                 if ($this->children[$i]->type == $type) {
  421.                     $count++;
  422.                 }
  423.             }
  424.             return $count;
  425.         }
  426.         if (isset($name)) {
  427.             // Some directives can have the same name
  428.             for ($i = 0, $children = count($this->children); $i < $children; $i++) {
  429.                 if ($this->children[$i]->name === $name) {
  430.                     $count++;
  431.                 }
  432.             }
  433.             return $count;
  434.         }
  435.     } // end func &countChildren
  436.  
  437.     /**
  438.     * Deletes an item (section, directive, comment...) from the current object
  439.     * TODO: recursive remove in sub-sections
  440.     * @return mixed  true if object was removed, false if not, or PEAR_Error if root
  441.     */
  442.     function removeItem()
  443.     {
  444.         if ($this->isRoot()) {
  445.             return PEAR::raiseError('Cannot remove root item in Config_Container::removeItem.', null, PEAR_ERROR_RETURN);
  446.         }
  447.         $index = $this->getItemIndex();
  448.         if (!is_null($index)) {
  449.             array_splice($this->parent->children, $index, 1);
  450.             return true;
  451.         }
  452.         return false;
  453.     } // end func removeItem
  454.  
  455.     /**
  456.     * Returns the item index in its parent children array.
  457.     * @return int  returns int or null if root object
  458.     */
  459.     function getItemIndex()
  460.     {
  461.         if (is_object($this->parent)) {
  462.             // This will be optimized with Zend Engine 2
  463.             $pchildren =& $this->parent->children;
  464.             for ($i = 0, $count = count($pchildren); $i < $count; $i++) {
  465.                 if ($pchildren[$i]->_id == $this->_id) {
  466.                     return $i;
  467.                 }
  468.             }
  469.         }
  470.         return;
  471.     } // end func getItemIndex
  472.  
  473.     /**
  474.     * Returns the item rank in its parent children array
  475.     * according to other items with same type and name.
  476.     * @param bool  count items differently by type
  477.     * @return int  returns int or null if root object
  478.     */
  479.     function getItemPosition($byType = true)
  480.     {
  481.         if (is_object($this->parent)) {
  482.             $pchildren =& $this->parent->children;
  483.             for ($i = 0, $count = count($pchildren); $i < $count; $i++) {
  484.                 if ($pchildren[$i]->name == $this->name) {
  485.                     if ($byType == true) {
  486.                         if ($pchildren[$i]->type == $this->type) {
  487.                             $obj[] =& $pchildren[$i];
  488.                         }
  489.                     } else {
  490.                         $obj[] =& $pchildren[$i];
  491.                     }
  492.                 }
  493.             }
  494.             for ($i = 0, $count = count($obj); $i < $count; $i++) {
  495.                 if ($obj[$i]->_id == $this->_id) {
  496.                     return $i;
  497.                 }
  498.             }
  499.         }
  500.         return;
  501.     } // end func getItemPosition
  502.  
  503.     /**
  504.     * Returns the item parent object.
  505.     * @return object  returns reference to parent object or null if root object
  506.     */
  507.     function &getParent()
  508.     {
  509.         return $this->parent;
  510.     } // end func &getParent
  511.  
  512.     /**
  513.     * Returns the item parent object.
  514.     * @return mixed  returns reference to child object or false if child does not exist
  515.     */
  516.     function &getChild($index = 0)
  517.     {
  518.         if (!empty($this->children[$index])) {
  519.             return $this->children[$index];
  520.         } else {
  521.             return false;
  522.         }
  523.     } // end func &getChild
  524.  
  525.     /**
  526.     * Set this item's name.
  527.     * @return void
  528.     */
  529.     function setName($name)
  530.     {
  531.         $this->name = $name;
  532.     } // end func setName
  533.  
  534.     /**
  535.     * Get this item's name.
  536.     * @return string    item's name
  537.     */
  538.     function getName()
  539.     {
  540.         return $this->name;
  541.     } // end func getName
  542.  
  543.     /**
  544.     * Set this item's content.
  545.     * @return void
  546.     */
  547.     function setContent($content)
  548.     {
  549.         $this->content = $content;
  550.     } // end func setContent
  551.     
  552.     /**
  553.     * Get this item's content.
  554.     * @return string    item's content
  555.     */
  556.     function getContent()
  557.     {
  558.         return $this->content;
  559.     } // end func getContent
  560.  
  561.     /**
  562.     * Set this item's type.
  563.     * @return void
  564.     */
  565.     function setType($type)
  566.     {
  567.         $this->type = $type;
  568.     } // end func setType
  569.  
  570.     /**
  571.     * Get this item's type.
  572.     * @return string    item's type
  573.     */
  574.     function getType()
  575.     {
  576.         return $this->type;
  577.     } // end func getType
  578.  
  579.     /**
  580.     * Set this item's attributes.
  581.     * @param  array    $attributes        Array of attributes
  582.     * @return void
  583.     */
  584.     function setAttributes($attributes)
  585.     {
  586.         $this->attributes = $attributes;
  587.     } // end func setAttributes
  588.  
  589.     /**
  590.     * Set this item's attributes.
  591.     * @param  array    $attributes        Array of attributes
  592.     * @return void
  593.     */
  594.     function updateAttributes($attributes)
  595.     {
  596.         if (is_array($attributes)) {
  597.             foreach ($attributes as $key => $value) {
  598.                 $this->attributes[$key] = $value;
  599.             }
  600.         }
  601.     } // end func updateAttributes
  602.  
  603.     /**
  604.     * Get this item's attributes.
  605.     * @return array    item's attributes
  606.     */
  607.     function getAttributes()
  608.     {
  609.         return $this->attributes;
  610.     } // end func getAttributes
  611.     
  612.     /**
  613.     * Get one attribute value of this item
  614.     * @param  string   $attribute        Attribute key
  615.     * @return mixed    item's attribute value
  616.     */
  617.     function getAttribute($attribute)
  618.     {
  619.         if (isset($this->attributes[$attribute])) {
  620.             return $this->attributes[$attribute];
  621.         }
  622.         return null;
  623.     } // end func getAttribute
  624.  
  625.     /**
  626.     * Set a children directive content.
  627.     * This is an helper method calling getItem and addItem or setContent for you.
  628.     * If the directive does not exist, it will be created at the bottom.
  629.     *
  630.     * @param  string    $name        Name of the directive to look for
  631.     * @param  mixed     $content     New content
  632.     * @param  int       $index       Index of the directive to set,
  633.     *                                in case there are more than one directive
  634.     *                                with the same name
  635.     * @return object    newly set directive
  636.     */
  637.     function &setDirective($name, $content, $index = -1)
  638.     {
  639.         $item =& $this->getItem('directive', $name, null, null, $index);
  640.         if ($item === false || PEAR::isError($item)) {
  641.             // Directive does not exist, will create one
  642.             unset($item);
  643.             return $this->createDirective($name, $content, null);
  644.         } else {
  645.             // Change existing directive value
  646.             $item->setContent($content);
  647.             return $item;
  648.         }
  649.     } // end func setDirective
  650.  
  651.     /**
  652.     * Is this item root, in a config container object
  653.     * @return bool    true if item is root
  654.     */
  655.     function isRoot()
  656.     {
  657.         if (is_null($this->parent)) {
  658.             return true;
  659.         }
  660.         return false;
  661.     } // end func isRoot
  662.  
  663.     /**
  664.     * Call the toString methods in the container plugin
  665.     * @param    string  $configType  Type of configuration used to generate the string
  666.     * @param    array   $options     Specify special options used by the parser
  667.     * @return   mixed   true on success or PEAR_ERROR
  668.     */
  669.     function toString($configType, $options = array())
  670.     {
  671.         $configType = strtolower($configType);
  672.         if (!isset($GLOBALS['CONFIG_TYPES'][$configType])) {
  673.             return PEAR::raiseError("Configuration type '$configType' is not registered in Config_Container::toString.", null, PEAR_ERROR_RETURN);
  674.         }
  675.         $includeFile = $GLOBALS['CONFIG_TYPES'][$configType][0];
  676.         $className   = $GLOBALS['CONFIG_TYPES'][$configType][1];
  677.         include_once($includeFile);
  678.         $renderer = new $className($options);
  679.         return $renderer->toString($this);
  680.     } // end func toString
  681.  
  682.     /**
  683.     * Returns a key/value pair array of the container and its children.
  684.     *
  685.     * Format : section[directive][index] = value
  686.     * If the container has attributes, it will use '@' and '#'
  687.     * index is here because multiple directives can have the same name.
  688.     *
  689.     * @param    bool    $useAttr        Whether to return the attributes too
  690.     * @return array
  691.     */
  692.     function toArray($useAttr = true)
  693.     {
  694.         $array[$this->name] = array();
  695.         switch ($this->type) {
  696.             case 'directive':
  697.                 if ($useAttr && count($this->attributes) > 0) {
  698.                     $array[$this->name]['#'] = $this->content;
  699.                     $array[$this->name]['@'] = $this->attributes;
  700.                 } else {
  701.                     $array[$this->name] = $this->content;
  702.                 }
  703.                 break;
  704.             case 'section':
  705.                 if ($useAttr && count($this->attributes) > 0) {
  706.                     $array[$this->name]['@'] = $this->attributes;
  707.                 }
  708.                 if ($count = count($this->children)) {
  709.                     for ($i = 0; $i < $count; $i++) {
  710.                         $newArr = $this->children[$i]->toArray($useAttr);
  711.                         if (!is_null($newArr)) {
  712.                             foreach ($newArr as $key => $value) {
  713.                                 if (isset($array[$this->name][$key])) {
  714.                                     // duplicate name/type
  715.                                     if (!is_array($array[$this->name][$key]) ||
  716.                                         !isset($array[$this->name][$key][0])) {
  717.                                         $old = $array[$this->name][$key];
  718.                                         unset($array[$this->name][$key]);
  719.                                         $array[$this->name][$key][0] = $old;
  720.                                     }
  721.                                     $array[$this->name][$key][] = $value;
  722.                                 } else {
  723.                                     $array[$this->name][$key] = $value;
  724.                                 }
  725.                             }
  726.                         }
  727.                     }
  728.                 }
  729.                 break;
  730.             default:
  731.                 return null;
  732.         }
  733.         return $array;
  734.     } // end func toArray
  735.     
  736.     /**
  737.     * Writes the configuration to a file
  738.     * 
  739.     * @param  mixed  $datasrc        Info on datasource such as path to the configuraton file or dsn...
  740.     * @param  string $configType     Type of configuration
  741.     * @param  array  $options        Options for writer
  742.     * @access public
  743.     * @return mixed     true on success or PEAR_ERROR
  744.     */
  745.     function writeDatasrc($datasrc, $configType, $options = array())
  746.     {
  747.         $configType = strtolower($configType);
  748.         if (!isset($GLOBALS['CONFIG_TYPES'][$configType])) {
  749.             return PEAR::raiseError("Configuration type '$configType' is not registered in Config_Container::writeDatasrc.", null, PEAR_ERROR_RETURN);
  750.         }
  751.         $includeFile = $GLOBALS['CONFIG_TYPES'][$configType][0];
  752.         $className = $GLOBALS['CONFIG_TYPES'][$configType][1];
  753.         include_once($includeFile);
  754.  
  755.         $writeMethodName = (version_compare(phpversion(), '5', '<')) ? 'writedatasrc' : 'writeDatasrc';
  756.         if (in_array($writeMethodName, get_class_methods($className))) {
  757.             $writer = new $className($options);
  758.             return $writer->writeDatasrc($datasrc, $this);
  759.         }
  760.  
  761.         // Default behaviour
  762.         $fp = @fopen($datasrc, 'w');
  763.         if ($fp) {
  764.             $string = $this->toString($configType, $options);
  765.             $len = strlen($string);
  766.             @flock($fp, LOCK_EX);
  767.             @fwrite($fp, $string, $len);
  768.             @flock($fp, LOCK_UN);
  769.             @fclose($fp);
  770.             return true;
  771.         } else {
  772.             return PEAR::raiseError('Cannot open datasource for writing.', 1, PEAR_ERROR_RETURN);
  773.         }
  774.     } // end func writeDatasrc
  775. } // end class Config_Container
  776. ?>
  777.