├── tests
├── failing1.yaml
├── quotes.yaml
├── indent_1.yaml
├── IndentTest.php
├── RoundTripTest.php
├── DumpTest.php
└── ParseTest.php
├── examples
├── yaml-load.php
└── yaml-dump.php
├── php4
├── 5to4.php
├── test.php4
└── spyc.php4
├── COPYING
├── spyc.yaml
├── README
└── spyc.php
/tests/failing1.yaml:
--------------------------------------------------------------------------------
1 | MyObject:
2 | Prop1: {key1:val1}
--------------------------------------------------------------------------------
/tests/quotes.yaml:
--------------------------------------------------------------------------------
1 | html_tags:
2 | -
3 | -
4 | html_content: 5 | -
hello world
6 | - helloYAML Data dumped back:'; 26 | -------------------------------------------------------------------------------- /php4/5to4.php: -------------------------------------------------------------------------------- 1 | ', $code); 13 | $f = fopen ($dest, 'w'); 14 | fwrite($f, $code); 15 | fclose ($f); 16 | print "Written to $dest.\n"; 17 | } -------------------------------------------------------------------------------- /examples/yaml-dump.php: -------------------------------------------------------------------------------- 1 | 'A sequence','second' => 'of mapped values'); 18 | $array['Mapped'] = array('A sequence','which is mapped'); 19 | $array['A Note'] = 'What if your text is too long?'; 20 | $array['Another Note'] = 'If that is the case, the dumper will probably fold your text by using a block. Kinda like this.'; 21 | $array['The trick?'] = 'The trick is that we overrode the default indent, 2, to 4 and the default wordwrap, 40, to 60.'; 22 | $array['Old Dog'] = "And if you want\n to preserve line breaks, \ngo ahead!"; 23 | $array['key:withcolon'] = "Should support this to"; 24 | 25 | $yaml = Spyc::YAMLDump($array,4,60); -------------------------------------------------------------------------------- /tests/indent_1.yaml: -------------------------------------------------------------------------------- 1 | root: 2 | child_1: 2 3 | 4 | child_2: 0 5 | child_3: 1 6 | 7 | root2: 8 | child_1: 1 9 | # A comment 10 | child_2: 2 11 | 12 | displays: 13 | - resolutions: 14 | 1024: 768 15 | - resolutions: 16 | 1920: 1200 17 | 18 | display: 19 | - resolutions: 20 | 1024: 768 21 | 1920: 1200 22 | producer: "Nec" 23 | 24 | nested_hashes_and_seqs: 25 | - { row: 0, col: 0, headsets_affected: [{ports: [0], side: left}], switch_function: {ics_ptt: true} } 26 | 27 | easier_nest: { h: [{a: b, a1: b1}, {c: d}] } 28 | 29 | steps: 30 | - step: &id001 31 | instrument: Lasik 2000 32 | pulseEnergy: 5.4 33 | pulseDuration: 12 34 | repetition: 1000 35 | spotSize: 1mm 36 | - step: 37 | <<: *id001 38 | spotSize: 2mm 39 | 40 | death masks are: 41 | sad: 2 42 | <<: {magnificent: 4} 43 | 44 | login: &login 45 | adapter: mysql 46 | host: localhost 47 | 48 | development: 49 | database: rails_dev 50 | <<: *login 51 | 52 | "key": "value:" 53 | colon_only: ":" 54 | 55 | list_and_comment: [one, two, three] # comment -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2011 Vladimir Andersen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /tests/IndentTest.php: -------------------------------------------------------------------------------- 1 | Y = Spyc::YAMLLoad("indent_1.yaml"); 11 | } 12 | 13 | public function testIndent_1() { 14 | $this->assertEquals (array ('child_1' => 2, 'child_2' => 0, 'child_3' => 1), $this->Y['root']); 15 | } 16 | 17 | public function testIndent_2() { 18 | $this->assertEquals (array ('child_1' => 1, 'child_2' => 2), $this->Y['root2']); 19 | } 20 | 21 | public function testIndent_3() { 22 | $this->assertEquals (array (array ('resolutions' => array (1024 => 768, 1920 => 1200), 'producer' => 'Nec')), $this->Y['display']); 23 | } 24 | 25 | public function testIndent_4() { 26 | $this->assertEquals (array ( 27 | array ('resolutions' => array (1024 => 768)), 28 | array ('resolutions' => array (1920 => 1200)), 29 | ), $this->Y['displays']); 30 | } 31 | 32 | public function testIndent_5() { 33 | $this->assertEquals (array (array ( 34 | 'row' => 0, 35 | 'col' => 0, 36 | 'headsets_affected' => array ( 37 | array ( 38 | 'ports' => array (0), 39 | 'side' => 'left', 40 | ) 41 | ), 42 | 'switch_function' => array ( 43 | 'ics_ptt' => true 44 | ) 45 | )), $this->Y['nested_hashes_and_seqs']); 46 | } 47 | 48 | public function testIndent_6() { 49 | $this->assertEquals (array ( 50 | 'h' => array ( 51 | array ('a' => 'b', 'a1' => 'b1'), 52 | array ('c' => 'd') 53 | ) 54 | ), $this->Y['easier_nest']); 55 | } 56 | 57 | public function testListAndComment() { 58 | $this->assertEquals (array ('one', 'two', 'three'), $this->Y['list_and_comment']); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /tests/RoundTripTest.php: -------------------------------------------------------------------------------- 1 | $a))); } 6 | 7 | 8 | class RoundTripTest extends PHPUnit_Framework_TestCase { 9 | 10 | protected function setUp() { 11 | } 12 | 13 | public function testNull() { 14 | $this->assertEquals (array ('x' => null), roundTrip (null)); 15 | } 16 | 17 | public function testY() { 18 | $this->assertEquals (array ('x' => 'y'), roundTrip ('y')); 19 | } 20 | 21 | public function testExclam() { 22 | $this->assertEquals (array ('x' => '!yeah'), roundTrip ('!yeah')); 23 | } 24 | 25 | public function test5() { 26 | $this->assertEquals (array ('x' => '5'), roundTrip ('5')); 27 | } 28 | 29 | public function testSpaces() { 30 | $this->assertEquals (array ('x' => 'x '), roundTrip ('x ')); 31 | } 32 | 33 | public function testApostrophes() { 34 | $this->assertEquals (array ('x' => "'biz'"), roundTrip ("'biz'")); 35 | } 36 | 37 | public function testNewLines() { 38 | $this->assertEquals (array ('x' => "\n"), roundTrip ("\n")); 39 | } 40 | 41 | public function testHashes() { 42 | $this->assertEquals (array ('x' => array ("#color" => '#fff')), roundTrip (array ("#color" => '#fff'))); 43 | } 44 | 45 | public function testWordWrap() { 46 | $this->assertEquals (array ('x' => "aaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), roundTrip ("aaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")); 47 | } 48 | 49 | public function testABCD() { 50 | $this->assertEquals (array ('a', 'b', 'c', 'd'), Spyc::YAMLLoad(Spyc::YAMLDump(array('a', 'b', 'c', 'd')))); 51 | } 52 | 53 | public function testABCD2() { 54 | $a = array('a', 'b', 'c', 'd'); // Create a simple list 55 | $b = Spyc::YAMLDump($a); // Dump the list as YAML 56 | $c = Spyc::YAMLLoad($b); // Load the dumped YAML 57 | $d = Spyc::YAMLDump($c); // Re-dump the data 58 | $this->assertSame($b, $d); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /tests/DumpTest.php: -------------------------------------------------------------------------------- 1 | files_to_test = array ('../spyc.yaml', 'failing1.yaml', 'indent_1.yaml', 'quotes.yaml'); 11 | } 12 | 13 | public function testDump() { 14 | foreach ($this->files_to_test as $file) { 15 | $yaml = spyc_load(file_get_contents($file)); 16 | $dump = Spyc::YAMLDump ($yaml); 17 | $yaml_after_dump = Spyc::YAMLLoad ($dump); 18 | $this->assertEquals ($yaml, $yaml_after_dump); 19 | } 20 | } 21 | 22 | public function testDumpWithQuotes() { 23 | $Spyc = new Spyc(); 24 | $Spyc->setting_dump_force_quotes = true; 25 | foreach ($this->files_to_test as $file) { 26 | $yaml = $Spyc->load(file_get_contents($file)); 27 | $dump = $Spyc->dump ($yaml); 28 | $yaml_after_dump = Spyc::YAMLLoad ($dump); 29 | $this->assertEquals ($yaml, $yaml_after_dump); 30 | } 31 | } 32 | 33 | public function testDumpArrays() { 34 | $dump = Spyc::YAMLDump(array ('item1', 'item2', 'item3')); 35 | $awaiting = "---\n- item1\n- item2\n- item3\n"; 36 | $this->assertEquals ($awaiting, $dump); 37 | } 38 | 39 | public function testDumpNumerics() { 40 | $dump = Spyc::YAMLDump(array ('404', '405', '500')); 41 | $awaiting = "---\n- 404\n- 405\n- 500\n"; 42 | $this->assertEquals ($awaiting, $dump); 43 | } 44 | 45 | public function testDumpAsterisks() { 46 | $dump = Spyc::YAMLDump(array ('*')); 47 | $awaiting = "---\n- '*'\n"; 48 | $this->assertEquals ($awaiting, $dump); 49 | } 50 | 51 | public function testDumpAmpersands() { 52 | $dump = Spyc::YAMLDump(array ('some' => '&foo')); 53 | $awaiting = "---\nsome: '&foo'\n"; 54 | $this->assertEquals ($awaiting, $dump); 55 | } 56 | 57 | public function testDumpExclamations() { 58 | $dump = Spyc::YAMLDump(array ('some' => '!foo')); 59 | $awaiting = "---\nsome: '!foo'\n"; 60 | $this->assertEquals ($awaiting, $dump); 61 | } 62 | 63 | public function testDumpExclamations2() { 64 | $dump = Spyc::YAMLDump(array ('some' => 'foo!')); 65 | $awaiting = "---\nsome: foo!\n"; 66 | $this->assertEquals ($awaiting, $dump); 67 | } 68 | 69 | public function testDumpApostrophes() { 70 | $dump = Spyc::YAMLDump(array ('some' => "'Biz' pimpt bedrijventerreinen")); 71 | $awaiting = "---\nsome: \"'Biz' pimpt bedrijventerreinen\"\n"; 72 | $this->assertEquals ($awaiting, $dump); 73 | } 74 | 75 | public function testDumpNumericHashes() { 76 | $dump = Spyc::YAMLDump(array ("titel"=> array("0" => "", 1 => "Dr.", 5 => "Prof.", 6 => "Prof. Dr."))); 77 | $awaiting = "---\ntitel:\n 0:\n 1: Dr.\n 5: Prof.\n 6: Prof. Dr.\n"; 78 | $this->assertEquals ($awaiting, $dump); 79 | } 80 | 81 | public function testEmpty() { 82 | $dump = Spyc::YAMLDump(array("foo" => array())); 83 | $awaiting = "---\nfoo: [ ]\n"; 84 | $this->assertEquals ($awaiting, $dump); 85 | } 86 | 87 | public function testHashesInKeys() { 88 | $dump = Spyc::YAMLDump(array ('#color' => '#ffffff')); 89 | $awaiting = "---\n\"#color\": '#ffffff'\n"; 90 | $this->assertEquals ($awaiting, $dump); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /spyc.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # S P Y C 3 | # a simple php yaml class 4 | # 5 | # authors: [vlad andersen (vlad.andersen@gmail.com), chris wanstrath (chris@ozmm.org)] 6 | # websites: [http://www.yaml.org, http://spyc.sourceforge.net/] 7 | # license: [MIT License, http://www.opensource.org/licenses/mit-license.php] 8 | # copyright: (c) 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen 9 | # 10 | # spyc.yml - A file containing the YAML that Spyc understands. 11 | 12 | --- 13 | 14 | # Mappings - with proper types 15 | String: Anyone's name, really. 16 | Int: 13 17 | True: true 18 | False: false 19 | Zero: 0 20 | Null: NULL 21 | NotNull: 'null' 22 | NotTrue: 'y' 23 | NotBoolTrue: 'true' 24 | NotInt: '5' 25 | Float: 5.34 26 | Negative: -90 27 | SmallFloat: 0.7 28 | NewLine: \n 29 | 30 | # A sequence 31 | - PHP Class 32 | - Basic YAML Loader 33 | - Very Basic YAML Dumper 34 | 35 | # A sequence of a sequence 36 | - 37 | - YAML is so easy to learn. 38 | - Your config files will never be the same. 39 | 40 | # Sequence of mappings 41 | - 42 | cpu: 1.5ghz 43 | ram: 1 gig 44 | os : os x 10.4.1 45 | 46 | # Mapped sequence 47 | domains: 48 | - yaml.org 49 | - php.net 50 | 51 | # A sequence like this. 52 | - program: Adium 53 | platform: OS X 54 | type: Chat Client 55 | 56 | # A folded block as a mapped value 57 | no time: > 58 | There isn't any time 59 | for your tricks! 60 | 61 | Do you understand? 62 | 63 | # A literal block as a mapped value 64 | some time: | 65 | There is nothing but time 66 | for your tricks. 67 | 68 | # Crazy combinations 69 | databases: 70 | - name: spartan 71 | notes: 72 | - Needs to be backed up 73 | - Needs to be normalized 74 | type: mysql 75 | 76 | # You can be a bit tricky 77 | "if: you'd": like 78 | 79 | # Inline sequences 80 | - [One, Two, Three, Four] 81 | 82 | # Nested Inline Sequences 83 | - [One, [Two, And, Three], Four, Five] 84 | 85 | # Nested Nested Inline Sequences 86 | - [This, [Is, Getting, [Ridiculous, Guys]], Seriously, [Show, Mercy]] 87 | 88 | # Inline mappings 89 | - {name: chris, age: young, brand: lucky strike} 90 | 91 | # Nested inline mappings 92 | - {name: mark, age: older than chris, brand: [marlboro, lucky strike]} 93 | 94 | # References -- they're shaky, but functional 95 | dynamic languages: &DLANGS 96 | - Perl 97 | - Python 98 | - PHP 99 | - Ruby 100 | compiled languages: &CLANGS 101 | - C/C++ 102 | - Java 103 | all languages: 104 | - *DLANGS 105 | - *CLANGS 106 | 107 | # Added in .2.2: Escaped quotes 108 | - you know, this shouldn't work. but it does. 109 | - 'that''s my value.' 110 | - 'again, that\'s my value.' 111 | - "here's to \"quotes\", boss." 112 | 113 | # added in .2.3 114 | - {name: "Foo, Bar's", age: 20} 115 | 116 | # Added in .2.4: bug [ 1418193 ] Quote Values in Nested Arrays 117 | - [a, ['1', "2"], b] 118 | 119 | # Added in .2.4: malformed YAML 120 | all 121 | javascripts: [dom1.js, dom.js] 122 | 123 | # Added in .2 124 | 1040: Ooo, a numeric key! # And working comments? Wow! Colons in comments: a menace (0.3). 125 | 126 | hash_1: Hash #and a comment 127 | hash_2: "Hash #and a comment" 128 | "hash#3": "Hash (#) can appear in key too" 129 | 130 | float_test: 1.0 131 | float_test_with_quotes: '1.0' 132 | float_inverse_test: 001 133 | 134 | a_really_large_number: 115792089237316195423570985008687907853269984665640564039457584007913129639936 # 2^256 135 | 136 | int array: [ 1, 2, 3 ] 137 | 138 | array on several lines: 139 | [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 140 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ] 141 | 142 | morelesskey: "
'; 24 | echo Spyc::YAMLDump($array); 25 | echo '
'), 'html_content' => array ('
hello world
', 'hello
44 | * $Spyc = new Spyc;
45 | * $array = $Spyc->load($file);
46 | *
47 | * or:
48 | *
49 | * $array = Spyc::YAMLLoad($file);
50 | *
51 | * or:
52 | *
53 | * $array = spyc_load_file($file);
54 | *
55 | * @package Spyc
56 | */
57 | class Spyc {
58 |
59 | // SETTINGS
60 |
61 | /**
62 | * Setting this to true will force YAMLDump to enclose any string value in
63 | * quotes. False by default.
64 | *
65 | * @var bool
66 | */
67 | var $setting_dump_force_quotes = false;
68 |
69 | /**
70 | * Setting this to true will forse YAMLLoad to use syck_load function when
71 | * possible. False by default.
72 | * @var bool
73 | */
74 | var $setting_use_syck_is_possible = false;
75 |
76 |
77 |
78 | /**#@+
79 | * @access private
80 | * @var mixed
81 | */
82 | var $_dumpIndent;
83 | var $_dumpWordWrap;
84 | var $_containsGroupAnchor = false;
85 | var $_containsGroupAlias = false;
86 | var $path;
87 | var $result;
88 | var $LiteralPlaceHolder = '___YAML_Literal_Block___';
89 | var $SavedGroups = array();
90 | var $indent;
91 | /**
92 | * Path modifier that should be applied after adding current element.
93 | * @var array
94 | */
95 | var $delayedPath = array();
96 |
97 | /**#@+
98 | * @access public
99 | * @var mixed
100 | */
101 | var $_nodeId;
102 |
103 | /**
104 | * Load a valid YAML string to Spyc.
105 | * @param string $input
106 | * @return array
107 | */
108 | function load ($input) {
109 | return $this->__loadString($input);
110 | }
111 |
112 | /**
113 | * Load a valid YAML file to Spyc.
114 | * @param string $file
115 | * @return array
116 | */
117 | function loadFile ($file) {
118 | return $this->__load($file);
119 | }
120 |
121 | /**
122 | * Load YAML into a PHP array statically
123 | *
124 | * The load method, when supplied with a YAML stream (string or file),
125 | * will do its best to convert YAML in a file into a PHP array. Pretty
126 | * simple.
127 | * Usage:
128 | *
129 | * $array = Spyc::YAMLLoad('lucky.yaml');
130 | * print_r($array);
131 | *
132 | * @access public
133 | * @return array
134 | * @param string $input Path of YAML file or string containing YAML
135 | */
136 | function YAMLLoad($input) {
137 | $Spyc = new Spyc;
138 | return $Spyc->__load($input);
139 | }
140 |
141 | /**
142 | * Load a string of YAML into a PHP array statically
143 | *
144 | * The load method, when supplied with a YAML string, will do its best
145 | * to convert YAML in a string into a PHP array. Pretty simple.
146 | *
147 | * Note: use this function if you don't want files from the file system
148 | * loaded and processed as YAML. This is of interest to people concerned
149 | * about security whose input is from a string.
150 | *
151 | * Usage:
152 | *
153 | * $array = Spyc::YAMLLoadString("---\n0: hello world\n");
154 | * print_r($array);
155 | *
156 | * @access public
157 | * @return array
158 | * @param string $input String containing YAML
159 | */
160 | function YAMLLoadString($input) {
161 | $Spyc = new Spyc;
162 | return $Spyc->__loadString($input);
163 | }
164 |
165 | /**
166 | * Dump YAML from PHP array statically
167 | *
168 | * The dump method, when supplied with an array, will do its best
169 | * to convert the array into friendly YAML. Pretty simple. Feel free to
170 | * save the returned string as nothing.yaml and pass it around.
171 | *
172 | * Oh, and you can decide how big the indent is and what the wordwrap
173 | * for folding is. Pretty cool -- just pass in 'false' for either if
174 | * you want to use the default.
175 | *
176 | * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
177 | * you can turn off wordwrap by passing in 0.
178 | *
179 | * @access public
180 | * @return string
181 | * @param array $array PHP array
182 | * @param int $indent Pass in false to use the default, which is 2
183 | * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
184 | */
185 | function YAMLDump($array,$indent = false,$wordwrap = false) {
186 | $spyc = new Spyc;
187 | return $spyc->dump($array,$indent,$wordwrap);
188 | }
189 |
190 |
191 | /**
192 | * Dump PHP array to YAML
193 | *
194 | * The dump method, when supplied with an array, will do its best
195 | * to convert the array into friendly YAML. Pretty simple. Feel free to
196 | * save the returned string as tasteful.yaml and pass it around.
197 | *
198 | * Oh, and you can decide how big the indent is and what the wordwrap
199 | * for folding is. Pretty cool -- just pass in 'false' for either if
200 | * you want to use the default.
201 | *
202 | * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
203 | * you can turn off wordwrap by passing in 0.
204 | *
205 | * @access public
206 | * @return string
207 | * @param array $array PHP array
208 | * @param int $indent Pass in false to use the default, which is 2
209 | * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
210 | */
211 | function dump($array,$indent = false,$wordwrap = false) {
212 | // Dumps to some very clean YAML. We'll have to add some more features
213 | // and options soon. And better support for folding.
214 |
215 | // New features and options.
216 | if ($indent === false or !is_numeric($indent)) {
217 | $this->_dumpIndent = 2;
218 | } else {
219 | $this->_dumpIndent = $indent;
220 | }
221 |
222 | if ($wordwrap === false or !is_numeric($wordwrap)) {
223 | $this->_dumpWordWrap = 40;
224 | } else {
225 | $this->_dumpWordWrap = $wordwrap;
226 | }
227 |
228 | // New YAML document
229 | $string = "---\n";
230 |
231 | // Start at the base of the array and move through it.
232 | if ($array) {
233 | $array = (array)$array;
234 | $first_key = key($array);
235 |
236 | $previous_key = -1;
237 | foreach ($array as $key => $value) {
238 | $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key);
239 | $previous_key = $key;
240 | }
241 | }
242 | return $string;
243 | }
244 |
245 | /**
246 | * Attempts to convert a key / value array item to YAML
247 | * @access private
248 | * @return string
249 | * @param $key The name of the key
250 | * @param $value The value of the item
251 | * @param $indent The indent of the current node
252 | */
253 | function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0) {
254 | if (is_array($value)) {
255 | if (empty ($value))
256 | return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key);
257 | // It has children. What to do?
258 | // Make it the right kind of item
259 | $string = $this->_dumpNode($key, NULL, $indent, $previous_key, $first_key);
260 | // Add the indent
261 | $indent += $this->_dumpIndent;
262 | // Yamlize the array
263 | $string .= $this->_yamlizeArray($value,$indent);
264 | } elseif (!is_array($value)) {
265 | // It doesn't have children. Yip.
266 | $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key);
267 | }
268 | return $string;
269 | }
270 |
271 | /**
272 | * Attempts to convert an array to YAML
273 | * @access private
274 | * @return string
275 | * @param $array The array you want to convert
276 | * @param $indent The indent of the current level
277 | */
278 | function _yamlizeArray($array,$indent) {
279 | if (is_array($array)) {
280 | $string = '';
281 | $previous_key = -1;
282 | $first_key = key($array);
283 | foreach ($array as $key => $value) {
284 | $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key);
285 | $previous_key = $key;
286 | }
287 | return $string;
288 | } else {
289 | return false;
290 | }
291 | }
292 |
293 | /**
294 | * Returns YAML from a key and a value
295 | * @access private
296 | * @return string
297 | * @param $key The name of the key
298 | * @param $value The value of the item
299 | * @param $indent The indent of the current node
300 | */
301 | function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0) {
302 | // do some folding here, for blocks
303 | if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
304 | strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false ||
305 | strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || substr ($value, -1, 1) == ':')) {
306 | $value = $this->_doLiteralBlock($value,$indent);
307 | } else {
308 | $value = $this->_doFolding($value,$indent);
309 | if (is_bool($value)) {
310 | $value = ($value) ? "true" : "false";
311 | }
312 | }
313 |
314 | if ($value === array()) $value = '[ ]';
315 |
316 | $spaces = str_repeat(' ',$indent);
317 |
318 | if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
319 | // It's a sequence
320 | $string = $spaces.'- '.$value."\n";
321 | } else {
322 | if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"');
323 | // It's mapped
324 | if (strpos($key, ":") !== false) { $key = '"' . $key . '"'; }
325 | $string = $spaces.$key.': '.$value."\n";
326 | }
327 | return $string;
328 | }
329 |
330 | /**
331 | * Creates a literal block for dumping
332 | * @access private
333 | * @return string
334 | * @param $value
335 | * @param $indent int The value of the indent
336 | */
337 | function _doLiteralBlock($value,$indent) {
338 | if (strpos($value, "\n") === false && strpos($value, "'") === false) {
339 | return sprintf ("'%s'", $value);
340 | }
341 | if (strpos($value, "\n") === false && strpos($value, '"') === false) {
342 | return sprintf ('"%s"', $value);
343 | }
344 | $exploded = explode("\n",$value);
345 | $newValue = '|';
346 | $indent += $this->_dumpIndent;
347 | $spaces = str_repeat(' ',$indent);
348 | foreach ($exploded as $line) {
349 | $newValue .= "\n" . $spaces . trim($line);
350 | }
351 | return $newValue;
352 | }
353 |
354 | /**
355 | * Folds a string of text, if necessary
356 | * @access private
357 | * @return string
358 | * @param $value The string you wish to fold
359 | */
360 | function _doFolding($value,$indent) {
361 | // Don't do anything if wordwrap is set to 0
362 |
363 | if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
364 | $indent += $this->_dumpIndent;
365 | $indent = str_repeat(' ',$indent);
366 | $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
367 | $value = ">\n".$indent.$wrapped;
368 | } else {
369 | if ($this->setting_dump_force_quotes && is_string ($value))
370 | $value = '"' . $value . '"';
371 | }
372 |
373 |
374 | return $value;
375 | }
376 |
377 | // LOADING FUNCTIONS
378 |
379 | function __load($input) {
380 | $Source = $this->loadFromSource($input);
381 | return $this->loadWithSource($Source);
382 | }
383 |
384 | function __loadString($input) {
385 | $Source = $this->loadFromString($input);
386 | return $this->loadWithSource($Source);
387 | }
388 |
389 | function loadWithSource($Source) {
390 | if (empty ($Source)) return array();
391 | if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
392 | $array = syck_load (implode ('', $Source));
393 | return is_array($array) ? $array : array();
394 | }
395 |
396 | $this->path = array();
397 | $this->result = array();
398 |
399 | $cnt = count($Source);
400 | for ($i = 0; $i < $cnt; $i++) {
401 | $line = $Source[$i];
402 |
403 | $this->indent = strlen($line) - strlen(ltrim($line));
404 | $tempPath = $this->getParentPathByIndent($this->indent);
405 | $line = $this->stripIndent($line, $this->indent);
406 | if ($this->isComment($line)) continue;
407 | if ($this->isEmpty($line)) continue;
408 | $this->path = $tempPath;
409 |
410 | $literalBlockStyle = $this->startsLiteralBlock($line);
411 | if ($literalBlockStyle) {
412 | $line = rtrim ($line, $literalBlockStyle . " \n");
413 | $literalBlock = '';
414 | $line .= $this->LiteralPlaceHolder;
415 |
416 | while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
417 | $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle);
418 | }
419 | $i--;
420 | }
421 |
422 | while (++$i < $cnt && $this->greedilyNeedNextLine($line)) {
423 | $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
424 | }
425 | $i--;
426 |
427 |
428 |
429 | if (strpos ($line, '#')) {
430 | if (strpos ($line, '"') === false && strpos ($line, "'") === false)
431 | $line = preg_replace('/\s+#(.+)$/','',$line);
432 | }
433 |
434 | $lineArray = $this->_parseLine($line);
435 |
436 | if ($literalBlockStyle)
437 | $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
438 |
439 | $this->addArray($lineArray, $this->indent);
440 |
441 | foreach ($this->delayedPath as $indent => $delayedPath)
442 | $this->path[$indent] = $delayedPath;
443 |
444 | $this->delayedPath = array();
445 |
446 | }
447 | return $this->result;
448 | }
449 |
450 | function loadFromSource ($input) {
451 | if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
452 | return file($input);
453 |
454 | return $this->loadFromString($input);
455 | }
456 |
457 | function loadFromString ($input) {
458 | $lines = explode("\n",$input);
459 | foreach ($lines as $k => $_) {
460 | $lines[$k] = rtrim ($_, "\r");
461 | }
462 | return $lines;
463 | }
464 |
465 | /**
466 | * Parses YAML code and returns an array for a node
467 | * @access private
468 | * @return array
469 | * @param string $line A line from the YAML file
470 | */
471 | function _parseLine($line) {
472 | if (!$line) return array();
473 | $line = trim($line);
474 |
475 | if (!$line) return array();
476 | $array = array();
477 |
478 | $group = $this->nodeContainsGroup($line);
479 | if ($group) {
480 | $this->addGroup($line, $group);
481 | $line = $this->stripGroup ($line, $group);
482 | }
483 |
484 | if ($this->startsMappedSequence($line))
485 | return $this->returnMappedSequence($line);
486 |
487 | if ($this->startsMappedValue($line))
488 | return $this->returnMappedValue($line);
489 |
490 | if ($this->isArrayElement($line))
491 | return $this->returnArrayElement($line);
492 |
493 | if ($this->isPlainArray($line))
494 | return $this->returnPlainArray($line);
495 |
496 |
497 | return $this->returnKeyValuePair($line);
498 |
499 | }
500 |
501 | /**
502 | * Finds the type of the passed value, returns the value as the new type.
503 | * @access private
504 | * @param string $value
505 | * @return mixed
506 | */
507 | function _toType($value) {
508 | if ($value === '') return null;
509 | $first_character = $value[0];
510 | $last_character = substr($value, -1, 1);
511 |
512 | $is_quoted = false;
513 | do {
514 | if (!$value) break;
515 | if ($first_character != '"' && $first_character != "'") break;
516 | if ($last_character != '"' && $last_character != "'") break;
517 | $is_quoted = true;
518 | } while (0);
519 |
520 | if ($is_quoted)
521 | return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\''));
522 |
523 | if (strpos($value, ' #') !== false)
524 | $value = preg_replace('/\s+#(.+)$/','',$value);
525 |
526 | if ($first_character == '[' && $last_character == ']') {
527 | // Take out strings sequences and mappings
528 | $innerValue = trim(substr ($value, 1, -1));
529 | if ($innerValue === '') return array();
530 | $explode = $this->_inlineEscape($innerValue);
531 | // Propagate value array
532 | $value = array();
533 | foreach ($explode as $v) {
534 | $value[] = $this->_toType($v);
535 | }
536 | return $value;
537 | }
538 |
539 | if (strpos($value,': ')!==false && $first_character != '{') {
540 | $array = explode(': ',$value);
541 | $key = trim($array[0]);
542 | array_shift($array);
543 | $value = trim(implode(': ',$array));
544 | $value = $this->_toType($value);
545 | return array($key => $value);
546 | }
547 |
548 | if ($first_character == '{' && $last_character == '}') {
549 | $innerValue = trim(substr ($value, 1, -1));
550 | if ($innerValue === '') return array();
551 | // Inline Mapping
552 | // Take out strings sequences and mappings
553 | $explode = $this->_inlineEscape($innerValue);
554 | // Propagate value array
555 | $array = array();
556 | foreach ($explode as $v) {
557 | $SubArr = $this->_toType($v);
558 | if (empty($SubArr)) continue;
559 | if (is_array ($SubArr)) {
560 | $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
561 | }
562 | $array[] = $SubArr;
563 | }
564 | return $array;
565 | }
566 |
567 | if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
568 | return null;
569 | }
570 |
571 | if (intval($first_character) > 0 && preg_match ('/^[1-9]+[0-9]*$/', $value)) {
572 | $intvalue = (int)$value;
573 | if ($intvalue != PHP_INT_MAX)
574 | $value = $intvalue;
575 | return $value;
576 | }
577 |
578 | if (in_array($value,
579 | array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) {
580 | return true;
581 | }
582 |
583 | if (in_array(strtolower($value),
584 | array('false', 'off', '-', 'no', 'n'))) {
585 | return false;
586 | }
587 |
588 | if (is_numeric($value)) {
589 | if ($value === '0') return 0;
590 | if (trim ($value, 0) === $value)
591 | $value = (float)$value;
592 | return $value;
593 | }
594 |
595 | return $value;
596 | }
597 |
598 | /**
599 | * Used in inlines to check for more inlines or quoted strings
600 | * @access private
601 | * @return array
602 | */
603 | function _inlineEscape($inline) {
604 | // There's gotta be a cleaner way to do this...
605 | // While pure sequences seem to be nesting just fine,
606 | // pure mappings and mappings with sequences inside can't go very
607 | // deep. This needs to be fixed.
608 |
609 | $seqs = array();
610 | $maps = array();
611 | $saved_strings = array();
612 |
613 | // Check for strings
614 | $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
615 | if (preg_match_all($regex,$inline,$strings)) {
616 | $saved_strings = $strings[0];
617 | $inline = preg_replace($regex,'YAMLString',$inline);
618 | }
619 | unset($regex);
620 |
621 | $i = 0;
622 | do {
623 |
624 | // Check for sequences
625 | while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
626 | $seqs[] = $matchseqs[0];
627 | $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
628 | }
629 |
630 | // Check for mappings
631 | while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
632 | $maps[] = $matchmaps[0];
633 | $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
634 | }
635 |
636 | if ($i++ >= 10) break;
637 |
638 | } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
639 |
640 | $explode = explode(', ',$inline);
641 | $stringi = 0; $i = 0;
642 |
643 | while (1) {
644 |
645 | // Re-add the sequences
646 | if (!empty($seqs)) {
647 | foreach ($explode as $key => $value) {
648 | if (strpos($value,'YAMLSeq') !== false) {
649 | foreach ($seqs as $seqk => $seq) {
650 | $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
651 | $value = $explode[$key];
652 | }
653 | }
654 | }
655 | }
656 |
657 | // Re-add the mappings
658 | if (!empty($maps)) {
659 | foreach ($explode as $key => $value) {
660 | if (strpos($value,'YAMLMap') !== false) {
661 | foreach ($maps as $mapk => $map) {
662 | $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
663 | $value = $explode[$key];
664 | }
665 | }
666 | }
667 | }
668 |
669 |
670 | // Re-add the strings
671 | if (!empty($saved_strings)) {
672 | foreach ($explode as $key => $value) {
673 | while (strpos($value,'YAMLString') !== false) {
674 | $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
675 | unset($saved_strings[$stringi]);
676 | ++$stringi;
677 | $value = $explode[$key];
678 | }
679 | }
680 | }
681 |
682 | $finished = true;
683 | foreach ($explode as $key => $value) {
684 | if (strpos($value,'YAMLSeq') !== false) {
685 | $finished = false; break;
686 | }
687 | if (strpos($value,'YAMLMap') !== false) {
688 | $finished = false; break;
689 | }
690 | if (strpos($value,'YAMLString') !== false) {
691 | $finished = false; break;
692 | }
693 | }
694 | if ($finished) break;
695 |
696 | $i++;
697 | if ($i > 10)
698 | break; // Prevent infinite loops.
699 | }
700 |
701 | return $explode;
702 | }
703 |
704 | function literalBlockContinues ($line, $lineIndent) {
705 | if (!trim($line)) return true;
706 | if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
707 | return false;
708 | }
709 |
710 | function referenceContentsByAlias ($alias) {
711 | do {
712 | if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
713 | $groupPath = $this->SavedGroups[$alias];
714 | $value = $this->result;
715 | foreach ($groupPath as $k) {
716 | $value = $value[$k];
717 | }
718 | } while (false);
719 | return $value;
720 | }
721 |
722 | function addArrayInline ($array, $indent) {
723 | $CommonGroupPath = $this->path;
724 | if (empty ($array)) return false;
725 |
726 | foreach ($array as $k => $_) {
727 | $this->addArray(array($k => $_), $indent);
728 | $this->path = $CommonGroupPath;
729 | }
730 | return true;
731 | }
732 |
733 | function addArray ($incoming_data, $incoming_indent) {
734 |
735 | // print_r ($incoming_data);
736 |
737 | if (count ($incoming_data) > 1)
738 | return $this->addArrayInline ($incoming_data, $incoming_indent);
739 |
740 | $key = key ($incoming_data);
741 | $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
742 | if ($key === '__!YAMLZero') $key = '0';
743 |
744 | if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
745 | if ($key || $key === '' || $key === '0') {
746 | $this->result[$key] = $value;
747 | } else {
748 | $this->result[] = $value; end ($this->result); $key = key ($this->result);
749 | }
750 | $this->path[$incoming_indent] = $key;
751 | return;
752 | }
753 |
754 |
755 |
756 | $history = array();
757 | // Unfolding inner array tree.
758 | $history[] = $_arr = $this->result;
759 | foreach ($this->path as $k) {
760 | $history[] = $_arr = $_arr[$k];
761 | }
762 |
763 | if ($this->_containsGroupAlias) {
764 | $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
765 | $this->_containsGroupAlias = false;
766 | }
767 |
768 |
769 | // Adding string or numeric key to the innermost level or $this->arr.
770 | if (is_string($key) && $key == '<<') {
771 | if (!is_array ($_arr)) { $_arr = array (); }
772 | $_arr = array_merge ($_arr, $value);
773 | } else if ($key || $key === '' || $key === '0') {
774 | $_arr[$key] = $value;
775 | } else {
776 | if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
777 | else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
778 | }
779 |
780 | $reverse_path = array_reverse($this->path);
781 | $reverse_history = array_reverse ($history);
782 | $reverse_history[0] = $_arr;
783 | $cnt = count($reverse_history) - 1;
784 | for ($i = 0; $i < $cnt; $i++) {
785 | $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
786 | }
787 | $this->result = $reverse_history[$cnt];
788 |
789 | $this->path[$incoming_indent] = $key;
790 |
791 | if ($this->_containsGroupAnchor) {
792 | $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
793 | if (is_array ($value)) {
794 | $k = key ($value);
795 | if (!is_int ($k)) {
796 | $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
797 | }
798 | }
799 | $this->_containsGroupAnchor = false;
800 | }
801 |
802 | }
803 |
804 | function startsLiteralBlock ($line) {
805 | $lastChar = substr (trim($line), -1);
806 | if ($lastChar != '>' && $lastChar != '|') return false;
807 | if ($lastChar == '|') return $lastChar;
808 | // HTML tags should not be counted as literal blocks.
809 | if (preg_match ('#<.*?>$#', $line)) return false;
810 | return $lastChar;
811 | }
812 |
813 | function greedilyNeedNextLine($line) {
814 | $line = trim ($line);
815 | if (!strlen($line)) return false;
816 | if (substr ($line, -1, 1) == ']') return false;
817 | if ($line[0] == '[') return true;
818 | if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
819 | return false;
820 | }
821 |
822 | function addLiteralLine ($literalBlock, $line, $literalBlockStyle) {
823 | $line = $this->stripIndent($line);
824 | $line = rtrim ($line, "\r\n\t ") . "\n";
825 | if ($literalBlockStyle == '|') {
826 | return $literalBlock . $line;
827 | }
828 | if (strlen($line) == 0)
829 | return rtrim($literalBlock, ' ') . "\n";
830 | if ($line == "\n" && $literalBlockStyle == '>') {
831 | return rtrim ($literalBlock, " \t") . "\n";
832 | }
833 | if ($line != "\n")
834 | $line = trim ($line, "\r\n ") . " ";
835 | return $literalBlock . $line;
836 | }
837 |
838 | function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
839 | foreach ($lineArray as $k => $_) {
840 | if (is_array($_))
841 | $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
842 | else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
843 | $lineArray[$k] = rtrim ($literalBlock, " \r\n");
844 | }
845 | return $lineArray;
846 | }
847 |
848 | function stripIndent ($line, $indent = -1) {
849 | if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
850 | return substr ($line, $indent);
851 | }
852 |
853 | function getParentPathByIndent ($indent) {
854 | if ($indent == 0) return array();
855 | $linePath = $this->path;
856 | do {
857 | end($linePath); $lastIndentInParentPath = key($linePath);
858 | if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
859 | } while ($indent <= $lastIndentInParentPath);
860 | return $linePath;
861 | }
862 |
863 |
864 | function clearBiggerPathValues ($indent) {
865 |
866 |
867 | if ($indent == 0) $this->path = array();
868 | if (empty ($this->path)) return true;
869 |
870 | foreach ($this->path as $k => $_) {
871 | if ($k > $indent) unset ($this->path[$k]);
872 | }
873 |
874 | return true;
875 | }
876 |
877 |
878 | function isComment ($line) {
879 | if (!$line) return false;
880 | if ($line[0] == '#') return true;
881 | if (trim($line, " \r\n\t") == '---') return true;
882 | return false;
883 | }
884 |
885 | function isEmpty ($line) {
886 | return (trim ($line) === '');
887 | }
888 |
889 |
890 | function isArrayElement ($line) {
891 | if (!$line) return false;
892 | if ($line[0] != '-') return false;
893 | if (strlen ($line) > 3)
894 | if (substr($line,0,3) == '---') return false;
895 |
896 | return true;
897 | }
898 |
899 | function isHashElement ($line) {
900 | return strpos($line, ':');
901 | }
902 |
903 | function isLiteral ($line) {
904 | if ($this->isArrayElement($line)) return false;
905 | if ($this->isHashElement($line)) return false;
906 | return true;
907 | }
908 |
909 |
910 | function unquote ($value) {
911 | if (!$value) return $value;
912 | if (!is_string($value)) return $value;
913 | if ($value[0] == '\'') return trim ($value, '\'');
914 | if ($value[0] == '"') return trim ($value, '"');
915 | return $value;
916 | }
917 |
918 | function startsMappedSequence ($line) {
919 | return ($line[0] == '-' && substr ($line, -1, 1) == ':');
920 | }
921 |
922 | function returnMappedSequence ($line) {
923 | $array = array();
924 | $key = $this->unquote(trim(substr($line,1,-1)));
925 | $array[$key] = array();
926 | $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
927 | return array($array);
928 | }
929 |
930 | function returnMappedValue ($line) {
931 | $array = array();
932 | $key = $this->unquote (trim(substr($line,0,-1)));
933 | $array[$key] = '';
934 | return $array;
935 | }
936 |
937 | function startsMappedValue ($line) {
938 | return (substr ($line, -1, 1) == ':');
939 | }
940 |
941 | function isPlainArray ($line) {
942 | return ($line[0] == '[' && substr ($line, -1, 1) == ']');
943 | }
944 |
945 | function returnPlainArray ($line) {
946 | return $this->_toType($line);
947 | }
948 |
949 | function returnKeyValuePair ($line) {
950 | $array = array();
951 | $key = '';
952 | if (strpos ($line, ':')) {
953 | // It's a key/value pair most likely
954 | // If the key is in double quotes pull it out
955 | if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
956 | $value = trim(str_replace($matches[1],'',$line));
957 | $key = $matches[2];
958 | } else {
959 | // Do some guesswork as to the key and the value
960 | $explode = explode(':',$line);
961 | $key = trim($explode[0]);
962 | array_shift($explode);
963 | $value = trim(implode(':',$explode));
964 | }
965 | // Set the type of the value. Int, string, etc
966 | $value = $this->_toType($value);
967 | if ($key === '0') $key = '__!YAMLZero';
968 | $array[$key] = $value;
969 | } else {
970 | $array = array ($line);
971 | }
972 | return $array;
973 |
974 | }
975 |
976 |
977 | function returnArrayElement ($line) {
978 | if (strlen($line) <= 1) return array(array()); // Weird %)
979 | $array = array();
980 | $value = trim(substr($line,1));
981 | $value = $this->_toType($value);
982 | $array[] = $value;
983 | return $array;
984 | }
985 |
986 |
987 | function nodeContainsGroup ($line) {
988 | $symbolsForReference = 'A-z0-9_\-';
989 | if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
990 | if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
991 | if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
992 | if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
993 | if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
994 | if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
995 | return false;
996 |
997 | }
998 |
999 | function addGroup ($line, $group) {
1000 | if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
1001 | if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
1002 | //print_r ($this->path);
1003 | }
1004 |
1005 | function stripGroup ($line, $group) {
1006 | $line = trim(str_replace($group, '', $line));
1007 | return $line;
1008 | }
1009 | }
1010 |
1011 | // Enable use of Spyc from command line
1012 | // The syntax is the following: php spyc.php spyc.yaml
1013 |
1014 | define ('SPYC_FROM_COMMAND_LINE', false);
1015 |
1016 | do {
1017 | if (!SPYC_FROM_COMMAND_LINE) break;
1018 | if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
1019 | if (empty ($_SERVER['PHP_SELF']) || $_SERVER['PHP_SELF'] != 'spyc.php') break;
1020 | $file = $argv[1];
1021 | printf ("Spyc loading file: %s\n", $file);
1022 | print_r (spyc_load_file ($file));
1023 | } while (0);
--------------------------------------------------------------------------------
/spyc.php:
--------------------------------------------------------------------------------
1 |
6 | * @author Chris Wanstrath
44 | * $Spyc = new Spyc;
45 | * $array = $Spyc->load($file);
46 | *
47 | * or:
48 | *
49 | * $array = Spyc::YAMLLoad($file);
50 | *
51 | * or:
52 | *
53 | * $array = spyc_load_file($file);
54 | *
55 | * @package Spyc
56 | */
57 | class Spyc {
58 |
59 | // SETTINGS
60 |
61 | /**
62 | * Setting this to true will force YAMLDump to enclose any string value in
63 | * quotes. False by default.
64 | *
65 | * @var bool
66 | */
67 | public $setting_dump_force_quotes = false;
68 |
69 | /**
70 | * Setting this to true will forse YAMLLoad to use syck_load function when
71 | * possible. False by default.
72 | * @var bool
73 | */
74 | public $setting_use_syck_is_possible = false;
75 |
76 |
77 |
78 | /**#@+
79 | * @access private
80 | * @var mixed
81 | */
82 | private $_dumpIndent;
83 | private $_dumpWordWrap;
84 | private $_containsGroupAnchor = false;
85 | private $_containsGroupAlias = false;
86 | private $path;
87 | private $result;
88 | private $LiteralPlaceHolder = '___YAML_Literal_Block___';
89 | private $SavedGroups = array();
90 | private $indent;
91 | /**
92 | * Path modifier that should be applied after adding current element.
93 | * @var array
94 | */
95 | private $delayedPath = array();
96 |
97 | /**#@+
98 | * @access public
99 | * @var mixed
100 | */
101 | public $_nodeId;
102 |
103 | /**
104 | * Load a valid YAML string to Spyc.
105 | * @param string $input
106 | * @return array
107 | */
108 | public function load ($input) {
109 | return $this->__loadString($input);
110 | }
111 |
112 | /**
113 | * Load a valid YAML file to Spyc.
114 | * @param string $file
115 | * @return array
116 | */
117 | public function loadFile ($file) {
118 | return $this->__load($file);
119 | }
120 |
121 | /**
122 | * Load YAML into a PHP array statically
123 | *
124 | * The load method, when supplied with a YAML stream (string or file),
125 | * will do its best to convert YAML in a file into a PHP array. Pretty
126 | * simple.
127 | * Usage:
128 | *
129 | * $array = Spyc::YAMLLoad('lucky.yaml');
130 | * print_r($array);
131 | *
132 | * @access public
133 | * @return array
134 | * @param string $input Path of YAML file or string containing YAML
135 | */
136 | public static function YAMLLoad($input) {
137 | $Spyc = new Spyc;
138 | return $Spyc->__load($input);
139 | }
140 |
141 | /**
142 | * Load a string of YAML into a PHP array statically
143 | *
144 | * The load method, when supplied with a YAML string, will do its best
145 | * to convert YAML in a string into a PHP array. Pretty simple.
146 | *
147 | * Note: use this function if you don't want files from the file system
148 | * loaded and processed as YAML. This is of interest to people concerned
149 | * about security whose input is from a string.
150 | *
151 | * Usage:
152 | *
153 | * $array = Spyc::YAMLLoadString("---\n0: hello world\n");
154 | * print_r($array);
155 | *
156 | * @access public
157 | * @return array
158 | * @param string $input String containing YAML
159 | */
160 | public static function YAMLLoadString($input) {
161 | $Spyc = new Spyc;
162 | return $Spyc->__loadString($input);
163 | }
164 |
165 | /**
166 | * Dump YAML from PHP array statically
167 | *
168 | * The dump method, when supplied with an array, will do its best
169 | * to convert the array into friendly YAML. Pretty simple. Feel free to
170 | * save the returned string as nothing.yaml and pass it around.
171 | *
172 | * Oh, and you can decide how big the indent is and what the wordwrap
173 | * for folding is. Pretty cool -- just pass in 'false' for either if
174 | * you want to use the default.
175 | *
176 | * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
177 | * you can turn off wordwrap by passing in 0.
178 | *
179 | * @access public
180 | * @return string
181 | * @param array $array PHP array
182 | * @param int $indent Pass in false to use the default, which is 2
183 | * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
184 | */
185 | public static function YAMLDump($array,$indent = false,$wordwrap = false) {
186 | $spyc = new Spyc;
187 | return $spyc->dump($array,$indent,$wordwrap);
188 | }
189 |
190 |
191 | /**
192 | * Dump PHP array to YAML
193 | *
194 | * The dump method, when supplied with an array, will do its best
195 | * to convert the array into friendly YAML. Pretty simple. Feel free to
196 | * save the returned string as tasteful.yaml and pass it around.
197 | *
198 | * Oh, and you can decide how big the indent is and what the wordwrap
199 | * for folding is. Pretty cool -- just pass in 'false' for either if
200 | * you want to use the default.
201 | *
202 | * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
203 | * you can turn off wordwrap by passing in 0.
204 | *
205 | * @access public
206 | * @return string
207 | * @param array $array PHP array
208 | * @param int $indent Pass in false to use the default, which is 2
209 | * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
210 | */
211 | public function dump($array,$indent = false,$wordwrap = false) {
212 | // Dumps to some very clean YAML. We'll have to add some more features
213 | // and options soon. And better support for folding.
214 |
215 | // New features and options.
216 | if ($indent === false or !is_numeric($indent)) {
217 | $this->_dumpIndent = 2;
218 | } else {
219 | $this->_dumpIndent = $indent;
220 | }
221 |
222 | if ($wordwrap === false or !is_numeric($wordwrap)) {
223 | $this->_dumpWordWrap = 40;
224 | } else {
225 | $this->_dumpWordWrap = $wordwrap;
226 | }
227 |
228 | // New YAML document
229 | $string = "---\n";
230 |
231 | // Start at the base of the array and move through it.
232 | if ($array) {
233 | $array = (array)$array;
234 | $previous_key = -1;
235 | foreach ($array as $key => $value) {
236 | if (!isset($first_key)) $first_key = $key;
237 | $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array);
238 | $previous_key = $key;
239 | }
240 | }
241 | return $string;
242 | }
243 |
244 | /**
245 | * Attempts to convert a key / value array item to YAML
246 | * @access private
247 | * @return string
248 | * @param $key The name of the key
249 | * @param $value The value of the item
250 | * @param $indent The indent of the current node
251 | */
252 | private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) {
253 | if (is_array($value)) {
254 | if (empty ($value))
255 | return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array);
256 | // It has children. What to do?
257 | // Make it the right kind of item
258 | $string = $this->_dumpNode($key, NULL, $indent, $previous_key, $first_key, $source_array);
259 | // Add the indent
260 | $indent += $this->_dumpIndent;
261 | // Yamlize the array
262 | $string .= $this->_yamlizeArray($value,$indent);
263 | } elseif (!is_array($value)) {
264 | // It doesn't have children. Yip.
265 | $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array);
266 | }
267 | return $string;
268 | }
269 |
270 | /**
271 | * Attempts to convert an array to YAML
272 | * @access private
273 | * @return string
274 | * @param $array The array you want to convert
275 | * @param $indent The indent of the current level
276 | */
277 | private function _yamlizeArray($array,$indent) {
278 | if (is_array($array)) {
279 | $string = '';
280 | $previous_key = -1;
281 | foreach ($array as $key => $value) {
282 | if (!isset($first_key)) $first_key = $key;
283 | $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array);
284 | $previous_key = $key;
285 | }
286 | return $string;
287 | } else {
288 | return false;
289 | }
290 | }
291 |
292 | /**
293 | * Returns YAML from a key and a value
294 | * @access private
295 | * @return string
296 | * @param $key The name of the key
297 | * @param $value The value of the item
298 | * @param $indent The indent of the current node
299 | */
300 | private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) {
301 | // do some folding here, for blocks
302 | if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
303 | strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, ' ') !== false ||
304 | strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 ||
305 | substr ($value, -1, 1) == ':')
306 | ) {
307 | $value = $this->_doLiteralBlock($value,$indent);
308 | } else {
309 | $value = $this->_doFolding($value,$indent);
310 | }
311 |
312 | if ($value === array()) $value = '[ ]';
313 | if (in_array ($value, array ('true', 'TRUE', 'false', 'FALSE', 'y', 'Y', 'n', 'N', 'null', 'NULL'), true)) {
314 | $value = $this->_doLiteralBlock($value,$indent);
315 | }
316 | if (trim ($value) != $value)
317 | $value = $this->_doLiteralBlock($value,$indent);
318 |
319 | if (is_bool($value)) {
320 | $value = ($value) ? "true" : "false";
321 | }
322 |
323 | $spaces = str_repeat(' ',$indent);
324 |
325 | //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
326 | if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) {
327 | // It's a sequence
328 | $string = $spaces.'- '.$value."\n";
329 | } else {
330 | // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"');
331 | // It's mapped
332 | if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; }
333 | $string = rtrim ($spaces.$key.': '.$value)."\n";
334 | }
335 | return $string;
336 | }
337 |
338 | /**
339 | * Creates a literal block for dumping
340 | * @access private
341 | * @return string
342 | * @param $value
343 | * @param $indent int The value of the indent
344 | */
345 | private function _doLiteralBlock($value,$indent) {
346 | if ($value === "\n") return '\n';
347 | if (strpos($value, "\n") === false && strpos($value, "'") === false) {
348 | return sprintf ("'%s'", $value);
349 | }
350 | if (strpos($value, "\n") === false && strpos($value, '"') === false) {
351 | return sprintf ('"%s"', $value);
352 | }
353 | $exploded = explode("\n",$value);
354 | $newValue = '|';
355 | $indent += $this->_dumpIndent;
356 | $spaces = str_repeat(' ',$indent);
357 | foreach ($exploded as $line) {
358 | $newValue .= "\n" . $spaces . ($line);
359 | }
360 | return $newValue;
361 | }
362 |
363 | /**
364 | * Folds a string of text, if necessary
365 | * @access private
366 | * @return string
367 | * @param $value The string you wish to fold
368 | */
369 | private function _doFolding($value,$indent) {
370 | // Don't do anything if wordwrap is set to 0
371 |
372 | if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
373 | $indent += $this->_dumpIndent;
374 | $indent = str_repeat(' ',$indent);
375 | $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
376 | $value = ">\n".$indent.$wrapped;
377 | } else {
378 | if ($this->setting_dump_force_quotes && is_string ($value))
379 | $value = '"' . $value . '"';
380 | }
381 |
382 |
383 | return $value;
384 | }
385 |
386 | // LOADING FUNCTIONS
387 |
388 | private function __load($input) {
389 | $Source = $this->loadFromSource($input);
390 | return $this->loadWithSource($Source);
391 | }
392 |
393 | private function __loadString($input) {
394 | $Source = $this->loadFromString($input);
395 | return $this->loadWithSource($Source);
396 | }
397 |
398 | private function loadWithSource($Source) {
399 | if (empty ($Source)) return array();
400 | if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
401 | $array = syck_load (implode ('', $Source));
402 | return is_array($array) ? $array : array();
403 | }
404 |
405 | $this->path = array();
406 | $this->result = array();
407 |
408 | $cnt = count($Source);
409 | for ($i = 0; $i < $cnt; $i++) {
410 | $line = $Source[$i];
411 |
412 | $this->indent = strlen($line) - strlen(ltrim($line));
413 | $tempPath = $this->getParentPathByIndent($this->indent);
414 | $line = self::stripIndent($line, $this->indent);
415 | if (self::isComment($line)) continue;
416 | if (self::isEmpty($line)) continue;
417 | $this->path = $tempPath;
418 |
419 | $literalBlockStyle = self::startsLiteralBlock($line);
420 | if ($literalBlockStyle) {
421 | $line = rtrim ($line, $literalBlockStyle . " \n");
422 | $literalBlock = '';
423 | $line .= $this->LiteralPlaceHolder;
424 |
425 | while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
426 | $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle);
427 | }
428 | $i--;
429 | }
430 |
431 | while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
432 | $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
433 | }
434 | $i--;
435 |
436 |
437 |
438 | if (strpos ($line, '#')) {
439 | if (strpos ($line, '"') === false && strpos ($line, "'") === false)
440 | $line = preg_replace('/\s+#(.+)$/','',$line);
441 | }
442 |
443 | $lineArray = $this->_parseLine($line);
444 |
445 | if ($literalBlockStyle)
446 | $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
447 |
448 | $this->addArray($lineArray, $this->indent);
449 |
450 | foreach ($this->delayedPath as $indent => $delayedPath)
451 | $this->path[$indent] = $delayedPath;
452 |
453 | $this->delayedPath = array();
454 |
455 | }
456 | return $this->result;
457 | }
458 |
459 | private function loadFromSource ($input) {
460 | if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
461 | return file($input);
462 |
463 | return $this->loadFromString($input);
464 | }
465 |
466 | private function loadFromString ($input) {
467 | $lines = explode("\n",$input);
468 | foreach ($lines as $k => $_) {
469 | $lines[$k] = rtrim ($_, "\r");
470 | }
471 | return $lines;
472 | }
473 |
474 | /**
475 | * Parses YAML code and returns an array for a node
476 | * @access private
477 | * @return array
478 | * @param string $line A line from the YAML file
479 | */
480 | private function _parseLine($line) {
481 | if (!$line) return array();
482 | $line = trim($line);
483 | if (!$line) return array();
484 |
485 | $array = array();
486 |
487 | $group = $this->nodeContainsGroup($line);
488 | if ($group) {
489 | $this->addGroup($line, $group);
490 | $line = $this->stripGroup ($line, $group);
491 | }
492 |
493 | if ($this->startsMappedSequence($line))
494 | return $this->returnMappedSequence($line);
495 |
496 | if ($this->startsMappedValue($line))
497 | return $this->returnMappedValue($line);
498 |
499 | if ($this->isArrayElement($line))
500 | return $this->returnArrayElement($line);
501 |
502 | if ($this->isPlainArray($line))
503 | return $this->returnPlainArray($line);
504 |
505 |
506 | return $this->returnKeyValuePair($line);
507 |
508 | }
509 |
510 | /**
511 | * Finds the type of the passed value, returns the value as the new type.
512 | * @access private
513 | * @param string $value
514 | * @return mixed
515 | */
516 | private function _toType($value) {
517 | if ($value === '') return null;
518 | $first_character = $value[0];
519 | $last_character = substr($value, -1, 1);
520 |
521 | $is_quoted = false;
522 | do {
523 | if (!$value) break;
524 | if ($first_character != '"' && $first_character != "'") break;
525 | if ($last_character != '"' && $last_character != "'") break;
526 | $is_quoted = true;
527 | } while (0);
528 |
529 | if ($is_quoted)
530 | return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\''));
531 |
532 | if (strpos($value, ' #') !== false && !$is_quoted)
533 | $value = preg_replace('/\s+#(.+)$/','',$value);
534 |
535 | if (!$is_quoted) $value = str_replace('\n', "\n", $value);
536 |
537 | if ($first_character == '[' && $last_character == ']') {
538 | // Take out strings sequences and mappings
539 | $innerValue = trim(substr ($value, 1, -1));
540 | if ($innerValue === '') return array();
541 | $explode = $this->_inlineEscape($innerValue);
542 | // Propagate value array
543 | $value = array();
544 | foreach ($explode as $v) {
545 | $value[] = $this->_toType($v);
546 | }
547 | return $value;
548 | }
549 |
550 | if (strpos($value,': ')!==false && $first_character != '{') {
551 | $array = explode(': ',$value);
552 | $key = trim($array[0]);
553 | array_shift($array);
554 | $value = trim(implode(': ',$array));
555 | $value = $this->_toType($value);
556 | return array($key => $value);
557 | }
558 |
559 | if ($first_character == '{' && $last_character == '}') {
560 | $innerValue = trim(substr ($value, 1, -1));
561 | if ($innerValue === '') return array();
562 | // Inline Mapping
563 | // Take out strings sequences and mappings
564 | $explode = $this->_inlineEscape($innerValue);
565 | // Propagate value array
566 | $array = array();
567 | foreach ($explode as $v) {
568 | $SubArr = $this->_toType($v);
569 | if (empty($SubArr)) continue;
570 | if (is_array ($SubArr)) {
571 | $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
572 | }
573 | $array[] = $SubArr;
574 | }
575 | return $array;
576 | }
577 |
578 | if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
579 | return null;
580 | }
581 |
582 | if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){
583 | $intvalue = (int)$value;
584 | if ($intvalue != PHP_INT_MAX)
585 | $value = $intvalue;
586 | return $value;
587 | }
588 |
589 | if (in_array($value,
590 | array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) {
591 | return true;
592 | }
593 |
594 | if (in_array(strtolower($value),
595 | array('false', 'off', '-', 'no', 'n'))) {
596 | return false;
597 | }
598 |
599 | if (is_numeric($value)) {
600 | if ($value === '0') return 0;
601 | if (rtrim ($value, 0) === $value)
602 | $value = (float)$value;
603 | return $value;
604 | }
605 |
606 | return $value;
607 | }
608 |
609 | /**
610 | * Used in inlines to check for more inlines or quoted strings
611 | * @access private
612 | * @return array
613 | */
614 | private function _inlineEscape($inline) {
615 | // There's gotta be a cleaner way to do this...
616 | // While pure sequences seem to be nesting just fine,
617 | // pure mappings and mappings with sequences inside can't go very
618 | // deep. This needs to be fixed.
619 |
620 | $seqs = array();
621 | $maps = array();
622 | $saved_strings = array();
623 |
624 | // Check for strings
625 | $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
626 | if (preg_match_all($regex,$inline,$strings)) {
627 | $saved_strings = $strings[0];
628 | $inline = preg_replace($regex,'YAMLString',$inline);
629 | }
630 | unset($regex);
631 |
632 | $i = 0;
633 | do {
634 |
635 | // Check for sequences
636 | while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
637 | $seqs[] = $matchseqs[0];
638 | $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
639 | }
640 |
641 | // Check for mappings
642 | while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
643 | $maps[] = $matchmaps[0];
644 | $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
645 | }
646 |
647 | if ($i++ >= 10) break;
648 |
649 | } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
650 |
651 | $explode = explode(', ',$inline);
652 | $stringi = 0; $i = 0;
653 |
654 | while (1) {
655 |
656 | // Re-add the sequences
657 | if (!empty($seqs)) {
658 | foreach ($explode as $key => $value) {
659 | if (strpos($value,'YAMLSeq') !== false) {
660 | foreach ($seqs as $seqk => $seq) {
661 | $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
662 | $value = $explode[$key];
663 | }
664 | }
665 | }
666 | }
667 |
668 | // Re-add the mappings
669 | if (!empty($maps)) {
670 | foreach ($explode as $key => $value) {
671 | if (strpos($value,'YAMLMap') !== false) {
672 | foreach ($maps as $mapk => $map) {
673 | $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
674 | $value = $explode[$key];
675 | }
676 | }
677 | }
678 | }
679 |
680 |
681 | // Re-add the strings
682 | if (!empty($saved_strings)) {
683 | foreach ($explode as $key => $value) {
684 | while (strpos($value,'YAMLString') !== false) {
685 | $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
686 | unset($saved_strings[$stringi]);
687 | ++$stringi;
688 | $value = $explode[$key];
689 | }
690 | }
691 | }
692 |
693 | $finished = true;
694 | foreach ($explode as $key => $value) {
695 | if (strpos($value,'YAMLSeq') !== false) {
696 | $finished = false; break;
697 | }
698 | if (strpos($value,'YAMLMap') !== false) {
699 | $finished = false; break;
700 | }
701 | if (strpos($value,'YAMLString') !== false) {
702 | $finished = false; break;
703 | }
704 | }
705 | if ($finished) break;
706 |
707 | $i++;
708 | if ($i > 10)
709 | break; // Prevent infinite loops.
710 | }
711 |
712 | return $explode;
713 | }
714 |
715 | private function literalBlockContinues ($line, $lineIndent) {
716 | if (!trim($line)) return true;
717 | if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
718 | return false;
719 | }
720 |
721 | private function referenceContentsByAlias ($alias) {
722 | do {
723 | if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
724 | $groupPath = $this->SavedGroups[$alias];
725 | $value = $this->result;
726 | foreach ($groupPath as $k) {
727 | $value = $value[$k];
728 | }
729 | } while (false);
730 | return $value;
731 | }
732 |
733 | private function addArrayInline ($array, $indent) {
734 | $CommonGroupPath = $this->path;
735 | if (empty ($array)) return false;
736 |
737 | foreach ($array as $k => $_) {
738 | $this->addArray(array($k => $_), $indent);
739 | $this->path = $CommonGroupPath;
740 | }
741 | return true;
742 | }
743 |
744 | private function addArray ($incoming_data, $incoming_indent) {
745 |
746 | // print_r ($incoming_data);
747 |
748 | if (count ($incoming_data) > 1)
749 | return $this->addArrayInline ($incoming_data, $incoming_indent);
750 |
751 | $key = key ($incoming_data);
752 | $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
753 | if ($key === '__!YAMLZero') $key = '0';
754 |
755 | if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
756 | if ($key || $key === '' || $key === '0') {
757 | $this->result[$key] = $value;
758 | } else {
759 | $this->result[] = $value; end ($this->result); $key = key ($this->result);
760 | }
761 | $this->path[$incoming_indent] = $key;
762 | return;
763 | }
764 |
765 |
766 |
767 | $history = array();
768 | // Unfolding inner array tree.
769 | $history[] = $_arr = $this->result;
770 | foreach ($this->path as $k) {
771 | $history[] = $_arr = $_arr[$k];
772 | }
773 |
774 | if ($this->_containsGroupAlias) {
775 | $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
776 | $this->_containsGroupAlias = false;
777 | }
778 |
779 |
780 | // Adding string or numeric key to the innermost level or $this->arr.
781 | if (is_string($key) && $key == '<<') {
782 | if (!is_array ($_arr)) { $_arr = array (); }
783 |
784 | $_arr = array_merge ($_arr, $value);
785 | } else if ($key || $key === '' || $key === '0') {
786 | if (!is_array ($_arr))
787 | $_arr = array ($key=>$value);
788 | else
789 | $_arr[$key] = $value;
790 | } else {
791 | if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
792 | else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
793 | }
794 |
795 | $reverse_path = array_reverse($this->path);
796 | $reverse_history = array_reverse ($history);
797 | $reverse_history[0] = $_arr;
798 | $cnt = count($reverse_history) - 1;
799 | for ($i = 0; $i < $cnt; $i++) {
800 | $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
801 | }
802 | $this->result = $reverse_history[$cnt];
803 |
804 | $this->path[$incoming_indent] = $key;
805 |
806 | if ($this->_containsGroupAnchor) {
807 | $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
808 | if (is_array ($value)) {
809 | $k = key ($value);
810 | if (!is_int ($k)) {
811 | $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
812 | }
813 | }
814 | $this->_containsGroupAnchor = false;
815 | }
816 |
817 | }
818 |
819 | private static function startsLiteralBlock ($line) {
820 | $lastChar = substr (trim($line), -1);
821 | if ($lastChar != '>' && $lastChar != '|') return false;
822 | if ($lastChar == '|') return $lastChar;
823 | // HTML tags should not be counted as literal blocks.
824 | if (preg_match ('#<.*?>$#', $line)) return false;
825 | return $lastChar;
826 | }
827 |
828 | private static function greedilyNeedNextLine($line) {
829 | $line = trim ($line);
830 | if (!strlen($line)) return false;
831 | if (substr ($line, -1, 1) == ']') return false;
832 | if ($line[0] == '[') return true;
833 | if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
834 | return false;
835 | }
836 |
837 | private function addLiteralLine ($literalBlock, $line, $literalBlockStyle) {
838 | $line = self::stripIndent($line);
839 | $line = rtrim ($line, "\r\n\t ") . "\n";
840 | if ($literalBlockStyle == '|') {
841 | return $literalBlock . $line;
842 | }
843 | if (strlen($line) == 0)
844 | return rtrim($literalBlock, ' ') . "\n";
845 | if ($line == "\n" && $literalBlockStyle == '>') {
846 | return rtrim ($literalBlock, " \t") . "\n";
847 | }
848 | if ($line != "\n")
849 | $line = trim ($line, "\r\n ") . " ";
850 | return $literalBlock . $line;
851 | }
852 |
853 | function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
854 | foreach ($lineArray as $k => $_) {
855 | if (is_array($_))
856 | $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
857 | else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
858 | $lineArray[$k] = rtrim ($literalBlock, " \r\n");
859 | }
860 | return $lineArray;
861 | }
862 |
863 | private static function stripIndent ($line, $indent = -1) {
864 | if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
865 | return substr ($line, $indent);
866 | }
867 |
868 | private function getParentPathByIndent ($indent) {
869 | if ($indent == 0) return array();
870 | $linePath = $this->path;
871 | do {
872 | end($linePath); $lastIndentInParentPath = key($linePath);
873 | if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
874 | } while ($indent <= $lastIndentInParentPath);
875 | return $linePath;
876 | }
877 |
878 |
879 | private function clearBiggerPathValues ($indent) {
880 |
881 |
882 | if ($indent == 0) $this->path = array();
883 | if (empty ($this->path)) return true;
884 |
885 | foreach ($this->path as $k => $_) {
886 | if ($k > $indent) unset ($this->path[$k]);
887 | }
888 |
889 | return true;
890 | }
891 |
892 |
893 | private static function isComment ($line) {
894 | if (!$line) return false;
895 | if ($line[0] == '#') return true;
896 | if (trim($line, " \r\n\t") == '---') return true;
897 | return false;
898 | }
899 |
900 | private static function isEmpty ($line) {
901 | return (trim ($line) === '');
902 | }
903 |
904 |
905 | private function isArrayElement ($line) {
906 | if (!$line) return false;
907 | if ($line[0] != '-') return false;
908 | if (strlen ($line) > 3)
909 | if (substr($line,0,3) == '---') return false;
910 |
911 | return true;
912 | }
913 |
914 | private function isHashElement ($line) {
915 | return strpos($line, ':');
916 | }
917 |
918 | private function isLiteral ($line) {
919 | if ($this->isArrayElement($line)) return false;
920 | if ($this->isHashElement($line)) return false;
921 | return true;
922 | }
923 |
924 |
925 | private static function unquote ($value) {
926 | if (!$value) return $value;
927 | if (!is_string($value)) return $value;
928 | if ($value[0] == '\'') return trim ($value, '\'');
929 | if ($value[0] == '"') return trim ($value, '"');
930 | return $value;
931 | }
932 |
933 | private function startsMappedSequence ($line) {
934 | return ($line[0] == '-' && substr ($line, -1, 1) == ':');
935 | }
936 |
937 | private function returnMappedSequence ($line) {
938 | $array = array();
939 | $key = self::unquote(trim(substr($line,1,-1)));
940 | $array[$key] = array();
941 | $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
942 | return array($array);
943 | }
944 |
945 | private function returnMappedValue ($line) {
946 | $array = array();
947 | $key = self::unquote (trim(substr($line,0,-1)));
948 | $array[$key] = '';
949 | return $array;
950 | }
951 |
952 | private function startsMappedValue ($line) {
953 | return (substr ($line, -1, 1) == ':');
954 | }
955 |
956 | private function isPlainArray ($line) {
957 | return ($line[0] == '[' && substr ($line, -1, 1) == ']');
958 | }
959 |
960 | private function returnPlainArray ($line) {
961 | return $this->_toType($line);
962 | }
963 |
964 | private function returnKeyValuePair ($line) {
965 | $array = array();
966 | $key = '';
967 | if (strpos ($line, ':')) {
968 | // It's a key/value pair most likely
969 | // If the key is in double quotes pull it out
970 | if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
971 | $value = trim(str_replace($matches[1],'',$line));
972 | $key = $matches[2];
973 | } else {
974 | // Do some guesswork as to the key and the value
975 | $explode = explode(':',$line);
976 | $key = trim($explode[0]);
977 | array_shift($explode);
978 | $value = trim(implode(':',$explode));
979 | }
980 | // Set the type of the value. Int, string, etc
981 | $value = $this->_toType($value);
982 | if ($key === '0') $key = '__!YAMLZero';
983 | $array[$key] = $value;
984 | } else {
985 | $array = array ($line);
986 | }
987 | return $array;
988 |
989 | }
990 |
991 |
992 | private function returnArrayElement ($line) {
993 | if (strlen($line) <= 1) return array(array()); // Weird %)
994 | $array = array();
995 | $value = trim(substr($line,1));
996 | $value = $this->_toType($value);
997 | $array[] = $value;
998 | return $array;
999 | }
1000 |
1001 |
1002 | private function nodeContainsGroup ($line) {
1003 | $symbolsForReference = 'A-z0-9_\-';
1004 | if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
1005 | if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
1006 | if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
1007 | if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
1008 | if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
1009 | if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
1010 | return false;
1011 |
1012 | }
1013 |
1014 | private function addGroup ($line, $group) {
1015 | if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
1016 | if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
1017 | //print_r ($this->path);
1018 | }
1019 |
1020 | private function stripGroup ($line, $group) {
1021 | $line = trim(str_replace($group, '', $line));
1022 | return $line;
1023 | }
1024 | }
1025 |
1026 | // Enable use of Spyc from command line
1027 | // The syntax is the following: php spyc.php spyc.yaml
1028 |
1029 | define ('SPYC_FROM_COMMAND_LINE', false);
1030 |
1031 | do {
1032 | if (!SPYC_FROM_COMMAND_LINE) break;
1033 | if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
1034 | if (empty ($_SERVER['PHP_SELF']) || $_SERVER['PHP_SELF'] != 'spyc.php') break;
1035 | $file = $argv[1];
1036 | printf ("Spyc loading file: %s\n", $file);
1037 | print_r (spyc_load_file ($file));
1038 | } while (0);
--------------------------------------------------------------------------------