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

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