├── 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 | - hello
world 7 | text_content: 8 | - hello world -------------------------------------------------------------------------------- /examples/yaml-load.php: -------------------------------------------------------------------------------- 1 | spyc.yaml loaded into PHP:
'; 19 | print_r($array); 20 | echo ''; 21 | 22 | 23 | echo '
YAML Data dumped back:
'; 24 | echo Spyc::YAMLDump($array); 25 | echo '
'; 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: "" 143 | 144 | array_of_zero: [0] 145 | sophisticated_array_of_zero: {rx: {tx: [0]} } 146 | 147 | switches: 148 | - { row: 0, col: 0, func: {tx: [0, 1]} } 149 | 150 | empty_sequence: [ ] 151 | empty_hash: { } 152 | 153 | special_characters: "[{]]{{]]" 154 | 155 | asterisks: "*" 156 | 157 | empty_key: 158 | : 159 | key: value 160 | 161 | trailing_colon: "foo:" 162 | 163 | multiline_items: 164 | - type: SomeItem 165 | values: [blah, blah, blah, 166 | blah] 167 | ints: [2, 54, 12, 168 | 2143] 169 | 170 | many_lines: | 171 | A quick 172 | fox 173 | 174 | 175 | jumped 176 | over 177 | 178 | 179 | 180 | 181 | 182 | a lazy 183 | 184 | 185 | 186 | dog 187 | 188 | 189 | werte: 190 | 1: nummer 1 191 | 0: Stunde 0 192 | 193 | noindent_records: 194 | - record1: value1 195 | - record2: value2 196 | 197 | "a:1": [1000] 198 | "a:2": 199 | - 2000 200 | 201 | # [Endloop] 202 | endloop: | 203 | Does this line in the end indeed make Spyc go to an infinite loop? -------------------------------------------------------------------------------- /php4/test.php4: -------------------------------------------------------------------------------- 1 | "1.5ghz", "ram" => "1 gig", 71 | "os" => "os x 10.4.1")) 72 | die('Sequence 4 failed'); 73 | 74 | # Mapped sequence 75 | if ($yaml['domains'] != array("yaml.org", "php.net")) 76 | die("Key: 'domains' failed"); 77 | 78 | # A sequence like this. 79 | if ($yaml[5] != array("program" => "Adium", "platform" => "OS X", 80 | "type" => "Chat Client")) 81 | die('Sequence 5 failed'); 82 | 83 | # A folded block as a mapped value 84 | if ($yaml['no time'] != "There isn't any time for your tricks!\nDo you understand?") 85 | die("Key: 'no time' failed"); 86 | 87 | # A literal block as a mapped value 88 | if ($yaml['some time'] != "There is nothing but time\nfor your tricks.") 89 | die("Key: 'some time' failed"); 90 | 91 | # Crazy combinations 92 | if ($yaml['databases'] != array( array("name" => "spartan", "notes" => 93 | array( "Needs to be backed up", 94 | "Needs to be normalized" ), 95 | "type" => "mysql" ))) 96 | die("Key: 'databases' failed"); 97 | 98 | # You can be a bit tricky 99 | if ($yaml["if: you'd"] != "like") 100 | die("Key: 'if: you\'d' failed"); 101 | 102 | # Inline sequences 103 | if ($yaml[6] != array("One", "Two", "Three", "Four")) 104 | die("Sequence 6 failed"); 105 | 106 | # Nested Inline Sequences 107 | if ($yaml[7] != array("One", array("Two", "And", "Three"), "Four", "Five")) 108 | die("Sequence 7 failed"); 109 | 110 | # Nested Nested Inline Sequences 111 | if ($yaml[8] != array( "This", array("Is", "Getting", array("Ridiculous", "Guys")), 112 | "Seriously", array("Show", "Mercy"))) 113 | die("Sequence 8 failed"); 114 | 115 | # Inline mappings 116 | if ($yaml[9] != array("name" => "chris", "age" => "young", "brand" => "lucky strike")) 117 | die("Sequence 9 failed"); 118 | 119 | # Nested inline mappings 120 | if ($yaml[10] != array("name" => "mark", "age" => "older than chris", 121 | "brand" => array("marlboro", "lucky strike"))) 122 | die("Sequence 10 failed"); 123 | 124 | # References -- they're shaky, but functional 125 | if ($yaml['dynamic languages'] != array('Perl', 'Python', 'PHP', 'Ruby')) 126 | die("Key: 'dynamic languages' failed"); 127 | 128 | if ($yaml['compiled languages'] != array('C/C++', 'Java')) 129 | die("Key: 'compiled languages' failed"); 130 | 131 | if ($yaml['all languages'] != array( 132 | array('Perl', 'Python', 'PHP', 'Ruby'), 133 | array('C/C++', 'Java') 134 | )) 135 | die("Key: 'all languages' failed"); 136 | 137 | # Added in .2.2: Escaped quotes 138 | if ($yaml[11] != "you know, this shouldn't work. but it does.") 139 | die("Sequence 11 failed."); 140 | 141 | if ($yaml[12] != "that's my value.") 142 | die("Sequence 12 failed."); 143 | 144 | if ($yaml[13] != "again, that's my value.") 145 | die("Sequence 13 failed."); 146 | 147 | if ($yaml[14] != "here's to \"quotes\", boss.") 148 | die("Sequence 14 failed."); 149 | 150 | if ($yaml[15] != array( 'name' => "Foo, Bar's", 'age' => 20)) 151 | die("Sequence 15 failed."); 152 | 153 | if ($yaml[16] != array( 0 => "a", 1 => array (0 => 1, 1 => 2), 2 => "b")) 154 | die("Sequence 16 failed."); 155 | 156 | if ($yaml['endloop'] != "Does this line in the end indeed make Spyc go to an infinite loop?") 157 | die("[endloop] failed."); 158 | 159 | 160 | print "spyc.yaml parsed correctly\n"; 161 | 162 | ?> -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # 2 | # S P Y C 3 | # a simple php yaml class 4 | # 5 | # Load this README! 6 | # >> $readme = Spyc::YAMLLoad('README'); 7 | # 8 | --- %YAML:1.1 9 | title: Spyc -- a Simple PHP YAML Class 10 | version: 0.4.5 11 | authors: [chris wanstrath (chris@ozmm.org), vlad andersen (vlad.andersen@gmail.com)] 12 | websites: [http://www.yaml.org, http://spyc.sourceforge.net] 13 | license: [MIT License, http://www.opensource.org/licenses/mit-license.php] 14 | copyright: "(c) 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen" 15 | tested on: [php 5.2.x] 16 | 17 | installation: > 18 | Copy spyc.php to a directory you can 19 | access with your YAML-ready PHP script. 20 | 21 | That's it! 22 | 23 | about: > 24 | From www.yaml.org: 25 | 26 | "YAML(tm) (rhymes with 'camel') is a human-friendly, cross language, 27 | Unicode based data serialization language designed around the common 28 | native data structures of agile programming languages. It is broadly 29 | useful for programming needs ranging from configuration files to 30 | Internet messaging to object persistence to data auditing. Together 31 | with the Unicode standard for characters, the YAML specification provides 32 | all the information necessary to understand YAML Version 1.1 and to 33 | creating programs that process YAML information. 34 | 35 | YAML(tm) is a balance of the following design goals: 36 | - YAML documents are very readable by humans. 37 | - YAML interacts well with scripting languages. 38 | - YAML uses host languages' native data structures. 39 | - YAML has a consistent information model. 40 | - YAML enables stream-based processing. 41 | - YAML is expressive and extensible. 42 | - YAML is easy to implement." 43 | 44 | YAML makes a lot of sense. It's easy to use, easy to learn, and cool. 45 | As the lucky stiff named why once said, "YAML is a beacon of light." 46 | 47 | If you're new to YAML, may we suggest YAML In Five Minutes: 48 | - http://yaml.kwiki.org/?YamlInFiveMinutes 49 | 50 | If you don't have five minutes, realize that this README is a completely 51 | valid YAML document. Dig in, load this or any YAML file into an array 52 | with Spyc and see how easy it is to translate friendly text into usable 53 | data. 54 | 55 | The purpose of Spyc is to provide a pure PHP alternative to Syck, a 56 | simple API for loading and dumping YAML documents, a YAML loader which 57 | understands a usable subset of the YAML spec, and to further spread 58 | the glory of YAML to the PHP masses. 59 | 60 | If you're at all hesitant ("usable subset of YAML?!"), navigate 61 | http://yaml.org/start.html. Spyc completely understands the YAML 62 | document shown there, a document which has features way beyond the 63 | scope of what normal config files might require. Try it for yourself, 64 | and then start enjoying the peace of mind YAML brings to your life. 65 | 66 | meat and a few potatoes: 67 | - concept: Loading a YAML document into PHP 68 | brief: > 69 | $yaml will become an array of all the data in wicked.yaml 70 | code: | 71 | 72 | include('spyc.php'); 73 | 74 | $yaml = Spyc::YAMLLoad('wicked.yaml'); 75 | 76 | - concept: Loading a YAML string into PHP 77 | brief: > 78 | $array will look like this: 79 | array('A YAML','document in a','string') 80 | code: | 81 | 82 | include('spyc.php'); 83 | 84 | $yaml = '- A YAML\n- document in a\n- string.'; 85 | $array = Spyc::YAMLLoad($yaml); 86 | 87 | - concept: Dumping a PHP array to YAML 88 | brief: > 89 | $yaml will become a string of a YAML document created from 90 | $array. 91 | code: | 92 | 93 | include('spyc.php'); 94 | 95 | $array['name'] = 'chris'; 96 | $array['sport'] = 'curbing'; 97 | 98 | $yaml = Spyc::YAMLDump($array); 99 | 100 | prior art: 101 | - who: [Brian Ingerson, Clark Evans, Oren Ben-Kiki] 102 | why?: > 103 | The YAML spec is really a piece of work, and these guys 104 | did a great job on it. A simple and elegant language like 105 | YAML was a long time coming and it's refreshing to know 106 | such able minded individuals took the task to heart and 107 | executed it with cunning and strength. In addition to 108 | their various noteworthy contributions to YAML parsers 109 | and related projects, YAML.pm's README is a treasure trove 110 | of information for knowledge seekers. Thanks, guys. 111 | 112 | - who: why the lucky stiff 113 | why?: > 114 | As the author of Syck, the code used in Ruby for the language's 115 | YAML class and methods, why is indirectly (directly?) responsible 116 | for my first exposure to YAML (as a config file in a Ruby web-app) 117 | and the countless hours I spent playing with this sheik new data 118 | format afterwards. Syck's README is a YAML file and thus the 119 | inspiration for this file and, even, this very piece of software. 120 | 121 | - who: Steve Howell 122 | why?: > 123 | Python's YAML implementation. PyYAML's README file is also YAML, 124 | so it too inspired the YAML format of this README file. 125 | 126 | - who: [Rasmus Lerdorf, Zeev Suraski, Andi Gutmans, et al] 127 | why?: > 128 | PHP is great at what it does best. It's also paid a lot of my bills. 129 | Thanks. 130 | 131 | bugs: 132 | report: > 133 | Please see Spyc's Sourceforge project page for information on reporting bugs. 134 | speed: > 135 | This implementation was not designed for speed. Rather, it 136 | was designed for those who need a pure PHP implementation of 137 | a YAML parser and who are not overly concerned with performance. 138 | If you want speed, check out Syck. 139 | depth: > 140 | This parser is by no means a comprehensive YAML parser. For supported 141 | features and future plans, check the website. 142 | unicode: > 143 | YAML is supposed to be unicode, but for now we're just using ASCII. 144 | PHP has crappy unicode support but who knows what the future holds. 145 | 146 | resources: 147 | - http://www.yaml.org 148 | - http://www.yaml.org/spec/ 149 | - http://yaml.kwiki.org/?YamlInFiveMinutes 150 | - http://www.whytheluckystiff.net/syck/ 151 | - http://yaml4r.sourceforge.net/cookbook/ 152 | 153 | thanks: 154 | - Adam Wood 155 | - Daniel Ferreira 156 | - Aaron Jensen 157 | - Mike Thornton 158 | - Fabien Potencier 159 | - Mustafa Kumas -------------------------------------------------------------------------------- /tests/ParseTest.php: -------------------------------------------------------------------------------- 1 | yaml = spyc_load_file('../spyc.yaml'); 12 | } 13 | 14 | public function testMergeHashKeys() { 15 | $Expected = array ( 16 | array ('step' => array('instrument' => 'Lasik 2000', 'pulseEnergy' => 5.4, 'pulseDuration' => 12, 'repetition' => 1000, 'spotSize' => '1mm')), 17 | array ('step' => array('instrument' => 'Lasik 2000', 'pulseEnergy' => 5.4, 'pulseDuration' => 12, 'repetition' => 1000, 'spotSize' => '2mm')), 18 | ); 19 | $Actual = spyc_load_file ('indent_1.yaml'); 20 | $this->assertEquals ($Expected, $Actual['steps']); 21 | } 22 | 23 | public function testDeathMasks() { 24 | $Expected = array ('sad' => 2, 'magnificent' => 4); 25 | $Actual = spyc_load_file ('indent_1.yaml'); 26 | $this->assertEquals ($Expected, $Actual['death masks are']); 27 | } 28 | 29 | public function testDevDb() { 30 | $Expected = array ('adapter' => 'mysql', 'host' => 'localhost', 'database' => 'rails_dev'); 31 | $Actual = spyc_load_file ('indent_1.yaml'); 32 | $this->assertEquals ($Expected, $Actual['development']); 33 | } 34 | 35 | public function testNumericKey() { 36 | $this->assertEquals ("Ooo, a numeric key!", $this->yaml[1040]); 37 | } 38 | 39 | public function testMappingsString() { 40 | $this->assertEquals ("Anyone's name, really.", $this->yaml['String']); 41 | } 42 | 43 | public function testMappingsInt() { 44 | $this->assertSame (13, $this->yaml['Int']); 45 | } 46 | 47 | public function testMappingsBooleanTrue() { 48 | $this->assertSame (true, $this->yaml['True']); 49 | } 50 | 51 | public function testMappingsBooleanFalse() { 52 | $this->assertSame (false, $this->yaml['False']); 53 | } 54 | 55 | public function testMappingsZero() { 56 | $this->assertSame (0, $this->yaml['Zero']); 57 | } 58 | 59 | public function testMappingsNull() { 60 | $this->assertSame (null, $this->yaml['Null']); 61 | } 62 | 63 | public function testMappingsNotNull() { 64 | $this->assertSame ('null', $this->yaml['NotNull']); 65 | } 66 | 67 | public function testMappingsFloat() { 68 | $this->assertSame (5.34, $this->yaml['Float']); 69 | } 70 | 71 | public function testMappingsNegative() { 72 | $this->assertSame (-90, $this->yaml['Negative']); 73 | } 74 | 75 | public function testMappingsSmallFloat() { 76 | $this->assertSame (0.7, $this->yaml['SmallFloat']); 77 | } 78 | 79 | public function testNewline() { 80 | $this->assertSame ("\n", $this->yaml['NewLine']); 81 | } 82 | 83 | 84 | public function testSeq0() { 85 | $this->assertEquals ("PHP Class", $this->yaml[0]); 86 | } 87 | 88 | public function testSeq1() { 89 | $this->assertEquals ("Basic YAML Loader", $this->yaml[1]); 90 | } 91 | 92 | public function testSeq2() { 93 | $this->assertEquals ("Very Basic YAML Dumper", $this->yaml[2]); 94 | } 95 | 96 | public function testSeq3() { 97 | $this->assertEquals (array("YAML is so easy to learn.", 98 | "Your config files will never be the same."), $this->yaml[3]); 99 | } 100 | 101 | public function testSeqMap() { 102 | $this->assertEquals (array("cpu" => "1.5ghz", "ram" => "1 gig", 103 | "os" => "os x 10.4.1"), $this->yaml[4]); 104 | } 105 | 106 | public function testMappedSequence() { 107 | $this->assertEquals (array("yaml.org", "php.net"), $this->yaml['domains']); 108 | } 109 | 110 | public function testAnotherSequence() { 111 | $this->assertEquals (array("program" => "Adium", "platform" => "OS X", 112 | "type" => "Chat Client"), $this->yaml[5]); 113 | } 114 | 115 | public function testFoldedBlock() { 116 | $this->assertEquals ("There isn't any time for your tricks!\nDo you understand?", $this->yaml['no time']); 117 | } 118 | 119 | public function testLiteralAsMapped() { 120 | $this->assertEquals ("There is nothing but time\nfor your tricks.", $this->yaml['some time']); 121 | } 122 | 123 | public function testCrazy() { 124 | $this->assertEquals (array( array("name" => "spartan", "notes" => 125 | array( "Needs to be backed up", 126 | "Needs to be normalized" ), 127 | "type" => "mysql" )), $this->yaml['databases']); 128 | } 129 | 130 | public function testColons() { 131 | $this->assertEquals ("like", $this->yaml["if: you'd"]); 132 | } 133 | 134 | public function testInline() { 135 | $this->assertEquals (array("One", "Two", "Three", "Four"), $this->yaml[6]); 136 | } 137 | 138 | public function testNestedInline() { 139 | $this->assertEquals (array("One", array("Two", "And", "Three"), "Four", "Five"), $this->yaml[7]); 140 | } 141 | 142 | public function testNestedNestedInline() { 143 | $this->assertEquals (array( "This", array("Is", "Getting", array("Ridiculous", "Guys")), 144 | "Seriously", array("Show", "Mercy")), $this->yaml[8]); 145 | } 146 | 147 | public function testInlineMappings() { 148 | $this->assertEquals (array("name" => "chris", "age" => "young", "brand" => "lucky strike"), $this->yaml[9]); 149 | } 150 | 151 | public function testNestedInlineMappings() { 152 | $this->assertEquals (array("name" => "mark", "age" => "older than chris", 153 | "brand" => array("marlboro", "lucky strike")), $this->yaml[10]); 154 | } 155 | 156 | public function testReferences() { 157 | $this->assertEquals (array('Perl', 'Python', 'PHP', 'Ruby'), $this->yaml['dynamic languages']); 158 | } 159 | 160 | public function testReferences2() { 161 | $this->assertEquals (array('C/C++', 'Java'), $this->yaml['compiled languages']); 162 | } 163 | 164 | public function testReferences3() { 165 | $this->assertEquals (array( 166 | array('Perl', 'Python', 'PHP', 'Ruby'), 167 | array('C/C++', 'Java') 168 | ), $this->yaml['all languages']); 169 | } 170 | 171 | public function testEscapedQuotes() { 172 | $this->assertEquals ("you know, this shouldn't work. but it does.", $this->yaml[11]); 173 | } 174 | 175 | public function testEscapedQuotes_2() { 176 | $this->assertEquals ( "that's my value.", $this->yaml[12]); 177 | } 178 | 179 | public function testEscapedQuotes_3() { 180 | $this->assertEquals ("again, that's my value.", $this->yaml[13]); 181 | } 182 | 183 | public function testQuotes() { 184 | $this->assertEquals ("here's to \"quotes\", boss.", $this->yaml[14]); 185 | } 186 | 187 | public function testQuoteSequence() { 188 | $this->assertEquals ( array( 'name' => "Foo, Bar's", 'age' => 20), $this->yaml[15]); 189 | } 190 | 191 | public function testShortSequence() { 192 | $this->assertEquals (array( 0 => "a", 1 => array (0 => 1, 1 => 2), 2 => "b"), $this->yaml[16]); 193 | } 194 | 195 | public function testHash_1() { 196 | $this->assertEquals ("Hash", $this->yaml['hash_1']); 197 | } 198 | 199 | public function testHash_2() { 200 | $this->assertEquals ('Hash #and a comment', $this->yaml['hash_2']); 201 | } 202 | 203 | public function testHash_3() { 204 | $this->assertEquals ('Hash (#) can appear in key too', $this->yaml['hash#3']); 205 | } 206 | 207 | public function testEndloop() { 208 | $this->assertEquals ("Does this line in the end indeed make Spyc go to an infinite loop?", $this->yaml['endloop']); 209 | } 210 | 211 | public function testReallyLargeNumber() { 212 | $this->assertEquals ('115792089237316195423570985008687907853269984665640564039457584007913129639936', $this->yaml['a_really_large_number']); 213 | } 214 | 215 | public function testFloatWithZeros() { 216 | $this->assertSame ('1.0', $this->yaml['float_test']); 217 | } 218 | 219 | public function testFloatWithQuotes() { 220 | $this->assertSame ('1.0', $this->yaml['float_test_with_quotes']); 221 | } 222 | 223 | public function testFloatInverse() { 224 | $this->assertEquals ('001', $this->yaml['float_inverse_test']); 225 | } 226 | 227 | public function testIntArray() { 228 | $this->assertEquals (array (1, 2, 3), $this->yaml['int array']); 229 | } 230 | 231 | public function testArrayOnSeveralLines() { 232 | $this->assertEquals (array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), $this->yaml['array on several lines']); 233 | } 234 | 235 | public function testmoreLessKey() { 236 | $this->assertEquals ('', $this->yaml['morelesskey']); 237 | } 238 | 239 | public function testArrayOfZero() { 240 | $this->assertSame (array(0), $this->yaml['array_of_zero']); 241 | } 242 | 243 | public function testSophisticatedArrayOfZero() { 244 | $this->assertSame (array('rx' => array ('tx' => array (0))), $this->yaml['sophisticated_array_of_zero']); 245 | } 246 | 247 | public function testSwitches() { 248 | $this->assertEquals (array (array ('row' => 0, 'col' => 0, 'func' => array ('tx' => array(0, 1)))), $this->yaml['switches']); 249 | } 250 | 251 | public function testEmptySequence() { 252 | $this->assertSame (array(), $this->yaml['empty_sequence']); 253 | } 254 | 255 | public function testEmptyHash() { 256 | $this->assertSame (array(), $this->yaml['empty_hash']); 257 | } 258 | 259 | public function testEmptykey() { 260 | $this->assertSame (array('' => array ('key' => 'value')), $this->yaml['empty_key']); 261 | } 262 | 263 | public function testMultilines() { 264 | $this->assertSame (array(array('type' => 'SomeItem', 'values' => array ('blah', 'blah', 'blah', 'blah'), 'ints' => array(2, 54, 12, 2143))), $this->yaml['multiline_items']); 265 | } 266 | 267 | public function testManyNewlines() { 268 | $this->assertSame ('A quick 269 | fox 270 | 271 | 272 | jumped 273 | over 274 | 275 | 276 | 277 | 278 | 279 | a lazy 280 | 281 | 282 | 283 | dog', $this->yaml['many_lines']); 284 | } 285 | 286 | public function testWerte() { 287 | $this->assertSame (array ('1' => 'nummer 1', '0' => 'Stunde 0'), $this->yaml['werte']); 288 | } 289 | 290 | /* public function testNoIndent() { 291 | $this->assertSame (array( 292 | array ('record1'=>'value1'), 293 | array ('record2'=>'value2') 294 | ) 295 | , $this->yaml['noindent_records']); 296 | } */ 297 | 298 | public function testColonsInKeys() { 299 | $this->assertSame (array (1000), $this->yaml['a:1']); 300 | } 301 | 302 | public function testColonsInKeys2() { 303 | $this->assertSame (array (2000), $this->yaml['a:2']); 304 | } 305 | 306 | public function testSpecialCharacters() { 307 | $this->assertSame ('[{]]{{]]', $this->yaml['special_characters']); 308 | } 309 | 310 | public function testAngleQuotes() { 311 | $Quotes = Spyc::YAMLLoad('quotes.yaml'); 312 | $this->assertEquals (array ('html_tags' => array ('
', '

