r53613 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r53612‎ | r53613 | r53614 >
Date:18:00, 21 July 2009
Author:nikerabbit
Status:ok (Comments)
Tags:todo 
Comment:
* Forgot MessageGroups.php
* Add spyc with weird interface, because api already "provides" crippled spyc.
Modified paths:
  • /trunk/extensions/Translate/MessageGroups.php (modified) (history)
  • /trunk/extensions/Translate/spyc (added) (history)
  • /trunk/extensions/Translate/spyc/loader.php (added) (history)
  • /trunk/extensions/Translate/spyc/spyc.php (added) (history)

Diff [purge]

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

Comments

#Comment by Catrope (talk | contribs)   10:30, 25 July 2009

This re-adds code removed by Tim in r42547 for being "scary".

#Comment by Nikerabbit (talk | contribs)   14:42, 25 July 2009

I asked Tim what is scary in it and got no answer. Imho the current interface is safe. It is not possible to load directly from the file using this custom interface, which was the case with old version of Spyc, it seems. I do not see it existing in this version (load string is always string, load file is a file or string).

What I am not particularly happy is that API reserves Spyc class and with some crippled interface, so I have to invent my own. I don't think this is fixme unless someone points me where there is security problem in this version.

#Comment by Werdna (talk | contribs)   18:25, 27 August 2009

What's scary about it is explained.

If a user enters a filename as YAML data, it loads an local file for an attacker. This is an information disclosure vulnerability, which can be escalated by reading LocalSettings.php.

Doesn't block deployment on Wikimedia, but I'll leave it as FIXME because it's so severe.


#Comment by Tim Starling (talk | contribs)   00:01, 3 September 2009

I agree that it's not good to have the core define a crippled Spyc class. So I reviewed Spyc with an eye to merging the Spyc dump functions into ApiFormatYaml. It very quickly became obvious that Spyc has lots of flaws, does not necessarily produce valid YAML, and does not come close to preserving data through a round trip.

> function roundTrip($a) { var_dump(Spyc::YAMLLoad(Spyc::YAMLDump(array('x' => $a)))); }

Implicit tag confusion:

> roundTrip('null')
array(1) {
  ["x"]=>
  NULL
}

> roundTrip('y')
array(1) {
  ["x"]=>
  bool(true)
}

> roundTrip('5')
array(1) {
  ["x"]=>
  int(5)
}

Trailing spaces destroyed:

> roundTrip('x ')
array(1) {
  ["x"]=>
  string(1) "x"
}

> roundTrip("\n")
array(1) {
  ["x"]=>
  string(0) ""
}

Trailing spaces with wordwrap:

> roundTrip("aaaaaaaaaaaaaaaaaaaaaaaaaaaa  bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
array(1) {
  ["x"]=>
  string(62) "aaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}

Quoting bugs:

> roundTrip("'x'")
array(1) {
  ["x"]=>
  string(1) "x"
}

> roundTrip("'x\\\"'")
array(1) {
  ["x"]=>
  string(2) "x""
}

Illegal characters in output (failure to escape):

> print bin2hex(Spyc::YAMLDump(array('x'=> "\000")));
2d2d2d0a783a20000a

No unescaping anyway:

> var_dump(Spyc::YAMLLoad("---\nx: \"\\100\"\n"))
array(1) {
  ["x"]=>
  string(4) "\100"
}

Basically the whole thing is a steaming pile of crap. I would recommend against using it for anything.

#Comment by Brion VIBBER (talk | contribs)   18:45, 8 September 2009

Switching from fixme to todo as this won't be live on Wikimedia.

Status & tagging log