PageRenderTime 8ms CodeModel.GetById 12ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/external/pear/Config/Container.php

https://bitbucket.org/thoti/phpscheduleit-sjr-edition
PHP | 776 lines | 415 code | 48 blank | 313 comment | 103 complexity | 0082f941795d5abcda9476dd106a9d0e MD5 | raw file
  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*/
 28class 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        require_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?>