1<?php 2/** 3 * from https://github.com/atk14/MiniYaml and namespaced for this plugin 4 */ 5namespace dokuwiki\plugin\questionnaire; 6 7 8/** 9* YAML dumper & loader 10* 11* It can dump and load associative or indexed arrays. 12* 13* Usage: 14* $ary = miniYAML::Load($yaml_str); 15* $yaml = miniYAML::Dump($ary); 16* 17* TODO: 18* Parser nenacte takovy zapis, ve kterem jsou vsechny radky odsazeny o spolecny indent. 19* 20* Changelog: 21* 22* 2013-03-02 23* Better string escaping 24* An associative array can be passed to miniYAML::Dump() 25* 26* 2008-04-09 27* Vylepseno rozpoznavani asociativniho pole. 28* 29* 2008-01-28 30* Prepracovani nacitani YAML dokumentu. Parser si nyni poradi i s indexovym pole, 31* jeho prevky jsou jine pole (indexove nebo asociativni): 32* --- 33* - element 1 34* - - element 2.1 35* - element 2.2 36* - key1: val1 37* key2: val2 38* 39* 2008-01-25 40* Doplnena schopnost zpracovat prazdne indexove pole. 41* --- 42* status: success 43* message: Ok 44* data: 45* domain: test.cz 46* registrant: ZUZANA-PROKOPOVA 47* nsset: HOSTING-NS 48* admin: 49* - JAN-PROKOP 50* - JANA-PROKOPOVA 51* tempcontact: [] 52* 53* registrar: REG-GENREG 54* create_date: 2001-01-10 55* expiry_date: 2014-01-11 56* 57* 2008-01-09 58* Dodelana schopnost zpracovavat indexovana pole. Priklad YAML: 59* --- 60* command: check domains availability 61* params: 62* domains: 63* - test1.cz 64* - test2.cz 65* - test3.cz 66* - test4.cz 67*/ 68class miniYAML{ 69 70 protected $_Lines = array(); 71 protected $nullable = true; 72 73 function __construct($options = array()){ 74 $options += array( 75 "nullable" => true, // Whether to consider strings null and NULL as true NULL? 76 ); 77 78 $this->nullable = $options["nullable"]; 79 } 80 81 /** 82 * Prevede YAML zapis na pole. 83 * 84 * @static 85 * @access public 86 * @param string $yaml zapis struktury v YAML 87 * @return array 88 */ 89 static function Load($yaml,$options = array()){ 90 $options = array_merge(array( 91 "interpret_php" => false, 92 "values" => array(), 93 ),$options); 94 95 if($options["interpret_php"]){ 96 $yaml = miniYAML::InterpretPHP($yaml,$options["values"]); 97 } 98 99 unset($options["interpret_php"]); 100 unset($options["values"]); 101 102 $obj = new miniYAML($options); 103 return $obj->_load($yaml); 104 } 105 106 /** 107 * Prevedete pole na do YAML zapisu. 108 * 109 * Reverzni metoda k miniYAML::Load(); 110 * 111 * @static 112 * @access public 113 * @param array $ar 114 * @return string 115 */ 116 static function Dump($ar,$options = array()){ 117 $obj = new miniYAML($options); 118 $out = "---"; 119 if($obj->_isIndexedArray($ar)){ 120 $out .= "\n".$obj->_dumpIndexedArray($ar,0); 121 }elseif(is_array($ar)){ 122 $out .= "\n".$obj->_dumpHashArray($ar,0); 123 } 124 $out .= "\n"; 125 return $out; 126 } 127 128 /** 129 * $yaml = miniYAML::InterpretPHP(' 130 * --- 131 * login: <?= $login ?> 132 * 133 * password: <?= $password ?> 134 * ',array( 135 * "login" => "admin", 136 * "password" => "magic" 137 * )); 138 * 139 * Note: Think of newline removal at the end of php end tag! 140 */ 141 static function InterpretPHP($__yaml,$__values = array()){ 142 foreach($__values as $__k => $__v){ 143 eval("\$$__k = \$__v;"); 144 } 145 $__error_reporting = error_reporting(0); 146 ob_start(); 147 eval("?".">".$__yaml); 148 $__yaml = ob_get_contents(); 149 ob_end_clean(); 150 error_reporting($__error_reporting); 151 return $__yaml; 152 } 153 154 function _load($yaml){ 155 $yaml = str_replace("\r","",$yaml); 156 $ar = explode("\n",$yaml); 157 $this->_Lines = array(); 158 $got_structure_begin = false; 159 for($i=0;$i<sizeof($ar);$i++){ 160 if(trim($ar[$i]) == "---"){ // zacatek nove struktury - muze byt v zapisu pouze 1x a to na jejim zacatku 161 if($got_structure_begin){ return null; } 162 $got_structure_begin = true; 163 continue; 164 } 165 if($this->_containsData($ar[$i])){ 166 $got_structure_begin = true; 167 $this->_Lines[] = $ar[$i]; 168 } 169 } 170 171 $out = $this->_readVar($this->_Lines,$lines_read); 172 // nasledujici podminka zachyti situaci, kdyz nejsou zpracovany vsechny radky a uz mame nejaky hotovy vystup... 173 // muze se to tykat napr. tohoto neplatneho zapisu: 174 // --- 175 // - jedna 176 // - dve 177 // klic: 178 if($lines_read!=sizeof($this->_Lines)){ 179 return null; 180 } 181 return $out; 182 } 183 184 /** 185 * Spocita mezery na zacatku radku. 186 * 187 * @access private 188 * @param string $line 189 * @return int 190 */ 191 function _getIndent($line){ 192 preg_match("/^( *)/",(string)$line,$matches); 193 return strlen($matches[1]); 194 } 195 196 /** 197 * Smaze z radky odsazeni. 198 * 199 * @access private 200 * @param string $line 201 * @param int $indent bude-li -1, bude delka odsazeni urcena automaticky 202 * @return string 203 */ 204 function _stripIndent($line, $indent = -1){ 205 if ($indent == -1){ 206 $indent = $this->_getIndent($line); 207 } 208 return substr ($line, $indent); 209 } 210 211 /** 212 * Vysekne z pole radku blok s danym odsazenim. 213 * 214 * Vybere vsechny radky, ktere maji alespon urcene odsazeni, 215 * ale i vsechny radky s odsazenim delsim. 216 * 217 * Zamerne vsak neni porovnavana delka odsazeni prvniho radku. 218 * Navic je prvnimu radku toto odsazeni automaticky nastaveno. 219 * 220 * @access public 221 * @param int $start_at prvni index v poli 222 * @param int $indent delka nejmensiho odsazeni 223 * @param string[] $lines pole radek; pokud nebude nastaveno, bude se uvazovat $this->_Lines 224 * @return string[] 225 */ 226 function _cutOutBlock($start_at,$indent,$lines = null){ 227 if(!isset($lines)){ $lines = &$this->_Lines; } 228 $out = array(); 229 $out[] = str_repeat(" ",$indent).substr($lines[$start_at],$indent); 230 for($i=$start_at+1;$i<sizeof($lines);$i++){ 231 $_indent = $this->_getIndent($lines[$i]); 232 if($_indent<$indent){ 233 break; 234 } 235 $out[] = $lines[$i]; 236 } 237 return $out; 238 } 239 240 /** 241 * Provede vyseknuti bloku. Navic vsem radkum odstrani indent. 242 * 243 * @access public 244 * @param int $start_at prvni index v poli 245 * @param int $indent delka nejmensiho odsazeni 246 * @param string[] $lines pole radek; pokud nebude nastaveno, bude se uvazovat $this->_Lines 247 * @return string[] 248 */ 249 function _cutOutBlock_Stripped($start_at,$indent,$lines = null){ 250 $lines = $this->_cutOutBlock($start_at,$indent,$lines); 251 for($i=0;$i<sizeof($lines);$i++){ 252 $lines[$i] = substr($lines[$i],$indent); 253 } 254 return $lines; 255 } 256 257 /** 258 * Nacte datovou strukturu z daneho pole radku. 259 * 260 * @access private 261 * @param string[] $block 262 * @param int &$lines_read pocet radku pole potrebnych pro nacteni struktury 263 * @param array $options parametry nacitani 264 * @return mixed indexove pole, hash pole nebo string 265 */ 266 function _readVar($block,&$lines_read,$options = array()){ 267 $options = array_merge(array( 268 "testing_for_array" => true 269 ),$options); 270 271 if(is_string($block)){ $block = array($block); } 272 273 $lines_read = 0; 274 275 if(sizeof($block)==0){ return null; } 276 277 if($options["testing_for_array"]){ 278 if(preg_match("/^- /",$block[0])){ 279 return $this->_readIndexedArray($block,$lines_read); 280 } 281 if(preg_match("/^[^\\s\"]+?:(\\s+[^\\s].*|\\s*)$/",$block[0])){ 282 return $this->_readHashArray($block,$lines_read); 283 } 284 } 285 286 if(sizeof($block)==1){ // toto je spatne!!!! zde zapadne i indexove pole o velikosti 1 287 $lines_read = 1; 288 $out = trim($block[0]); 289 if($out == "[]"){ return array(); } 290 $this->_unescapeString($out); 291 return $out; 292 } 293 } 294 295 /** 296 * Nacte indexove pole z pole radku 297 * 298 * @access private 299 * @param string[] $block 300 * @param int &$lines_read pocet radku pole potrebnych pro nacteni vraceneho pole 301 * @return array 302 */ 303 function _readIndexedArray($block,&$lines_read){ 304 $out = array(); 305 $lines_read = 0; 306 for($i=0;$i<sizeof($block);$i++){ 307 $line = $block[$i]; 308 if(!preg_match("/^- /",$line)){ 309 break; 310 } 311 $value_block = $this->_cutOutBlock_Stripped($i,2,$block); 312 $out[] = $this->_readVar($value_block,$li); 313 $i += $li-1; // -1 -> zaciname cist na akt. radku 314 } 315 $lines_read = $i; 316 return $out; 317 } 318 319 /** 320 * Nacte asociativni pole (hash) z pole radku. 321 * 322 * @access private 323 * @param string[] $block 324 * @param int &$lines_read pocet radku pole potrebnych pro nacteni vraceneho pole 325 * @return array 326 */ 327 function _readHashArray($block,&$lines_read){ 328 $out = array(); 329 $lines_read = 0; 330 for($i=0;$i<sizeof($block);$i++){ 331 $line = $block[$i]; 332 $next_line = null; 333 if(isset($block[$i+1])){ $next_line = $block[$i+1]; } 334 if(!preg_match("/^(.+?):(.*)/",$line,$matches)){ 335 $i--; 336 break; 337 } 338 $key = $matches[1]; 339 $_values = trim($matches[2]); 340 $next_line_indent = $this->_getIndent($next_line); 341 if($next_line_indent>0 || preg_match("/^- /",(string)$next_line)){ 342 $value_block = $this->_cutOutBlock_Stripped($i+1,$next_line_indent,$block); 343 $value = $this->_readVar($value_block,$li); 344 $i += $li; 345 }else{ 346 $value = $this->_readVar($_values,$li,array("testing_for_array" => false)); 347 } 348 if(preg_match('/^\s/',$key)){ 349 throw new Exception("token cannot begin with tabulator or other white character on line: $line"); 350 } 351 $out[$key] = $value; 352 } 353 $lines_read = $i; 354 return $out; 355 } 356 357 function _isComment($line){ 358 if(preg_match("/^#/",$line)){ return true;} 359 return false; 360 } 361 362 function _isEmpty($line){ 363 return (strlen(trim($line))==0); 364 } 365 366 function _containsData($line){ 367 return !($this->_isComment($line) || $this->_isEmpty($line)); 368 } 369 370 function _unescapeString(&$str){ 371 if(preg_match('/^("(.*)"|\'(.*)\')/',$str,$matches)){ 372 $str = end($matches); 373 $str = preg_replace('/(\'\'|\\\\\')/',"'",$str); 374 $str = preg_replace('/\\\\"/','"',$str); 375 return true; 376 } 377 if($this->nullable && ($str==="null" || $str==="NULL")){ 378 $str = null; 379 return true; 380 } 381 return false; 382 } 383 384 /* 385 * Metody pro dumpovani. 386 */ 387 388 function _dumpVar($var,$indent = 0){ 389 $out = array(); 390 if($this->_isIndexedArray($var)){ 391 $out[] = sizeof($var)==0 ? "[]" : ""; 392 $out[] = $this->_dumpIndexedArray($var,$indent); // indexove pole se tiskne se stejnym indentem 393 }elseif(is_array($var)){ 394 $out[] = ""; 395 $out[] = $this->_dumpHashArray($var,$indent + 1); 396 }else{ 397 $out[] = $this->_dumpString($var); // schvalne je vynechan $indent, ident je pred klicem 398 } 399 return join("\n",$out); 400 } 401 402 function _dumpString($str,$indent = 0){ 403 $patterns_to_escape = array( 404 "/^\\s+/", "/\\s+$/","/\\n/", 405 "/^yes$/i", "/^on$/i", "/^\\+$/", "/^y$/", "/^true$/i", 406 "/^no$/i", "/^off$/i", "/^-$/", "/^n$/", "/^false$/i", 407 "/^null$/i", "/^~$/", "/^$/", 408 "/^\\-?.inf$/i", "/^.nan$/i", 409 "/^\"/", "/^'/", "/#/", 410 "/^{/", "/^}/", "/^\\[/", "/^\\]/", "/^:/", "/^=$/", "/^\\?$/", "/^\\|/", "/^>/", "/^-$/", "/^<<$/", 411 "/^!/", "/^\\*/", "/^\\&/", 412 "/:\s/", 413 "/^:/", 414 "/:$/", 415 ); 416 417 if(is_null($str) && $this->nullable){ 418 $str = "NULL"; 419 }elseif(is_numeric($str) || is_numeric(str_replace("_","",$str))){ 420 $str = $this->_escapeString($str); 421 }else{ 422 $_escaped = false; 423 foreach($patterns_to_escape as $pattern){ 424 if(preg_match($pattern,$str)){ 425 $str = $this->_escapeString($str); 426 $_escaped = true; 427 break; 428 } 429 } 430 } 431 return $this->_dumpIndent($indent).$str; 432 } 433 434 function _isIndexedArray($ar){ 435 if(!is_array($ar)){ return false; } 436 $expected_key = 0; 437 foreach(array_keys($ar) as $_key){ 438 if(!is_int($_key) || $_key!=$expected_key){ return false; } 439 $expected_key++; 440 } 441 return true; 442 } 443 444 function _dumpIndexedArray($ar,$indent){ 445 $out = array(); 446 foreach($ar as $_value){ 447 if($this->_isIndexedArray($_value) && sizeof($_value)>0){ 448 $_dump = $this->_dumpIndexedArray($_value,$indent + 1); // "- " 449 $_prefix = $this->_dumpIndent($indent)."- "; 450 $out[] = $_prefix.substr($_dump,strlen($_prefix)); 451 }elseif(is_array($_value) && sizeof($_value)>0){ 452 $_dump = $this->_dumpHashArray($_value,$indent + 1); // "- " 453 $_prefix = $this->_dumpIndent($indent)."- "; 454 $out[] = $_prefix.substr($_dump,strlen($_prefix)); 455 //$out[] = $_prefix.$_dump; 456 }else{ 457 $out[] = $this->_dumpIndent($indent)."- ".$this->_dumpVar($_value,$indent + 2); // "- " 458 } 459 } 460 return join("\n",$out); 461 } 462 463 function _dumpHashArray($ar,$indent){ 464 $out = array(); 465 foreach($ar as $_key => $_value){ 466 $out[] = $this->_dumpIndent($indent).$this->_dumpString($_key).": ".$this->_dumpVar($_value,$indent); 467 } 468 return join("\n",$out); 469 } 470 471 function _dumpIndent($indent){ 472 if($indent<=0){ return ""; } 473 return str_repeat(" ",$indent * 2); 474 } 475 476 function _escapeString($str){ 477 return "\"".str_replace("\"","\\\"",$str)."\""; 478 } 479} 480// vim: set expandtab: 481