'), 'html_content' => array ('

hello world

', 'hello
world'), 'text_content' => array ('hello world')), 313 | $Quotes); 314 | } 315 | 316 | public function testFailingColons() { 317 | $Failing = Spyc::YAMLLoad('failing1.yaml'); 318 | $this->assertSame (array ('MyObject' => array ('Prop1' => array ('key1:val1'))), 319 | $Failing); 320 | } 321 | 322 | } -------------------------------------------------------------------------------- /php4/spyc.php4: -------------------------------------------------------------------------------- 1 | 6 | * @author Chris Wanstrath 7 | * @link http://code.google.com/p/spyc/ 8 | * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen 9 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 10 | * @package Spyc 11 | */ 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 | 24 | if (!function_exists('spyc_load_file')) { 25 | /** 26 | * Parses YAML to array. 27 | * @param string $file Path to YAML file. 28 | * @return array 29 | */ 30 | function spyc_load_file ($file) { 31 | return Spyc::YAMLLoad($file); 32 | } 33 | } 34 | 35 | /** 36 | * The Simple PHP YAML Class. 37 | * 38 | * This class can be used to read a YAML file and convert its contents 39 | * into a PHP array. It currently supports a very limited subsection of 40 | * the YAML spec. 41 | * 42 | * Usage: 43 | * 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 7 | * @link http://code.google.com/p/spyc/ 8 | * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2009 Vlad Andersen 9 | * @license http://www.opensource.org/licenses/mit-license.php MIT License 10 | * @package Spyc 11 | */ 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 | 24 | if (!function_exists('spyc_load_file')) { 25 | /** 26 | * Parses YAML to array. 27 | * @param string $file Path to YAML file. 28 | * @return array 29 | */ 30 | function spyc_load_file ($file) { 31 | return Spyc::YAMLLoad($file); 32 | } 33 | } 34 | 35 | /** 36 | * The Simple PHP YAML Class. 37 | * 38 | * This class can be used to read a YAML file and convert its contents 39 | * into a PHP array. It currently supports a very limited subsection of 40 | * the YAML spec. 41 | * 42 | * Usage: 43 | * 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); --------------------------------------------------------------------------------