PageRenderTime 72ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/php/Config/Container.php

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