PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Config/Config.php

https://github.com/mrbanzai/zf2
PHP | 536 lines | 228 code | 42 blank | 266 comment | 34 complexity | 8cc655fca1c1ca7df42a75ce031fbdc3 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Config
  17. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. */
  20. /**
  21. * @namespace
  22. */
  23. namespace Zend\Config;
  24. /**
  25. * @uses \Zend\Config\Exception
  26. * @category Zend
  27. * @package Zend_Config
  28. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  29. * @license http://framework.zend.com/license/new-bsd New BSD License
  30. */
  31. class Config implements \Countable, \Iterator, \ArrayAccess
  32. {
  33. /**
  34. * Whether in-memory modifications to configuration data are allowed
  35. *
  36. * @var boolean
  37. */
  38. protected $_allowModifications;
  39. /**
  40. * Iteration index
  41. *
  42. * @var integer
  43. */
  44. protected $_index;
  45. /**
  46. * Number of elements in configuration data
  47. *
  48. * @var integer
  49. */
  50. protected $_count;
  51. /**
  52. * Contains array of configuration data
  53. *
  54. * @var array
  55. */
  56. protected $_data;
  57. /**
  58. * Used when unsetting values during iteration to ensure we do not skip
  59. * the next element
  60. *
  61. * @var boolean
  62. */
  63. protected $_skipNextIteration;
  64. /**
  65. * Contains which config file sections were loaded. This is null
  66. * if all sections were loaded, a string name if one section is loaded
  67. * and an array of string names if multiple sections were loaded.
  68. *
  69. * @var mixed
  70. */
  71. protected $_loadedSection;
  72. /**
  73. * This is used to track section inheritance. The keys are names of sections that
  74. * extend other sections, and the values are the extended sections.
  75. *
  76. * @var array
  77. */
  78. protected $_extends = array();
  79. /**
  80. * Internal error messages
  81. *
  82. * @var null|array
  83. */
  84. protected $_errorMessages = array();
  85. /**
  86. * Zend_Config provides a property based interface to
  87. * an array. The data are read-only unless $allowModifications
  88. * is set to true on construction.
  89. *
  90. * Zend_Config also implements Countable and Iterator to
  91. * facilitate easy access to the data.
  92. *
  93. * @param array $array
  94. * @param boolean $allowModifications
  95. * @return void
  96. */
  97. public function __construct(array $array, $allowModifications = false)
  98. {
  99. $this->_allowModifications = (boolean) $allowModifications;
  100. $this->_loadedSection = null;
  101. $this->_index = 0;
  102. $this->_data = array();
  103. foreach ($array as $key => $value) {
  104. if (is_array($value)) {
  105. $this->_data[$key] = new self($value, $this->_allowModifications);
  106. } else {
  107. $this->_data[$key] = $value;
  108. }
  109. }
  110. $this->_count = count($this->_data);
  111. }
  112. /**
  113. * Retrieve a value and return $default if there is no element set.
  114. *
  115. * @param string $name
  116. * @param mixed $default
  117. * @return mixed
  118. */
  119. public function get($name, $default = null)
  120. {
  121. $result = $default;
  122. if (array_key_exists($name, $this->_data)) {
  123. $result = $this->_data[$name];
  124. }
  125. return $result;
  126. }
  127. /**
  128. * Magic function so that $obj->value will work.
  129. *
  130. * @param string $name
  131. * @return mixed
  132. */
  133. public function __get($name)
  134. {
  135. return $this->get($name);
  136. }
  137. /**
  138. * Only allow setting of a property if $allowModifications
  139. * was set to true on construction. Otherwise, throw an exception.
  140. *
  141. * @param string $name
  142. * @param mixed $value
  143. * @throws \Zend\Config\Exception
  144. * @return void
  145. */
  146. public function __set($name, $value)
  147. {
  148. if ($this->_allowModifications) {
  149. if (is_array($value)) {
  150. $this->_data[$name] = new self($value, true);
  151. } else {
  152. $this->_data[$name] = $value;
  153. }
  154. $this->_count = count($this->_data);
  155. } else {
  156. throw new Exception\InvalidArgumentException('Zend_Config is read only');
  157. }
  158. }
  159. /**
  160. * Deep clone of this instance to ensure that nested Zend_Configs
  161. * are also cloned.
  162. *
  163. * @return void
  164. */
  165. public function __clone()
  166. {
  167. $array = array();
  168. foreach ($this->_data as $key => $value) {
  169. if ($value instanceof Config) {
  170. $array[$key] = clone $value;
  171. } else {
  172. $array[$key] = $value;
  173. }
  174. }
  175. $this->_data = $array;
  176. }
  177. /**
  178. * Return an associative array of the stored data.
  179. *
  180. * @return array
  181. */
  182. public function toArray()
  183. {
  184. $array = array();
  185. $data = $this->_data;
  186. foreach ($data as $key => $value) {
  187. if ($value instanceof Config) {
  188. $array[$key] = $value->toArray();
  189. } else {
  190. $array[$key] = $value;
  191. }
  192. }
  193. return $array;
  194. }
  195. /**
  196. * Support isset() overloading on PHP 5.1
  197. *
  198. * @param string $name
  199. * @return boolean
  200. */
  201. public function __isset($name)
  202. {
  203. return isset($this->_data[$name]);
  204. }
  205. /**
  206. * Support unset() overloading on PHP 5.1
  207. *
  208. * @param string $name
  209. * @throws \Zend\Config\Exception
  210. * @return void
  211. */
  212. public function __unset($name)
  213. {
  214. if ($this->_allowModifications) {
  215. unset($this->_data[$name]);
  216. $this->_count = count($this->_data);
  217. $this->_skipNextIteration = true;
  218. } else {
  219. throw new Exception\InvalidArgumentException('Zend_Config is read only');
  220. }
  221. }
  222. /**
  223. * Defined by Countable interface
  224. *
  225. * @return int
  226. */
  227. public function count()
  228. {
  229. return $this->_count;
  230. }
  231. /**
  232. * Defined by Iterator interface
  233. *
  234. * @return mixed
  235. */
  236. public function current()
  237. {
  238. $this->_skipNextIteration = false;
  239. return current($this->_data);
  240. }
  241. /**
  242. * Defined by Iterator interface
  243. *
  244. * @return mixed
  245. */
  246. public function key()
  247. {
  248. return key($this->_data);
  249. }
  250. /**
  251. * Defined by Iterator interface
  252. *
  253. */
  254. public function next()
  255. {
  256. if ($this->_skipNextIteration) {
  257. $this->_skipNextIteration = false;
  258. return;
  259. }
  260. next($this->_data);
  261. $this->_index++;
  262. }
  263. /**
  264. * Defined by Iterator interface
  265. *
  266. */
  267. public function rewind()
  268. {
  269. $this->_skipNextIteration = false;
  270. reset($this->_data);
  271. $this->_index = 0;
  272. }
  273. /**
  274. * Defined by Iterator interface
  275. *
  276. * @return boolean
  277. */
  278. public function valid()
  279. {
  280. return $this->_index < $this->_count;
  281. }
  282. /**
  283. * offsetExists(): defined by ArrayAccess interface.
  284. *
  285. * @see ArrayAccess::offsetExists()
  286. * @param mixed $offset
  287. * @return boolean
  288. */
  289. public function offsetExists($offset)
  290. {
  291. return $this->__isset($offset);
  292. }
  293. /**
  294. * offsetGet(): defined by ArrayAccess interface.
  295. *
  296. * @see ArrayAccess::offsetGet()
  297. * @param mixed $offset
  298. * @return mixed
  299. */
  300. public function offsetGet($offset)
  301. {
  302. return $this->__get($offset);
  303. }
  304. /**
  305. * offsetSet(): defined by ArrayAccess interface.
  306. *
  307. * @see ArrayAccess::offsetSet()
  308. * @param mixed $offset
  309. * @param mixed $value
  310. * @return void
  311. */
  312. public function offsetSet($offset, $value)
  313. {
  314. $this->__set($offset, $value);
  315. }
  316. /**
  317. * offsetUnset(): defined by ArrayAccess interface.
  318. *
  319. * @see ArrayAccess::offsetUnset()
  320. * @param mixed $offset
  321. * @return void
  322. */
  323. public function offsetUnset($offset)
  324. {
  325. $this->__unset($offset);
  326. }
  327. /**
  328. * Returns the section name(s) loaded.
  329. *
  330. * @return mixed
  331. */
  332. public function getSectionName()
  333. {
  334. if(is_array($this->_loadedSection) && count($this->_loadedSection) == 1) {
  335. $this->_loadedSection = $this->_loadedSection[0];
  336. }
  337. return $this->_loadedSection;
  338. }
  339. /**
  340. * Returns true if all sections were loaded
  341. *
  342. * @return boolean
  343. */
  344. public function areAllSectionsLoaded()
  345. {
  346. return $this->_loadedSection === null;
  347. }
  348. /**
  349. * Merge another Zend_Config with this one. The items
  350. * in $merge will override the same named items in
  351. * the current config.
  352. *
  353. * @param \Zend\Config\Config $merge
  354. * @return \Zend\Config\Config
  355. */
  356. public function merge(Config $merge)
  357. {
  358. foreach($merge as $key => $item) {
  359. if(array_key_exists($key, $this->_data)) {
  360. if($item instanceof Config && $this->$key instanceof Config) {
  361. $this->$key = $this->$key->merge(new Config($item->toArray(), !$this->readOnly()));
  362. } else {
  363. $this->$key = $item;
  364. }
  365. } else {
  366. if($item instanceof Config) {
  367. $this->$key = new Config($item->toArray(), !$this->readOnly());
  368. } else {
  369. $this->$key = $item;
  370. }
  371. }
  372. }
  373. return $this;
  374. }
  375. /**
  376. * Prevent any more modifications being made to this instance. Useful
  377. * after merge() has been used to merge multiple Zend_Config objects
  378. * into one object which should then not be modified again.
  379. *
  380. */
  381. public function setReadOnly()
  382. {
  383. $this->_allowModifications = false;
  384. foreach ($this->_data as $key => $value) {
  385. if ($value instanceof Config) {
  386. $value->setReadOnly();
  387. }
  388. }
  389. }
  390. /**
  391. * Returns if this Zend_Config object is read only or not.
  392. *
  393. * @return boolean
  394. */
  395. public function readOnly()
  396. {
  397. return !$this->_allowModifications;
  398. }
  399. /**
  400. * Get the current extends
  401. *
  402. * @return array
  403. */
  404. public function getExtends()
  405. {
  406. return $this->_extends;
  407. }
  408. /**
  409. * Set an extend for Zend_Config_Writer
  410. *
  411. * @param string $extendingSection
  412. * @param string $extendedSection
  413. * @return void
  414. */
  415. public function setExtend($extendingSection, $extendedSection = null)
  416. {
  417. if ($extendedSection === null && isset($this->_extends[$extendingSection])) {
  418. unset($this->_extends[$extendingSection]);
  419. } else if ($extendedSection !== null) {
  420. $this->_extends[$extendingSection] = $extendedSection;
  421. }
  422. }
  423. /**
  424. * Throws an exception if $extendingSection may not extend $extendedSection,
  425. * and tracks the section extension if it is valid.
  426. *
  427. * @param string $extendingSection
  428. * @param string $extendedSection
  429. * @throws \Zend\Config\Exception
  430. * @return void
  431. */
  432. protected function _assertValidExtend($extendingSection, $extendedSection)
  433. {
  434. // detect circular section inheritance
  435. $extendedSectionCurrent = $extendedSection;
  436. while (array_key_exists($extendedSectionCurrent, $this->_extends)) {
  437. if ($this->_extends[$extendedSectionCurrent] == $extendingSection) {
  438. throw new Exception\RuntimeException('Illegal circular inheritance detected');
  439. }
  440. $extendedSectionCurrent = $this->_extends[$extendedSectionCurrent];
  441. }
  442. // remember that this section extends another section
  443. $this->_extends[$extendingSection] = $extendedSection;
  444. }
  445. /**
  446. * Merge two arrays recursively, overwriting keys of the same name
  447. * in $firstArray with the value in $secondArray.
  448. *
  449. * @param mixed $firstArray First array
  450. * @param mixed $secondArray Second array to merge into first array
  451. * @return array
  452. */
  453. protected function _arrayMergeRecursive($firstArray, $secondArray)
  454. {
  455. if (is_array($firstArray) && is_array($secondArray)) {
  456. return array_replace_recursive($firstArray, $secondArray);
  457. }
  458. return $secondArray;
  459. }
  460. /**
  461. * Set internal error handler
  462. *
  463. * @return void
  464. */
  465. protected function _setErrorHandler()
  466. {
  467. set_error_handler(array($this, '_handleError'));
  468. }
  469. /**
  470. * Restore internal error handler
  471. *
  472. * @return array Handled error messages
  473. */
  474. protected function _restoreErrorHandler()
  475. {
  476. restore_error_handler();
  477. $errorMessages = $this->_errorMessages;
  478. $this->_errorMessages = array();
  479. return $errorMessages;
  480. }
  481. /**
  482. * Handle internal errors
  483. *
  484. * @param integer $errno
  485. * @param string $errstr
  486. * @param string $errfile
  487. * @param integer $errline
  488. * @return void
  489. */
  490. protected function _handleError($errno, $errstr, $errfile, $errline)
  491. {
  492. $this->_errorMessages[] = trim($errstr);
  493. }
  494. }