Spyc.php 31KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  1. <?php
  2. namespace Muzich\CoreBundle\ElementFactory\lib;
  3. /**
  4. * Spyc -- A Simple PHP YAML Class
  5. * @version 0.5
  6. * @author Vlad Andersen <vlad.andersen@gmail.com>
  7. * @author Chris Wanstrath <chris@ozmm.org>
  8. * @link http://code.google.com/p/spyc/
  9. * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen
  10. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  11. * @package Spyc
  12. */
  13. if (!function_exists('spyc_load')) {
  14. /**
  15. * Parses YAML to array.
  16. * @param string $string YAML string.
  17. * @return array
  18. */
  19. function spyc_load ($string) {
  20. return Spyc::YAMLLoadString($string);
  21. }
  22. }
  23. if (!function_exists('spyc_load_file')) {
  24. /**
  25. * Parses YAML to array.
  26. * @param string $file Path to YAML file.
  27. * @return array
  28. */
  29. function spyc_load_file ($file) {
  30. return Spyc::YAMLLoad($file);
  31. }
  32. }
  33. /**
  34. * The Simple PHP YAML Class.
  35. *
  36. * This class can be used to read a YAML file and convert its contents
  37. * into a PHP array. It currently supports a very limited subsection of
  38. * the YAML spec.
  39. *
  40. * Usage:
  41. * <code>
  42. * $Spyc = new Spyc;
  43. * $array = $Spyc->load($file);
  44. * </code>
  45. * or:
  46. * <code>
  47. * $array = Spyc::YAMLLoad($file);
  48. * </code>
  49. * or:
  50. * <code>
  51. * $array = spyc_load_file($file);
  52. * </code>
  53. * @package Spyc
  54. */
  55. class Spyc {
  56. // SETTINGS
  57. const REMPTY = "\0\0\0\0\0";
  58. /**
  59. * Setting this to true will force YAMLDump to enclose any string value in
  60. * quotes. False by default.
  61. *
  62. * @var bool
  63. */
  64. public $setting_dump_force_quotes = false;
  65. /**
  66. * Setting this to true will forse YAMLLoad to use syck_load function when
  67. * possible. False by default.
  68. * @var bool
  69. */
  70. public $setting_use_syck_is_possible = false;
  71. /**#@+
  72. * @access private
  73. * @var mixed
  74. */
  75. private $_dumpIndent;
  76. private $_dumpWordWrap;
  77. private $_containsGroupAnchor = false;
  78. private $_containsGroupAlias = false;
  79. private $path;
  80. private $result;
  81. private $LiteralPlaceHolder = '___YAML_Literal_Block___';
  82. private $SavedGroups = array();
  83. private $indent;
  84. /**
  85. * Path modifier that should be applied after adding current element.
  86. * @var array
  87. */
  88. private $delayedPath = array();
  89. /**#@+
  90. * @access public
  91. * @var mixed
  92. */
  93. public $_nodeId;
  94. /**
  95. * Load a valid YAML string to Spyc.
  96. * @param string $input
  97. * @return array
  98. */
  99. public function load ($input) {
  100. return $this->__loadString($input);
  101. }
  102. /**
  103. * Load a valid YAML file to Spyc.
  104. * @param string $file
  105. * @return array
  106. */
  107. public function loadFile ($file) {
  108. return $this->__load($file);
  109. }
  110. /**
  111. * Load YAML into a PHP array statically
  112. *
  113. * The load method, when supplied with a YAML stream (string or file),
  114. * will do its best to convert YAML in a file into a PHP array. Pretty
  115. * simple.
  116. * Usage:
  117. * <code>
  118. * $array = Spyc::YAMLLoad('lucky.yaml');
  119. * //print_r($array);
  120. * </code>
  121. * @access public
  122. * @return array
  123. * @param string $input Path of YAML file or string containing YAML
  124. */
  125. public static function YAMLLoad($input) {
  126. $Spyc = new Spyc;
  127. return $Spyc->__load($input);
  128. }
  129. /**
  130. * Load a string of YAML into a PHP array statically
  131. *
  132. * The load method, when supplied with a YAML string, will do its best
  133. * to convert YAML in a string into a PHP array. Pretty simple.
  134. *
  135. * Note: use this function if you don't want files from the file system
  136. * loaded and processed as YAML. This is of interest to people concerned
  137. * about security whose input is from a string.
  138. *
  139. * Usage:
  140. * <code>
  141. * $array = Spyc::YAMLLoadString("---\n0: hello world\n");
  142. * //print_r($array);
  143. * </code>
  144. * @access public
  145. * @return array
  146. * @param string $input String containing YAML
  147. */
  148. public static function YAMLLoadString($input) {
  149. $Spyc = new Spyc;
  150. return $Spyc->__loadString($input);
  151. }
  152. /**
  153. * Dump YAML from PHP array statically
  154. *
  155. * The dump method, when supplied with an array, will do its best
  156. * to convert the array into friendly YAML. Pretty simple. Feel free to
  157. * save the returned string as nothing.yaml and pass it around.
  158. *
  159. * Oh, and you can decide how big the indent is and what the wordwrap
  160. * for folding is. Pretty cool -- just pass in 'false' for either if
  161. * you want to use the default.
  162. *
  163. * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
  164. * you can turn off wordwrap by passing in 0.
  165. *
  166. * @access public
  167. * @return string
  168. * @param array $array PHP array
  169. * @param int $indent Pass in false to use the default, which is 2
  170. * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
  171. */
  172. public static function YAMLDump($array,$indent = false,$wordwrap = false) {
  173. $spyc = new Spyc;
  174. return $spyc->dump($array,$indent,$wordwrap);
  175. }
  176. /**
  177. * Dump PHP array to YAML
  178. *
  179. * The dump method, when supplied with an array, will do its best
  180. * to convert the array into friendly YAML. Pretty simple. Feel free to
  181. * save the returned string as tasteful.yaml and pass it around.
  182. *
  183. * Oh, and you can decide how big the indent is and what the wordwrap
  184. * for folding is. Pretty cool -- just pass in 'false' for either if
  185. * you want to use the default.
  186. *
  187. * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
  188. * you can turn off wordwrap by passing in 0.
  189. *
  190. * @access public
  191. * @return string
  192. * @param array $array PHP array
  193. * @param int $indent Pass in false to use the default, which is 2
  194. * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
  195. */
  196. public function dump($array,$indent = false,$wordwrap = false) {
  197. // Dumps to some very clean YAML. We'll have to add some more features
  198. // and options soon. And better support for folding.
  199. // New features and options.
  200. if ($indent === false or !is_numeric($indent)) {
  201. $this->_dumpIndent = 2;
  202. } else {
  203. $this->_dumpIndent = $indent;
  204. }
  205. if ($wordwrap === false or !is_numeric($wordwrap)) {
  206. $this->_dumpWordWrap = 40;
  207. } else {
  208. $this->_dumpWordWrap = $wordwrap;
  209. }
  210. // New YAML document
  211. $string = "---\n";
  212. // Start at the base of the array and move through it.
  213. if ($array) {
  214. $array = (array)$array;
  215. $previous_key = -1;
  216. foreach ($array as $key => $value) {
  217. if (!isset($first_key)) $first_key = $key;
  218. $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array);
  219. $previous_key = $key;
  220. }
  221. }
  222. return $string;
  223. }
  224. /**
  225. * Attempts to convert a key / value array item to YAML
  226. * @access private
  227. * @return string
  228. * @param $key The name of the key
  229. * @param $value The value of the item
  230. * @param $indent The indent of the current node
  231. */
  232. private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) {
  233. if (is_array($value)) {
  234. if (empty ($value))
  235. return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array);
  236. // It has children. What to do?
  237. // Make it the right kind of item
  238. $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array);
  239. // Add the indent
  240. $indent += $this->_dumpIndent;
  241. // Yamlize the array
  242. $string .= $this->_yamlizeArray($value,$indent);
  243. } elseif (!is_array($value)) {
  244. // It doesn't have children. Yip.
  245. $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array);
  246. }
  247. return $string;
  248. }
  249. /**
  250. * Attempts to convert an array to YAML
  251. * @access private
  252. * @return string
  253. * @param $array The array you want to convert
  254. * @param $indent The indent of the current level
  255. */
  256. private function _yamlizeArray($array,$indent) {
  257. if (is_array($array)) {
  258. $string = '';
  259. $previous_key = -1;
  260. foreach ($array as $key => $value) {
  261. if (!isset($first_key)) $first_key = $key;
  262. $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array);
  263. $previous_key = $key;
  264. }
  265. return $string;
  266. } else {
  267. return false;
  268. }
  269. }
  270. /**
  271. * Returns YAML from a key and a value
  272. * @access private
  273. * @return string
  274. * @param $key The name of the key
  275. * @param $value The value of the item
  276. * @param $indent The indent of the current node
  277. */
  278. private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) {
  279. // do some folding here, for blocks
  280. if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
  281. strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, ' ') !== false ||
  282. strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 ||
  283. substr ($value, -1, 1) == ':')
  284. ) {
  285. $value = $this->_doLiteralBlock($value,$indent);
  286. } else {
  287. $value = $this->_doFolding($value,$indent);
  288. }
  289. if ($value === array()) $value = '[ ]';
  290. if (in_array ($value, array ('true', 'TRUE', 'false', 'FALSE', 'y', 'Y', 'n', 'N', 'null', 'NULL'), true)) {
  291. $value = $this->_doLiteralBlock($value,$indent);
  292. }
  293. if (trim ($value) != $value)
  294. $value = $this->_doLiteralBlock($value,$indent);
  295. if (is_bool($value)) {
  296. $value = ($value) ? "true" : "false";
  297. }
  298. if ($value === null) $value = 'null';
  299. if ($value === "'" . self::REMPTY . "'") $value = null;
  300. $spaces = str_repeat(' ',$indent);
  301. //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
  302. if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) {
  303. // It's a sequence
  304. $string = $spaces.'- '.$value."\n";
  305. } else {
  306. // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"');
  307. // It's mapped
  308. if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; }
  309. $string = rtrim ($spaces.$key.': '.$value)."\n";
  310. }
  311. return $string;
  312. }
  313. /**
  314. * Creates a literal block for dumping
  315. * @access private
  316. * @return string
  317. * @param $value
  318. * @param $indent int The value of the indent
  319. */
  320. private function _doLiteralBlock($value,$indent) {
  321. if ($value === "\n") return '\n';
  322. if (strpos($value, "\n") === false && strpos($value, "'") === false) {
  323. return sprintf ("'%s'", $value);
  324. }
  325. if (strpos($value, "\n") === false && strpos($value, '"') === false) {
  326. return sprintf ('"%s"', $value);
  327. }
  328. $exploded = explode("\n",$value);
  329. $newValue = '|';
  330. $indent += $this->_dumpIndent;
  331. $spaces = str_repeat(' ',$indent);
  332. foreach ($exploded as $line) {
  333. $newValue .= "\n" . $spaces . ($line);
  334. }
  335. return $newValue;
  336. }
  337. /**
  338. * Folds a string of text, if necessary
  339. * @access private
  340. * @return string
  341. * @param $value The string you wish to fold
  342. */
  343. private function _doFolding($value,$indent) {
  344. // Don't do anything if wordwrap is set to 0
  345. if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
  346. $indent += $this->_dumpIndent;
  347. $indent = str_repeat(' ',$indent);
  348. $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
  349. $value = ">\n".$indent.$wrapped;
  350. } else {
  351. if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY)
  352. $value = '"' . $value . '"';
  353. }
  354. return $value;
  355. }
  356. // LOADING FUNCTIONS
  357. private function __load($input) {
  358. $Source = $this->loadFromSource($input);
  359. return $this->loadWithSource($Source);
  360. }
  361. private function __loadString($input) {
  362. $Source = $this->loadFromString($input);
  363. return $this->loadWithSource($Source);
  364. }
  365. private function loadWithSource($Source) {
  366. if (empty ($Source)) return array();
  367. if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
  368. $array = syck_load (implode ('', $Source));
  369. return is_array($array) ? $array : array();
  370. }
  371. $this->path = array();
  372. $this->result = array();
  373. $cnt = count($Source);
  374. for ($i = 0; $i < $cnt; $i++) {
  375. $line = $Source[$i];
  376. $this->indent = strlen($line) - strlen(ltrim($line));
  377. $tempPath = $this->getParentPathByIndent($this->indent);
  378. $line = self::stripIndent($line, $this->indent);
  379. if (self::isComment($line)) continue;
  380. if (self::isEmpty($line)) continue;
  381. $this->path = $tempPath;
  382. $literalBlockStyle = self::startsLiteralBlock($line);
  383. if ($literalBlockStyle) {
  384. $line = rtrim ($line, $literalBlockStyle . " \n");
  385. $literalBlock = '';
  386. $line .= $this->LiteralPlaceHolder;
  387. $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1]));
  388. while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
  389. $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
  390. }
  391. $i--;
  392. }
  393. while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
  394. $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
  395. }
  396. $i--;
  397. if (strpos ($line, '#')) {
  398. if (strpos ($line, '"') === false && strpos ($line, "'") === false)
  399. $line = preg_replace('/\s+#(.+)$/','',$line);
  400. }
  401. $lineArray = $this->_parseLine($line);
  402. if ($literalBlockStyle)
  403. $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
  404. $this->addArray($lineArray, $this->indent);
  405. foreach ($this->delayedPath as $indent => $delayedPath)
  406. $this->path[$indent] = $delayedPath;
  407. $this->delayedPath = array();
  408. }
  409. return $this->result;
  410. }
  411. private function loadFromSource ($input) {
  412. if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
  413. return file($input);
  414. return $this->loadFromString($input);
  415. }
  416. private function loadFromString ($input) {
  417. $lines = explode("\n",$input);
  418. foreach ($lines as $k => $_) {
  419. $lines[$k] = rtrim ($_, "\r");
  420. }
  421. return $lines;
  422. }
  423. /**
  424. * Parses YAML code and returns an array for a node
  425. * @access private
  426. * @return array
  427. * @param string $line A line from the YAML file
  428. */
  429. private function _parseLine($line) {
  430. if (!$line) return array();
  431. $line = trim($line);
  432. if (!$line) return array();
  433. $array = array();
  434. $group = $this->nodeContainsGroup($line);
  435. if ($group) {
  436. $this->addGroup($line, $group);
  437. $line = $this->stripGroup ($line, $group);
  438. }
  439. if ($this->startsMappedSequence($line))
  440. return $this->returnMappedSequence($line);
  441. if ($this->startsMappedValue($line))
  442. return $this->returnMappedValue($line);
  443. if ($this->isArrayElement($line))
  444. return $this->returnArrayElement($line);
  445. if ($this->isPlainArray($line))
  446. return $this->returnPlainArray($line);
  447. return $this->returnKeyValuePair($line);
  448. }
  449. /**
  450. * Finds the type of the passed value, returns the value as the new type.
  451. * @access private
  452. * @param string $value
  453. * @return mixed
  454. */
  455. private function _toType($value) {
  456. if ($value === '') return null;
  457. $first_character = $value[0];
  458. $last_character = substr($value, -1, 1);
  459. $is_quoted = false;
  460. do {
  461. if (!$value) break;
  462. if ($first_character != '"' && $first_character != "'") break;
  463. if ($last_character != '"' && $last_character != "'") break;
  464. $is_quoted = true;
  465. } while (0);
  466. if ($is_quoted)
  467. return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\''));
  468. if (strpos($value, ' #') !== false && !$is_quoted)
  469. $value = preg_replace('/\s+#(.+)$/','',$value);
  470. if (!$is_quoted) $value = str_replace('\n', "\n", $value);
  471. if ($first_character == '[' && $last_character == ']') {
  472. // Take out strings sequences and mappings
  473. $innerValue = trim(substr ($value, 1, -1));
  474. if ($innerValue === '') return array();
  475. $explode = $this->_inlineEscape($innerValue);
  476. // Propagate value array
  477. $value = array();
  478. foreach ($explode as $v) {
  479. $value[] = $this->_toType($v);
  480. }
  481. return $value;
  482. }
  483. if (strpos($value,': ')!==false && $first_character != '{') {
  484. $array = explode(': ',$value);
  485. $key = trim($array[0]);
  486. array_shift($array);
  487. $value = trim(implode(': ',$array));
  488. $value = $this->_toType($value);
  489. return array($key => $value);
  490. }
  491. if ($first_character == '{' && $last_character == '}') {
  492. $innerValue = trim(substr ($value, 1, -1));
  493. if ($innerValue === '') return array();
  494. // Inline Mapping
  495. // Take out strings sequences and mappings
  496. $explode = $this->_inlineEscape($innerValue);
  497. // Propagate value array
  498. $array = array();
  499. foreach ($explode as $v) {
  500. $SubArr = $this->_toType($v);
  501. if (empty($SubArr)) continue;
  502. if (is_array ($SubArr)) {
  503. $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
  504. }
  505. $array[] = $SubArr;
  506. }
  507. return $array;
  508. }
  509. if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
  510. return null;
  511. }
  512. if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){
  513. $intvalue = (int)$value;
  514. if ($intvalue != PHP_INT_MAX)
  515. $value = $intvalue;
  516. return $value;
  517. }
  518. if (in_array($value,
  519. array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) {
  520. return true;
  521. }
  522. if (in_array(strtolower($value),
  523. array('false', 'off', '-', 'no', 'n'))) {
  524. return false;
  525. }
  526. if (is_numeric($value)) {
  527. if ($value === '0') return 0;
  528. if (rtrim ($value, 0) === $value)
  529. $value = (float)$value;
  530. return $value;
  531. }
  532. return $value;
  533. }
  534. /**
  535. * Used in inlines to check for more inlines or quoted strings
  536. * @access private
  537. * @return array
  538. */
  539. private function _inlineEscape($inline) {
  540. // There's gotta be a cleaner way to do this...
  541. // While pure sequences seem to be nesting just fine,
  542. // pure mappings and mappings with sequences inside can't go very
  543. // deep. This needs to be fixed.
  544. $seqs = array();
  545. $maps = array();
  546. $saved_strings = array();
  547. // Check for strings
  548. $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
  549. if (preg_match_all($regex,$inline,$strings)) {
  550. $saved_strings = $strings[0];
  551. $inline = preg_replace($regex,'YAMLString',$inline);
  552. }
  553. unset($regex);
  554. $i = 0;
  555. do {
  556. // Check for sequences
  557. while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
  558. $seqs[] = $matchseqs[0];
  559. $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
  560. }
  561. // Check for mappings
  562. while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
  563. $maps[] = $matchmaps[0];
  564. $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
  565. }
  566. if ($i++ >= 10) break;
  567. } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
  568. $explode = explode(', ',$inline);
  569. $stringi = 0; $i = 0;
  570. while (1) {
  571. // Re-add the sequences
  572. if (!empty($seqs)) {
  573. foreach ($explode as $key => $value) {
  574. if (strpos($value,'YAMLSeq') !== false) {
  575. foreach ($seqs as $seqk => $seq) {
  576. $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
  577. $value = $explode[$key];
  578. }
  579. }
  580. }
  581. }
  582. // Re-add the mappings
  583. if (!empty($maps)) {
  584. foreach ($explode as $key => $value) {
  585. if (strpos($value,'YAMLMap') !== false) {
  586. foreach ($maps as $mapk => $map) {
  587. $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
  588. $value = $explode[$key];
  589. }
  590. }
  591. }
  592. }
  593. // Re-add the strings
  594. if (!empty($saved_strings)) {
  595. foreach ($explode as $key => $value) {
  596. while (strpos($value,'YAMLString') !== false) {
  597. $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
  598. unset($saved_strings[$stringi]);
  599. ++$stringi;
  600. $value = $explode[$key];
  601. }
  602. }
  603. }
  604. $finished = true;
  605. foreach ($explode as $key => $value) {
  606. if (strpos($value,'YAMLSeq') !== false) {
  607. $finished = false; break;
  608. }
  609. if (strpos($value,'YAMLMap') !== false) {
  610. $finished = false; break;
  611. }
  612. if (strpos($value,'YAMLString') !== false) {
  613. $finished = false; break;
  614. }
  615. }
  616. if ($finished) break;
  617. $i++;
  618. if ($i > 10)
  619. break; // Prevent infinite loops.
  620. }
  621. return $explode;
  622. }
  623. private function literalBlockContinues ($line, $lineIndent) {
  624. if (!trim($line)) return true;
  625. if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
  626. return false;
  627. }
  628. private function referenceContentsByAlias ($alias) {
  629. do {
  630. if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
  631. $groupPath = $this->SavedGroups[$alias];
  632. $value = $this->result;
  633. foreach ($groupPath as $k) {
  634. $value = $value[$k];
  635. }
  636. } while (false);
  637. return $value;
  638. }
  639. private function addArrayInline ($array, $indent) {
  640. $CommonGroupPath = $this->path;
  641. if (empty ($array)) return false;
  642. foreach ($array as $k => $_) {
  643. $this->addArray(array($k => $_), $indent);
  644. $this->path = $CommonGroupPath;
  645. }
  646. return true;
  647. }
  648. private function addArray ($incoming_data, $incoming_indent) {
  649. // print_r ($incoming_data);
  650. if (count ($incoming_data) > 1)
  651. return $this->addArrayInline ($incoming_data, $incoming_indent);
  652. $key = key ($incoming_data);
  653. $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
  654. if ($key === '__!YAMLZero') $key = '0';
  655. if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
  656. if ($key || $key === '' || $key === '0') {
  657. $this->result[$key] = $value;
  658. } else {
  659. $this->result[] = $value; end ($this->result); $key = key ($this->result);
  660. }
  661. $this->path[$incoming_indent] = $key;
  662. return;
  663. }
  664. $history = array();
  665. // Unfolding inner array tree.
  666. $history[] = $_arr = $this->result;
  667. foreach ($this->path as $k) {
  668. $history[] = $_arr = $_arr[$k];
  669. }
  670. if ($this->_containsGroupAlias) {
  671. $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
  672. $this->_containsGroupAlias = false;
  673. }
  674. // Adding string or numeric key to the innermost level or $this->arr.
  675. if (is_string($key) && $key == '<<') {
  676. if (!is_array ($_arr)) { $_arr = array (); }
  677. $_arr = array_merge ($_arr, $value);
  678. } else if ($key || $key === '' || $key === '0') {
  679. if (!is_array ($_arr))
  680. $_arr = array ($key=>$value);
  681. else
  682. $_arr[$key] = $value;
  683. } else {
  684. if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
  685. else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
  686. }
  687. $reverse_path = array_reverse($this->path);
  688. $reverse_history = array_reverse ($history);
  689. $reverse_history[0] = $_arr;
  690. $cnt = count($reverse_history) - 1;
  691. for ($i = 0; $i < $cnt; $i++) {
  692. $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
  693. }
  694. $this->result = $reverse_history[$cnt];
  695. $this->path[$incoming_indent] = $key;
  696. if ($this->_containsGroupAnchor) {
  697. $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
  698. if (is_array ($value)) {
  699. $k = key ($value);
  700. if (!is_int ($k)) {
  701. $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
  702. }
  703. }
  704. $this->_containsGroupAnchor = false;
  705. }
  706. }
  707. private static function startsLiteralBlock ($line) {
  708. $lastChar = substr (trim($line), -1);
  709. if ($lastChar != '>' && $lastChar != '|') return false;
  710. if ($lastChar == '|') return $lastChar;
  711. // HTML tags should not be counted as literal blocks.
  712. if (preg_match ('#<.*?>$#', $line)) return false;
  713. return $lastChar;
  714. }
  715. private static function greedilyNeedNextLine($line) {
  716. $line = trim ($line);
  717. if (!strlen($line)) return false;
  718. if (substr ($line, -1, 1) == ']') return false;
  719. if ($line[0] == '[') return true;
  720. if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
  721. return false;
  722. }
  723. private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) {
  724. $line = self::stripIndent($line, $indent);
  725. if ($literalBlockStyle !== '|') {
  726. $line = self::stripIndent($line);
  727. }
  728. $line = rtrim ($line, "\r\n\t ") . "\n";
  729. if ($literalBlockStyle == '|') {
  730. return $literalBlock . $line;
  731. }
  732. if (strlen($line) == 0)
  733. return rtrim($literalBlock, ' ') . "\n";
  734. if ($line == "\n" && $literalBlockStyle == '>') {
  735. return rtrim ($literalBlock, " \t") . "\n";
  736. }
  737. if ($line != "\n")
  738. $line = trim ($line, "\r\n ") . " ";
  739. return $literalBlock . $line;
  740. }
  741. function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
  742. foreach ($lineArray as $k => $_) {
  743. if (is_array($_))
  744. $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
  745. else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
  746. $lineArray[$k] = rtrim ($literalBlock, " \r\n");
  747. }
  748. return $lineArray;
  749. }
  750. private static function stripIndent ($line, $indent = -1) {
  751. if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
  752. return substr ($line, $indent);
  753. }
  754. private function getParentPathByIndent ($indent) {
  755. if ($indent == 0) return array();
  756. $linePath = $this->path;
  757. do {
  758. end($linePath); $lastIndentInParentPath = key($linePath);
  759. if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
  760. } while ($indent <= $lastIndentInParentPath);
  761. return $linePath;
  762. }
  763. private function clearBiggerPathValues ($indent) {
  764. if ($indent == 0) $this->path = array();
  765. if (empty ($this->path)) return true;
  766. foreach ($this->path as $k => $_) {
  767. if ($k > $indent) unset ($this->path[$k]);
  768. }
  769. return true;
  770. }
  771. private static function isComment ($line) {
  772. if (!$line) return false;
  773. if ($line[0] == '#') return true;
  774. if (trim($line, " \r\n\t") == '---') return true;
  775. return false;
  776. }
  777. private static function isEmpty ($line) {
  778. return (trim ($line) === '');
  779. }
  780. private function isArrayElement ($line) {
  781. if (!$line) return false;
  782. if ($line[0] != '-') return false;
  783. if (strlen ($line) > 3)
  784. if (substr($line,0,3) == '---') return false;
  785. return true;
  786. }
  787. private function isHashElement ($line) {
  788. return strpos($line, ':');
  789. }
  790. private function isLiteral ($line) {
  791. if ($this->isArrayElement($line)) return false;
  792. if ($this->isHashElement($line)) return false;
  793. return true;
  794. }
  795. private static function unquote ($value) {
  796. if (!$value) return $value;
  797. if (!is_string($value)) return $value;
  798. if ($value[0] == '\'') return trim ($value, '\'');
  799. if ($value[0] == '"') return trim ($value, '"');
  800. return $value;
  801. }
  802. private function startsMappedSequence ($line) {
  803. return ($line[0] == '-' && substr ($line, -1, 1) == ':');
  804. }
  805. private function returnMappedSequence ($line) {
  806. $array = array();
  807. $key = self::unquote(trim(substr($line,1,-1)));
  808. $array[$key] = array();
  809. $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
  810. return array($array);
  811. }
  812. private function returnMappedValue ($line) {
  813. $array = array();
  814. $key = self::unquote (trim(substr($line,0,-1)));
  815. $array[$key] = '';
  816. return $array;
  817. }
  818. private function startsMappedValue ($line) {
  819. return (substr ($line, -1, 1) == ':');
  820. }
  821. private function isPlainArray ($line) {
  822. return ($line[0] == '[' && substr ($line, -1, 1) == ']');
  823. }
  824. private function returnPlainArray ($line) {
  825. return $this->_toType($line);
  826. }
  827. private function returnKeyValuePair ($line) {
  828. $array = array();
  829. $key = '';
  830. if (strpos ($line, ':')) {
  831. // It's a key/value pair most likely
  832. // If the key is in double quotes pull it out
  833. if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
  834. $value = trim(str_replace($matches[1],'',$line));
  835. $key = $matches[2];
  836. } else {
  837. // Do some guesswork as to the key and the value
  838. $explode = explode(':',$line);
  839. $key = trim($explode[0]);
  840. array_shift($explode);
  841. $value = trim(implode(':',$explode));
  842. }
  843. // Set the type of the value. Int, string, etc
  844. $value = $this->_toType($value);
  845. if ($key === '0') $key = '__!YAMLZero';
  846. $array[$key] = $value;
  847. } else {
  848. $array = array ($line);
  849. }
  850. return $array;
  851. }
  852. private function returnArrayElement ($line) {
  853. if (strlen($line) <= 1) return array(array()); // Weird %)
  854. $array = array();
  855. $value = trim(substr($line,1));
  856. $value = $this->_toType($value);
  857. $array[] = $value;
  858. return $array;
  859. }
  860. private function nodeContainsGroup ($line) {
  861. $symbolsForReference = 'A-z0-9_\-';
  862. if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
  863. if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
  864. if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
  865. if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
  866. if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
  867. if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
  868. return false;
  869. }
  870. private function addGroup ($line, $group) {
  871. if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
  872. if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
  873. //print_r ($this->path);
  874. }
  875. private function stripGroup ($line, $group) {
  876. $line = trim(str_replace($group, '', $line));
  877. return $line;
  878. }
  879. }
  880. // Enable use of Spyc from command line
  881. // The syntax is the following: php spyc.php spyc.yaml
  882. define ('SPYC_FROM_COMMAND_LINE', false);
  883. do {
  884. if (!SPYC_FROM_COMMAND_LINE) break;
  885. if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
  886. if (empty ($_SERVER['PHP_SELF']) || $_SERVER['PHP_SELF'] != 'spyc.php') break;
  887. $file = $argv[1];
  888. // printf ("Spyc loading file: %s\n", $file);
  889. // print_r (spyc_load_file ($file));
  890. } while (0);