Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php// +---------------------------------------------------------------------+// | PHP Version 4 |// +---------------------------------------------------------------------+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |// +---------------------------------------------------------------------+// | This source file is subject to version 2.0 of the PHP license, |// | that is bundled with this package in the file LICENSE, and is |// | available at through the world-wide-web at |// | http://www.php.net/license/2_02.txt. |// | If you did not receive a copy of the PHP license and are unable to |// | obtain it through the world-wide-web, please send a note to |// | license@php.net so we can mail you a copy immediately. |// +---------------------------------------------------------------------+// | Author: Bertrand Mansion <bmansion@mamasam.com> |// +---------------------------------------------------------------------+//// $Id: Container.php 306597 2010-12-24 05:11:09Z aharvey $require_once 'Config.php';/*** Interface for Config containers** @author Bertrand Mansion <bmansion@mamasam.com>* @package Config*/class Config_Container {/*** Container object type* Ex: section, directive, comment, blank* @var string*/var $type;/*** Container object name* @var string*/var $name = '';/*** Container object content* @var string*/var $content = '';/*** Container object children* @var array*/var $children = array();/*** Reference to container object's parent* @var object*/var $parent;/*** Array of attributes for this item* @var array*/var $attributes;/*** Unique id to differenciate nodes** This is used to compare nodes* Will not be needed anymore when this class will use ZendEngine 2** @var int*/var $_id;/*** Constructor** @param string $type Type of container object* @param string $name Name of container object* @param string $content Content of container object* @param array $attributes Array of attributes for container object*/function Config_Container($type = 'section', $name = '', $content = '', $attributes = null){$this->type = $type;$this->name = $name;$this->content = $content;$this->attributes = $attributes;$this->parent = null;if (version_compare(PHP_VERSION, '5.0.0', 'gt')) {$this->_id = uniqid($name.$type, true);} else {$this->_id = uniqid(substr($name.$type, 0, 114), true);}} // end constructor/*** Create a child for this item.* @param string $type type of item: directive, section, comment, blank...* @param mixed $item item name* @param string $content item content* @param array $attributes item attributes* @param string $where choose a position 'bottom', 'top', 'after', 'before'* @param object $target needed if you choose 'before' or 'after' for where* @return object reference to new item or Pear_Error*/function &createItem($type, $name, $content, $attributes = null, $where = 'bottom', $target = null){$item = new Config_Container($type, $name, $content, $attributes);$result =& $this->addItem($item, $where, $target);return $result;} // end func &createItem/*** Adds an item to this item.* @param object $item a container object* @param string $where choose a position 'bottom', 'top', 'after', 'before'* @param object $target needed if you choose 'before' or 'after' in $where* @return mixed reference to added container on success, Pear_Error on error*/function &addItem(&$item, $where = 'bottom', $target = null){if ($this->type != 'section') {return PEAR::raiseError('Config_Container::addItem must be called on a section type object.', null, PEAR_ERROR_RETURN);}if (is_null($target)) {$target =& $this;}if (strtolower(get_class($target)) != 'config_container') {return PEAR::raiseError('Target must be a Config_Container object in Config_Container::addItem.', null, PEAR_ERROR_RETURN);}switch ($where) {case 'before':$index = $target->getItemIndex();break;case 'after':$index = $target->getItemIndex()+1;break;case 'top':$index = 0;break;case 'bottom':$index = -1;break;default:return PEAR::raiseError('Use only top, bottom, before or after in Config_Container::addItem.', null, PEAR_ERROR_RETURN);}if (isset($index) && $index >= 0) {array_splice($this->children, $index, 0, 'tmp');} else {$index = count($this->children);}$this->children[$index] =& $item;$this->children[$index]->parent =& $this;return $item;} // end func addItem/*** Adds a comment to this item.* This is a helper method that calls createItem** @param string $content Object content* @param string $where Position : 'top', 'bottom', 'before', 'after'* @param object $target Needed when $where is 'before' or 'after'* @return object reference to new item or Pear_Error*/function &createComment($content = '', $where = 'bottom', $target = null){return $this->createItem('comment', null, $content, null, $where, $target);} // end func &createComment/*** Adds a blank line to this item.* This is a helper method that calls createItem** @return object reference to new item or Pear_Error*/function &createBlank($where = 'bottom', $target = null){return $this->createItem('blank', null, null, null, $where, $target);} // end func &createBlank/*** Adds a directive to this item.* This is a helper method that calls createItem** @param string $name Name of new directive* @param string $content Content of new directive* @param mixed $attributes Directive attributes* @param string $where Position : 'top', 'bottom', 'before', 'after'* @param object $target Needed when $where is 'before' or 'after'* @return object reference to new item or Pear_Error*/function &createDirective($name, $content, $attributes = null, $where = 'bottom', $target = null){return $this->createItem('directive', $name, $content, $attributes, $where, $target);} // end func &createDirective/*** Adds a section to this item.** This is a helper method that calls createItem* If the section already exists, it won't create a new one.* It will return reference to existing item.** @param string $name Name of new section* @param array $attributes Section attributes* @param string $where Position : 'top', 'bottom', 'before', 'after'* @param object $target Needed when $where is 'before' or 'after'* @return object reference to new item or Pear_Error*/function &createSection($name, $attributes = null, $where = 'bottom', $target = null){return $this->createItem('section', $name, null, $attributes, $where, $target);} // end func &createSection/*** Tries to find the specified item(s) and returns the objects.** Examples:* $directives =& $obj->getItem('directive');* $directive_bar_4 =& $obj->getItem('directive', 'bar', null, 4);* $section_foo =& $obj->getItem('section', 'foo');** This method can only be called on an object of type 'section'.* Note that root is a section.* This method is not recursive and tries to keep the current structure.* For a deeper search, use searchPath()** @param string $type Type of item: directive, section, comment, blank...* @param mixed $name Item name* @param mixed $content Find item with this content* @param array $attributes Find item with attribute set to the given value* @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.* @return mixed reference to item found or false when not found* @see &searchPath()*/function &getItem($type = null, $name = null, $content = null, $attributes = null, $index = -1){if ($this->type != 'section') {return PEAR::raiseError('Config_Container::getItem must be called on a section type object.', null, PEAR_ERROR_RETURN);}if (!is_null($type)) {$testFields[] = 'type';}if (!is_null($name)) {$testFields[] = 'name';}if (!is_null($content)) {$testFields[] = 'content';}if (!is_null($attributes) && is_array($attributes)) {$testFields[] = 'attributes';}$itemsArr = array();$fieldsToMatch = count($testFields);for ($i = 0, $count = count($this->children); $i < $count; $i++) {$match = 0;reset($testFields);foreach ($testFields as $field) {if ($field != 'attributes') {if ($this->children[$i]->$field == ${$field}) {$match++;}} else {// Look for attributes in array$attrToMatch = count($attributes);$attrMatch = 0;foreach ($attributes as $key => $value) {if (isset($this->children[$i]->attributes[$key]) &&$this->children[$i]->attributes[$key] == $value) {$attrMatch++;}}if ($attrMatch == $attrToMatch) {$match++;}}}if ($match == $fieldsToMatch) {$itemsArr[] =& $this->children[$i];}}if ($index >= 0) {if (isset($itemsArr[$index])) {return $itemsArr[$index];} else {$return = false;return $return;}} else {if ($count = count($itemsArr)) {return $itemsArr[$count-1];} else {$return = false;return $return;}}} // end func &getItem/*** Finds a node using XPATH like format.** The search format is an array:* array(item1, item2, item3, ...)** Each item can be defined as the following:* item = 'string' : will match the container named 'string'* item = array('string', array('name' => 'xyz'))* will match the container name 'string' whose attribute name is equal to "xyz"* For example : <string name="xyz">** @param mixed Search path and attributes** @return mixed Config_Container object, array of Config_Container objects or false on failure.* @access public*/function &searchPath($args){if ($this->type != 'section') {return PEAR::raiseError('Config_Container::searchPath must be called on a section type object.', null, PEAR_ERROR_RETURN);}$arg = array_shift($args);if (is_array($arg)) {$name = $arg[0];$attributes = $arg[1];} else {$name = $arg;$attributes = null;}// find all the matches for first..$match =& $this->getItem(null, $name, null, $attributes);if (!$match) {$return = false;return $return;}if (!empty($args)) {return $match->searchPath($args);}return $match;} // end func &searchPath/*** Return a child directive's content.** This method can use two different search approach, depending on* the parameter it is given. If the parameter is an array, it will use* the {@link Config_Container::searchPath()} method. If it is a string,* it will use the {@link Config_Container::getItem()} method.** Example:* <code>* require_once 'Config.php';* $ini = new Config();* $conf =& $ini->parseConfig('/path/to/config.ini', 'inicommented');** // Will return the value found at :* // [Database]* // host=localhost* echo $conf->directiveContent(array('Database', 'host')));** // Will return the value found at :* // date="dec-2004"* echo $conf->directiveContent('date');** </code>** @param mixed Search path and attributes or a directive name* @param int Index of the item in the returned directive list.* Eventually used if args is a string.** @return mixed Content of directive or false if not found.* @access public*/function directiveContent($args, $index = -1){if (is_array($args)) {$item =& $this->searchPath($args);} else {$item =& $this->getItem('directive', $args, null, null, $index);}if ($item) {return $item->getContent();}return false;} // end func getDirectiveContent/*** Returns how many children this container has** @param string $type type of children counted* @param string $name name of children counted* @return int number of children found*/function countChildren($type = null, $name = null){if (is_null($type) && is_null($name)) {return count($this->children);}$count = 0;if (isset($name) && isset($type)) {for ($i = 0, $children = count($this->children); $i < $children; $i++) {if ($this->children[$i]->name === $name &&$this->children[$i]->type == $type) {$count++;}}return $count;}if (isset($type)) {for ($i = 0, $children = count($this->children); $i < $children; $i++) {if ($this->children[$i]->type == $type) {$count++;}}return $count;}if (isset($name)) {// Some directives can have the same namefor ($i = 0, $children = count($this->children); $i < $children; $i++) {if ($this->children[$i]->name === $name) {$count++;}}return $count;}} // end func &countChildren/*** Deletes an item (section, directive, comment...) from the current object* TODO: recursive remove in sub-sections* @return mixed true if object was removed, false if not, or PEAR_Error if root*/function removeItem(){if ($this->isRoot()) {return PEAR::raiseError('Cannot remove root item in Config_Container::removeItem.', null, PEAR_ERROR_RETURN);}$index = $this->getItemIndex();if (!is_null($index)) {array_splice($this->parent->children, $index, 1);return true;}return false;} // end func removeItem/*** Returns the item index in its parent children array.* @return int returns int or null if root object*/function getItemIndex(){if (is_object($this->parent)) {// This will be optimized with Zend Engine 2$pchildren =& $this->parent->children;for ($i = 0, $count = count($pchildren); $i < $count; $i++) {if ($pchildren[$i]->_id == $this->_id) {return $i;}}}return;} // end func getItemIndex/*** Returns the item rank in its parent children array* according to other items with same type and name.* @param bool count items differently by type* @return int returns int or null if root object*/function getItemPosition($byType = true){if (is_object($this->parent)) {$pchildren =& $this->parent->children;for ($i = 0, $count = count($pchildren); $i < $count; $i++) {if ($pchildren[$i]->name == $this->name) {if ($byType == true) {if ($pchildren[$i]->type == $this->type) {$obj[] =& $pchildren[$i];}} else {$obj[] =& $pchildren[$i];}}}for ($i = 0, $count = count($obj); $i < $count; $i++) {if ($obj[$i]->_id == $this->_id) {return $i;}}}return;} // end func getItemPosition/*** Returns the item parent object.* @return object returns reference to parent object or null if root object*/function &getParent(){return $this->parent;} // end func &getParent/*** Returns the item parent object.* @return mixed returns reference to child object or false if child does not exist*/function &getChild($index = 0){if (!empty($this->children[$index])) {return $this->children[$index];} else {return false;}} // end func &getChild/*** Set this item's name.* @return void*/function setName($name){$this->name = $name;} // end func setName/*** Get this item's name.* @return string item's name*/function getName(){return $this->name;} // end func getName/*** Set this item's content.* @return void*/function setContent($content){$this->content = $content;} // end func setContent/*** Get this item's content.* @return string item's content*/function getContent(){return $this->content;} // end func getContent/*** Set this item's type.* @return void*/function setType($type){$this->type = $type;} // end func setType/*** Get this item's type.* @return string item's type*/function getType(){return $this->type;} // end func getType/*** Set this item's attributes.* @param array $attributes Array of attributes* @return void*/function setAttributes($attributes){$this->attributes = $attributes;} // end func setAttributes/*** Set this item's attributes.* @param array $attributes Array of attributes* @return void*/function updateAttributes($attributes){if (is_array($attributes)) {foreach ($attributes as $key => $value) {$this->attributes[$key] = $value;}}} // end func updateAttributes/*** Get this item's attributes.* @return array item's attributes*/function getAttributes(){return $this->attributes;} // end func getAttributes/*** Get one attribute value of this item* @param string $attribute Attribute key* @return mixed item's attribute value*/function getAttribute($attribute){if (isset($this->attributes[$attribute])) {return $this->attributes[$attribute];}return null;} // end func getAttribute/*** Set a children directive content.* This is an helper method calling getItem and addItem or setContent for you.* If the directive does not exist, it will be created at the bottom.** @param string $name Name of the directive to look for* @param mixed $content New content* @param int $index Index of the directive to set,* in case there are more than one directive* with the same name* @return object newly set directive*/function &setDirective($name, $content, $index = -1){$item =& $this->getItem('directive', $name, null, null, $index);if ($item === false || PEAR::isError($item)) {// Directive does not exist, will create oneunset($item);return $this->createDirective($name, $content, null);} else {// Change existing directive value$item->setContent($content);return $item;}} // end func setDirective/*** Is this item root, in a config container object* @return bool true if item is root*/function isRoot(){if (is_null($this->parent)) {return true;}return false;} // end func isRoot/*** Call the toString methods in the container plugin* @param string $configType Type of configuration used to generate the string* @param array $options Specify special options used by the parser* @return mixed true on success or PEAR_ERROR*/function toString($configType, $options = array()){$configType = strtolower($configType);if (!isset($GLOBALS['CONFIG_TYPES'][$configType])) {return PEAR::raiseError("Configuration type '$configType' is not registered in Config_Container::toString.", null, PEAR_ERROR_RETURN);}$includeFile = $GLOBALS['CONFIG_TYPES'][$configType][0];$className = $GLOBALS['CONFIG_TYPES'][$configType][1];include_once($includeFile);$renderer = new $className($options);return $renderer->toString($this);} // end func toString/*** Returns a key/value pair array of the container and its children.** Format : section[directive][index] = value* If the container has attributes, it will use '@' and '#'* index is here because multiple directives can have the same name.** @param bool $useAttr Whether to return the attributes too* @return array*/function toArray($useAttr = true){$array[$this->name] = array();switch ($this->type) {case 'directive':if ($useAttr && count($this->attributes) > 0) {$array[$this->name]['#'] = $this->content;$array[$this->name]['@'] = $this->attributes;} else {$array[$this->name] = $this->content;}break;case 'section':if ($useAttr && count($this->attributes) > 0) {$array[$this->name]['@'] = $this->attributes;}if ($count = count($this->children)) {for ($i = 0; $i < $count; $i++) {$newArr = $this->children[$i]->toArray($useAttr);if (!is_null($newArr)) {foreach ($newArr as $key => $value) {if (isset($array[$this->name][$key])) {// duplicate name/typeif (!is_array($array[$this->name][$key]) ||!isset($array[$this->name][$key][0])) {$old = $array[$this->name][$key];unset($array[$this->name][$key]);$array[$this->name][$key][0] = $old;}$array[$this->name][$key][] = $value;} else {$array[$this->name][$key] = $value;}}}}}break;default:return null;}return $array;} // end func toArray/*** Writes the configuration to a file** @param mixed $datasrc Info on datasource such as path to the configuraton file or dsn...* @param string $configType Type of configuration* @param array $options Options for writer* @access public* @return mixed true on success or PEAR_ERROR*/function writeDatasrc($datasrc, $configType, $options = array()){$configType = strtolower($configType);if (!isset($GLOBALS['CONFIG_TYPES'][$configType])) {return PEAR::raiseError("Configuration type '$configType' is not registered in Config_Container::writeDatasrc.", null, PEAR_ERROR_RETURN);}$includeFile = $GLOBALS['CONFIG_TYPES'][$configType][0];$className = $GLOBALS['CONFIG_TYPES'][$configType][1];include_once($includeFile);$writeMethodName = (version_compare(phpversion(), '5', '<')) ? 'writedatasrc' : 'writeDatasrc';if (in_array($writeMethodName, get_class_methods($className))) {$writer = new $className($options);return $writer->writeDatasrc($datasrc, $this);}// Default behaviour$fp = @fopen($datasrc, 'w');if ($fp) {$string = $this->toString($configType, $options);$len = strlen($string);@flock($fp, LOCK_EX);@fwrite($fp, $string, $len);@flock($fp, LOCK_UN);@fclose($fp);return true;} else {return PEAR::raiseError('Cannot open datasource for writing.', 1, PEAR_ERROR_RETURN);}} // end func writeDatasrc} // end class Config_Container?>