PageRenderTime 29ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Config/Yaml.php

https://gitlab.com/devtoannh/cafe
PHP | 381 lines | 201 code | 38 blank | 142 comment | 33 complexity | 5788c22262ed1bbbd3eabc695b9b21d5 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-2011 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: Yaml.php 24092 2011-05-31 02:43:28Z adamlundrigan $
  20. */
  21. /**
  22. * @see Zend_Config
  23. */
  24. require_once 'Zend/Config.php';
  25. /**
  26. * YAML Adapter for Zend_Config
  27. *
  28. * @category Zend
  29. * @package Zend_Config
  30. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  31. * @license http://framework.zend.com/license/new-bsd New BSD License
  32. */
  33. class Zend_Config_Yaml extends Zend_Config
  34. {
  35. /**
  36. * Attribute name that indicates what section a config extends from
  37. */
  38. const EXTENDS_NAME = "_extends";
  39. /**
  40. * Whether to skip extends or not
  41. *
  42. * @var boolean
  43. */
  44. protected $_skipExtends = false;
  45. /**
  46. * What to call when we need to decode some YAML?
  47. *
  48. * @var callable
  49. */
  50. protected $_yamlDecoder = array(__CLASS__, 'decode');
  51. /**
  52. * Whether or not to ignore constants in parsed YAML
  53. * @var bool
  54. */
  55. protected static $_ignoreConstants = false;
  56. /**
  57. * Indicate whether parser should ignore constants or not
  58. *
  59. * @param bool $flag
  60. * @return void
  61. */
  62. public static function setIgnoreConstants($flag)
  63. {
  64. self::$_ignoreConstants = (bool) $flag;
  65. }
  66. /**
  67. * Whether parser should ignore constants or not
  68. *
  69. * @return bool
  70. */
  71. public static function ignoreConstants()
  72. {
  73. return self::$_ignoreConstants;
  74. }
  75. /**
  76. * Get callback for decoding YAML
  77. *
  78. * @return callable
  79. */
  80. public function getYamlDecoder()
  81. {
  82. return $this->_yamlDecoder;
  83. }
  84. /**
  85. * Set callback for decoding YAML
  86. *
  87. * @param callable $yamlDecoder the decoder to set
  88. * @return Zend_Config_Yaml
  89. */
  90. public function setYamlDecoder($yamlDecoder)
  91. {
  92. if (!is_callable($yamlDecoder)) {
  93. require_once 'Zend/Config/Exception.php';
  94. throw new Zend_Config_Exception('Invalid parameter to setYamlDecoder() - must be callable');
  95. }
  96. $this->_yamlDecoder = $yamlDecoder;
  97. return $this;
  98. }
  99. /**
  100. * Loads the section $section from the config file encoded as YAML
  101. *
  102. * Sections are defined as properties of the main object
  103. *
  104. * In order to extend another section, a section defines the "_extends"
  105. * property having a value of the section name from which the extending
  106. * section inherits values.
  107. *
  108. * Note that the keys in $section will override any keys of the same
  109. * name in the sections that have been included via "_extends".
  110. *
  111. * Options may include:
  112. * - allow_modifications: whether or not the config object is mutable
  113. * - skip_extends: whether or not to skip processing of parent configuration
  114. * - yaml_decoder: a callback to use to decode the Yaml source
  115. *
  116. * @param string $yaml YAML file to process
  117. * @param mixed $section Section to process
  118. * @param array|boolean $options
  119. */
  120. public function __construct($yaml, $section = null, $options = false)
  121. {
  122. if (empty($yaml)) {
  123. require_once 'Zend/Config/Exception.php';
  124. throw new Zend_Config_Exception('Filename is not set');
  125. }
  126. $ignoreConstants = $staticIgnoreConstants = self::ignoreConstants();
  127. $allowModifications = false;
  128. if (is_bool($options)) {
  129. $allowModifications = $options;
  130. } elseif (is_array($options)) {
  131. foreach ($options as $key => $value) {
  132. switch (strtolower($key)) {
  133. case 'allow_modifications':
  134. case 'allowmodifications':
  135. $allowModifications = (bool) $value;
  136. break;
  137. case 'skip_extends':
  138. case 'skipextends':
  139. $this->_skipExtends = (bool) $value;
  140. break;
  141. case 'ignore_constants':
  142. case 'ignoreconstants':
  143. $ignoreConstants = (bool) $value;
  144. break;
  145. case 'yaml_decoder':
  146. case 'yamldecoder':
  147. $this->setYamlDecoder($value);
  148. break;
  149. default:
  150. break;
  151. }
  152. }
  153. }
  154. // Suppress warnings and errors while loading file
  155. set_error_handler(array($this, '_loadFileErrorHandler'));
  156. $yaml = file_get_contents($yaml);
  157. restore_error_handler();
  158. // Check if there was a error while loading file
  159. if ($this->_loadFileErrorStr !== null) {
  160. require_once 'Zend/Config/Exception.php';
  161. throw new Zend_Config_Exception($this->_loadFileErrorStr);
  162. }
  163. // Override static value for ignore_constants if provided in $options
  164. self::setIgnoreConstants($ignoreConstants);
  165. // Parse YAML
  166. $config = call_user_func($this->getYamlDecoder(), $yaml);
  167. // Reset original static state of ignore_constants
  168. self::setIgnoreConstants($staticIgnoreConstants);
  169. if (null === $config) {
  170. // decode failed
  171. require_once 'Zend/Config/Exception.php';
  172. throw new Zend_Config_Exception("Error parsing YAML data");
  173. }
  174. if (null === $section) {
  175. $dataArray = array();
  176. foreach ($config as $sectionName => $sectionData) {
  177. $dataArray[$sectionName] = $this->_processExtends($config, $sectionName);
  178. }
  179. parent::__construct($dataArray, $allowModifications);
  180. } elseif (is_array($section)) {
  181. $dataArray = array();
  182. foreach ($section as $sectionName) {
  183. if (!isset($config[$sectionName])) {
  184. require_once 'Zend/Config/Exception.php';
  185. throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
  186. }
  187. $dataArray = array_merge($this->_processExtends($config, $sectionName), $dataArray);
  188. }
  189. parent::__construct($dataArray, $allowModifications);
  190. } else {
  191. if (!isset($config[$section])) {
  192. require_once 'Zend/Config/Exception.php';
  193. throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
  194. }
  195. $dataArray = $this->_processExtends($config, $section);
  196. if (!is_array($dataArray)) {
  197. // Section in the yaml data contains just one top level string
  198. $dataArray = array($section => $dataArray);
  199. }
  200. parent::__construct($dataArray, $allowModifications);
  201. }
  202. $this->_loadedSection = $section;
  203. }
  204. /**
  205. * Helper function to process each element in the section and handle
  206. * the "_extends" inheritance attribute.
  207. *
  208. * @param array $data Data array to process
  209. * @param string $section Section to process
  210. * @param array $config Configuration which was parsed yet
  211. * @return array
  212. * @throws Zend_Config_Exception When $section cannot be found
  213. */
  214. protected function _processExtends(array $data, $section, array $config = array())
  215. {
  216. if (!isset($data[$section])) {
  217. require_once 'Zend/Config/Exception.php';
  218. throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
  219. }
  220. $thisSection = $data[$section];
  221. if (is_array($thisSection) && isset($thisSection[self::EXTENDS_NAME])) {
  222. $this->_assertValidExtend($section, $thisSection[self::EXTENDS_NAME]);
  223. if (!$this->_skipExtends) {
  224. $config = $this->_processExtends($data, $thisSection[self::EXTENDS_NAME], $config);
  225. }
  226. unset($thisSection[self::EXTENDS_NAME]);
  227. }
  228. $config = $this->_arrayMergeRecursive($config, $thisSection);
  229. return $config;
  230. }
  231. /**
  232. * Very dumb YAML parser
  233. *
  234. * Until we have Zend_Yaml...
  235. *
  236. * @param string $yaml YAML source
  237. * @return array Decoded data
  238. */
  239. public static function decode($yaml)
  240. {
  241. $lines = explode("\n", $yaml);
  242. reset($lines);
  243. return self::_decodeYaml(0, $lines);
  244. }
  245. /**
  246. * Service function to decode YAML
  247. *
  248. * @param int $currentIndent Current indent level
  249. * @param array $lines YAML lines
  250. * @return array|string
  251. */
  252. protected static function _decodeYaml($currentIndent, &$lines)
  253. {
  254. $config = array();
  255. $inIndent = false;
  256. while (list($n, $line) = each($lines)) {
  257. $lineno = $n + 1;
  258. $line = rtrim(preg_replace("/#.*$/", "", $line));
  259. if (strlen($line) == 0) {
  260. continue;
  261. }
  262. $indent = strspn($line, " ");
  263. // line without the spaces
  264. $line = trim($line);
  265. if (strlen($line) == 0) {
  266. continue;
  267. }
  268. if ($indent < $currentIndent) {
  269. // this level is done
  270. prev($lines);
  271. return $config;
  272. }
  273. if (!$inIndent) {
  274. $currentIndent = $indent;
  275. $inIndent = true;
  276. }
  277. if (preg_match("/(\w+):\s*(.*)/", $line, $m)) {
  278. // key: value
  279. if (strlen($m[2])) {
  280. // simple key: value
  281. $value = rtrim(preg_replace("/#.*$/", "", $m[2]));
  282. // Check for booleans and constants
  283. if (preg_match('/^(t(rue)?|on|y(es)?)$/i', $value)) {
  284. $value = true;
  285. } elseif (preg_match('/^(f(alse)?|off|n(o)?)$/i', $value)) {
  286. $value = false;
  287. } elseif (!self::$_ignoreConstants) {
  288. // test for constants
  289. $value = self::_replaceConstants($value);
  290. }
  291. } else {
  292. // key: and then values on new lines
  293. $value = self::_decodeYaml($currentIndent + 1, $lines);
  294. if (is_array($value) && !count($value)) {
  295. $value = "";
  296. }
  297. }
  298. $config[$m[1]] = $value;
  299. } elseif ($line[0] == "-") {
  300. // item in the list:
  301. // - FOO
  302. if (strlen($line) > 2) {
  303. $config[] = substr($line, 2);
  304. } else {
  305. $config[] = self::_decodeYaml($currentIndent + 1, $lines);
  306. }
  307. } else {
  308. require_once 'Zend/Config/Exception.php';
  309. throw new Zend_Config_Exception(sprintf(
  310. 'Error parsing YAML at line %d - unsupported syntax: "%s"',
  311. $lineno, $line
  312. ));
  313. }
  314. }
  315. return $config;
  316. }
  317. /**
  318. * Replace any constants referenced in a string with their values
  319. *
  320. * @param string $value
  321. * @return string
  322. */
  323. protected static function _replaceConstants($value)
  324. {
  325. foreach (self::_getConstants() as $constant) {
  326. if (strstr($value, $constant)) {
  327. $value = str_replace($constant, constant($constant), $value);
  328. }
  329. }
  330. return $value;
  331. }
  332. /**
  333. * Get (reverse) sorted list of defined constant names
  334. *
  335. * @return array
  336. */
  337. protected static function _getConstants()
  338. {
  339. $constants = array_keys(get_defined_constants());
  340. rsort($constants, SORT_STRING);
  341. return $constants;
  342. }
  343. }