File indexing completed on 2025-01-19 05:20:59
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Config 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * @see Zend_Config 0024 */ 0025 // require_once 'Zend/Config.php'; 0026 0027 /** 0028 * YAML Adapter for Zend_Config 0029 * 0030 * @category Zend 0031 * @package Zend_Config 0032 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0033 * @license http://framework.zend.com/license/new-bsd New BSD License 0034 */ 0035 class Zend_Config_Yaml extends Zend_Config 0036 { 0037 /** 0038 * Attribute name that indicates what section a config extends from 0039 */ 0040 const EXTENDS_NAME = "_extends"; 0041 0042 /** 0043 * Whether to skip extends or not 0044 * 0045 * @var boolean 0046 */ 0047 protected $_skipExtends = false; 0048 0049 /** 0050 * What to call when we need to decode some YAML? 0051 * 0052 * @var callable 0053 */ 0054 protected $_yamlDecoder = array(__CLASS__, 'decode'); 0055 0056 /** 0057 * Whether or not to ignore constants in parsed YAML 0058 * @var bool 0059 */ 0060 protected static $_ignoreConstants = false; 0061 0062 /** 0063 * Indicate whether parser should ignore constants or not 0064 * 0065 * @param bool $flag 0066 * @return void 0067 */ 0068 public static function setIgnoreConstants($flag) 0069 { 0070 self::$_ignoreConstants = (bool) $flag; 0071 } 0072 0073 /** 0074 * Whether parser should ignore constants or not 0075 * 0076 * @return bool 0077 */ 0078 public static function ignoreConstants() 0079 { 0080 return self::$_ignoreConstants; 0081 } 0082 0083 /** 0084 * Get callback for decoding YAML 0085 * 0086 * @return callable 0087 */ 0088 public function getYamlDecoder() 0089 { 0090 return $this->_yamlDecoder; 0091 } 0092 0093 /** 0094 * Set callback for decoding YAML 0095 * 0096 * @param callable $yamlDecoder the decoder to set 0097 * @return Zend_Config_Yaml 0098 */ 0099 public function setYamlDecoder($yamlDecoder) 0100 { 0101 if (!is_callable($yamlDecoder)) { 0102 // require_once 'Zend/Config/Exception.php'; 0103 throw new Zend_Config_Exception('Invalid parameter to setYamlDecoder() - must be callable'); 0104 } 0105 0106 $this->_yamlDecoder = $yamlDecoder; 0107 return $this; 0108 } 0109 0110 /** 0111 * Loads the section $section from the config file encoded as YAML 0112 * 0113 * Sections are defined as properties of the main object 0114 * 0115 * In order to extend another section, a section defines the "_extends" 0116 * property having a value of the section name from which the extending 0117 * section inherits values. 0118 * 0119 * Note that the keys in $section will override any keys of the same 0120 * name in the sections that have been included via "_extends". 0121 * 0122 * Options may include: 0123 * - allow_modifications: whether or not the config object is mutable 0124 * - skip_extends: whether or not to skip processing of parent configuration 0125 * - yaml_decoder: a callback to use to decode the Yaml source 0126 * 0127 * @param string $yaml YAML file to process 0128 * @param mixed $section Section to process 0129 * @param array|boolean $options 0130 */ 0131 public function __construct($yaml, $section = null, $options = false) 0132 { 0133 if (empty($yaml)) { 0134 // require_once 'Zend/Config/Exception.php'; 0135 throw new Zend_Config_Exception('Filename is not set'); 0136 } 0137 0138 $ignoreConstants = $staticIgnoreConstants = self::ignoreConstants(); 0139 $allowModifications = false; 0140 if (is_bool($options)) { 0141 $allowModifications = $options; 0142 } elseif (is_array($options)) { 0143 foreach ($options as $key => $value) { 0144 switch (strtolower($key)) { 0145 case 'allow_modifications': 0146 case 'allowmodifications': 0147 $allowModifications = (bool) $value; 0148 break; 0149 case 'skip_extends': 0150 case 'skipextends': 0151 $this->_skipExtends = (bool) $value; 0152 break; 0153 case 'ignore_constants': 0154 case 'ignoreconstants': 0155 $ignoreConstants = (bool) $value; 0156 break; 0157 case 'yaml_decoder': 0158 case 'yamldecoder': 0159 $this->setYamlDecoder($value); 0160 break; 0161 default: 0162 break; 0163 } 0164 } 0165 } 0166 0167 // Suppress warnings and errors while loading file 0168 set_error_handler(array($this, '_loadFileErrorHandler')); 0169 $yaml = file_get_contents($yaml); 0170 restore_error_handler(); 0171 0172 // Check if there was a error while loading file 0173 if ($this->_loadFileErrorStr !== null) { 0174 // require_once 'Zend/Config/Exception.php'; 0175 throw new Zend_Config_Exception($this->_loadFileErrorStr); 0176 } 0177 0178 // Override static value for ignore_constants if provided in $options 0179 self::setIgnoreConstants($ignoreConstants); 0180 0181 // Parse YAML 0182 $config = call_user_func($this->getYamlDecoder(), $yaml); 0183 0184 // Reset original static state of ignore_constants 0185 self::setIgnoreConstants($staticIgnoreConstants); 0186 0187 if (null === $config) { 0188 // decode failed 0189 // require_once 'Zend/Config/Exception.php'; 0190 throw new Zend_Config_Exception("Error parsing YAML data"); 0191 } 0192 0193 if (null === $section) { 0194 $dataArray = array(); 0195 foreach ($config as $sectionName => $sectionData) { 0196 $dataArray[$sectionName] = $this->_processExtends($config, $sectionName); 0197 } 0198 parent::__construct($dataArray, $allowModifications); 0199 } elseif (is_array($section)) { 0200 $dataArray = array(); 0201 foreach ($section as $sectionName) { 0202 if (!isset($config[$sectionName])) { 0203 // require_once 'Zend/Config/Exception.php'; 0204 throw new Zend_Config_Exception(sprintf( 0205 'Section "%s" cannot be found', 0206 implode(' ', (array)$section) 0207 )); 0208 } 0209 0210 $dataArray = array_merge($this->_processExtends($config, $sectionName), $dataArray); 0211 } 0212 parent::__construct($dataArray, $allowModifications); 0213 } else { 0214 if (!isset($config[$section])) { 0215 // require_once 'Zend/Config/Exception.php'; 0216 throw new Zend_Config_Exception(sprintf( 0217 'Section "%s" cannot be found', 0218 implode(' ', (array)$section) 0219 )); 0220 } 0221 0222 $dataArray = $this->_processExtends($config, $section); 0223 if (!is_array($dataArray)) { 0224 // Section in the yaml data contains just one top level string 0225 $dataArray = array($section => $dataArray); 0226 } 0227 parent::__construct($dataArray, $allowModifications); 0228 } 0229 0230 $this->_loadedSection = $section; 0231 } 0232 0233 /** 0234 * Helper function to process each element in the section and handle 0235 * the "_extends" inheritance attribute. 0236 * 0237 * @param array $data Data array to process 0238 * @param string $section Section to process 0239 * @param array $config Configuration which was parsed yet 0240 * @return array 0241 * @throws Zend_Config_Exception When $section cannot be found 0242 */ 0243 protected function _processExtends(array $data, $section, array $config = array()) 0244 { 0245 if (!isset($data[$section])) { 0246 // require_once 'Zend/Config/Exception.php'; 0247 throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section)); 0248 } 0249 0250 $thisSection = $data[$section]; 0251 0252 if (is_array($thisSection) && isset($thisSection[self::EXTENDS_NAME])) { 0253 $this->_assertValidExtend($section, $thisSection[self::EXTENDS_NAME]); 0254 0255 if (!$this->_skipExtends) { 0256 $config = $this->_processExtends($data, $thisSection[self::EXTENDS_NAME], $config); 0257 } 0258 unset($thisSection[self::EXTENDS_NAME]); 0259 } 0260 0261 $config = $this->_arrayMergeRecursive($config, $thisSection); 0262 0263 return $config; 0264 } 0265 0266 /** 0267 * Very dumb YAML parser 0268 * 0269 * Until we have Zend_Yaml... 0270 * 0271 * @param string $yaml YAML source 0272 * @return array Decoded data 0273 */ 0274 public static function decode($yaml) 0275 { 0276 $lines = explode("\n", $yaml); 0277 reset($lines); 0278 return self::_decodeYaml(0, $lines); 0279 } 0280 0281 /** 0282 * Service function to decode YAML 0283 * 0284 * @param int $currentIndent Current indent level 0285 * @param array $lines YAML lines 0286 * @return array|string 0287 */ 0288 protected static function _decodeYaml($currentIndent, &$lines) 0289 { 0290 $config = array(); 0291 $inIndent = false; 0292 while (list($n, $line) = each($lines)) { 0293 $lineno = $n + 1; 0294 0295 $line = rtrim(preg_replace("/#.*$/", "", $line)); 0296 if (strlen($line) == 0) { 0297 continue; 0298 } 0299 0300 $indent = strspn($line, " "); 0301 0302 // line without the spaces 0303 $line = trim($line); 0304 if (strlen($line) == 0) { 0305 continue; 0306 } 0307 0308 if ($indent < $currentIndent) { 0309 // this level is done 0310 prev($lines); 0311 return $config; 0312 } 0313 0314 if (!$inIndent) { 0315 $currentIndent = $indent; 0316 $inIndent = true; 0317 } 0318 0319 if (preg_match("/(?!-)([\w\-]+):\s*(.*)/", $line, $m)) { 0320 // key: value 0321 if (strlen($m[2])) { 0322 // simple key: value 0323 $value = preg_replace("/#.*$/", "", $m[2]); 0324 $value = self::_parseValue($value); 0325 } else { 0326 // key: and then values on new lines 0327 $value = self::_decodeYaml($currentIndent + 1, $lines); 0328 if (is_array($value) && !count($value)) { 0329 $value = ""; 0330 } 0331 } 0332 $config[$m[1]] = $value; 0333 } elseif ($line[0] == "-") { 0334 // item in the list: 0335 // - FOO 0336 if (strlen($line) > 2) { 0337 $value = substr($line, 2); 0338 0339 $config[] = self::_parseValue($value); 0340 } else { 0341 $config[] = self::_decodeYaml($currentIndent + 1, $lines); 0342 } 0343 } else { 0344 // require_once 'Zend/Config/Exception.php'; 0345 throw new Zend_Config_Exception(sprintf( 0346 'Error parsing YAML at line %d - unsupported syntax: "%s"', 0347 $lineno, $line 0348 )); 0349 } 0350 } 0351 return $config; 0352 } 0353 0354 /** 0355 * Parse values 0356 * 0357 * @param string $value 0358 * @return string 0359 */ 0360 protected static function _parseValue($value) 0361 { 0362 $value = trim($value); 0363 0364 // remove quotes from string. 0365 if ('"' == $value['0']) { 0366 if ('"' == $value[count($value) -1]) { 0367 $value = substr($value, 1, -1); 0368 } 0369 } elseif ('\'' == $value['0'] && '\'' == $value[count($value) -1]) { 0370 $value = strtr($value, array("''" => "'", "'" => '')); 0371 } 0372 0373 // Check for booleans and constants 0374 if (preg_match('/^(t(rue)?|on|y(es)?)$/i', $value)) { 0375 $value = true; 0376 } elseif (preg_match('/^(f(alse)?|off|n(o)?)$/i', $value)) { 0377 $value = false; 0378 } elseif (strcasecmp($value, 'null') === 0) { 0379 $value = null; 0380 } elseif (!self::$_ignoreConstants) { 0381 // test for constants 0382 $value = self::_replaceConstants($value); 0383 } 0384 0385 return $value; 0386 } 0387 0388 /** 0389 * Replace any constants referenced in a string with their values 0390 * 0391 * @param string $value 0392 * @return string 0393 */ 0394 protected static function _replaceConstants($value) 0395 { 0396 foreach (self::_getConstants() as $constant) { 0397 if (strstr($value, $constant)) { 0398 $value = str_replace($constant, constant($constant), $value); 0399 } 0400 } 0401 return $value; 0402 } 0403 0404 /** 0405 * Get (reverse) sorted list of defined constant names 0406 * 0407 * @return array 0408 */ 0409 protected static function _getConstants() 0410 { 0411 $constants = array_keys(get_defined_constants()); 0412 rsort($constants, SORT_STRING); 0413 return $constants; 0414 } 0415 }