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