]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blobdiff - extensions/LocalisationUpdate/QuickArrayReader.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / extensions / LocalisationUpdate / QuickArrayReader.php
diff --git a/extensions/LocalisationUpdate/QuickArrayReader.php b/extensions/LocalisationUpdate/QuickArrayReader.php
new file mode 100644 (file)
index 0000000..d8b7972
--- /dev/null
@@ -0,0 +1,214 @@
+<?php
+
+/**
+ * Quickie parser class that can happily read the subset of PHP we need
+ * for our localization arrays safely.
+ *
+ * Still an order of magnitude slower than eval().
+ */
+class QuickArrayReader {
+       private $vars = [];
+
+       /**
+        * @param $string string
+        */
+       function __construct( $string ) {
+               $scalarTypes = [
+                       T_LNUMBER => true,
+                       T_DNUMBER => true,
+                       T_STRING => true,
+                       T_CONSTANT_ENCAPSED_STRING => true,
+               ];
+               $skipTypes = [
+                       T_WHITESPACE => true,
+                       T_COMMENT => true,
+                       T_DOC_COMMENT => true,
+               ];
+               $tokens = token_get_all( $string );
+               $count = count( $tokens );
+               for ( $i = 0; $i < $count; ) {
+                       while ( isset( $skipTypes[$tokens[$i][0]] ) ) {
+                               $i++;
+                       }
+                       switch ( $tokens[$i][0] ) {
+                               case T_OPEN_TAG:
+                                       $i++;
+                                       continue;
+                               case T_VARIABLE:
+                                       // '$messages' -> 'messages'
+                                       $varname = trim( substr( $tokens[$i][1], 1 ) );
+                                       $varindex = null;
+
+                                       while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                       }
+
+                                       if ( $tokens[$i] === '[' ) {
+                                               while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                               }
+
+                                               if ( isset( $scalarTypes[$tokens[$i][0]] ) ) {
+                                                       $varindex = $this->parseScalar( $tokens[$i] );
+                                               } else {
+                                                       throw $this->except( $tokens[$i], 'scalar index' );
+                                               }
+                                               while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                               }
+
+                                               if ( $tokens[$i] !== ']' ) {
+                                                       throw $this->except( $tokens[$i], ']' );
+                                               }
+                                               while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                               }
+                                       }
+
+                                       if ( $tokens[$i] !== '=' ) {
+                                               throw $this->except( $tokens[$i], '=' );
+                                       }
+                                       while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                       }
+
+                                       if ( isset( $scalarTypes[$tokens[$i][0]] ) ) {
+                                               $buildval = $this->parseScalar( $tokens[$i] );
+                                       } elseif ( $tokens[$i][0] === T_ARRAY ) {
+                                               while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                               }
+                                               if ( $tokens[$i] !== '(' ) {
+                                                       throw $this->except( $tokens[$i], '(' );
+                                               }
+                                               $buildval = [];
+                                               do {
+                                                       while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                                       }
+
+                                                       if ( $tokens[$i] === ')' ) {
+                                                               break;
+                                                       }
+                                                       if ( isset( $scalarTypes[$tokens[$i][0]] ) ) {
+                                                               $key = $this->parseScalar( $tokens[$i] );
+                                                       }
+                                                       while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                                       }
+
+                                                       if ( $tokens[$i][0] !== T_DOUBLE_ARROW ) {
+                                                               throw $this->except( $tokens[$i], '=>' );
+                                                       }
+                                                       while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                                       }
+
+                                                       if ( isset( $scalarTypes[$tokens[$i][0]] ) ) {
+                                                               $val = $this->parseScalar( $tokens[$i] );
+                                                       }
+                                                       wfSuppressWarnings();
+                                                       $buildval[$key] = $val;
+                                                       wfRestoreWarnings();
+                                                       while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                                       }
+
+                                                       if ( $tokens[$i] === ',' ) {
+                                                               continue;
+                                                       } elseif ( $tokens[$i] === ')' ) {
+                                                               break;
+                                                       } else {
+                                                               throw $this->except( $tokens[$i], ', or )' );
+                                                       }
+                                               } while ( true );
+                                       } else {
+                                               throw $this->except( $tokens[$i], 'scalar or array' );
+                                       }
+                                       if ( is_null( $varindex ) ) {
+                                               $this->vars[$varname] = $buildval;
+                                       } else {
+                                               wfSuppressWarnings();
+                                               $this->vars[$varname][$varindex] = $buildval;
+                                               wfRestoreWarnings();
+                                       }
+                                       while ( isset( $skipTypes[$tokens[++$i][0]] ) ) {
+                                       }
+                                       if ( $tokens[$i] !== ';' ) {
+                                               throw $this->except( $tokens[$i], ';' );
+                                       }
+                                       $i++;
+                                       break;
+                               default:
+                                       throw $this->except( $tokens[$i], 'open tag, whitespace, or variable.' );
+                       }
+               }
+       }
+
+       /**
+        * @param $got string
+        * @param $expected string
+        * @return Exception
+        */
+       private function except( $got, $expected ) {
+               if ( is_array( $got ) ) {
+                       $got = token_name( $got[0] ) . " ('" . $got[1] . "')";
+               } else {
+                       $got = "'" . $got . "'";
+               }
+
+               return new Exception( "Expected $expected, got $got" );
+       }
+
+       /**
+        * Parse a scalar value in PHP
+        *
+        * @param $token string
+        *
+        * @return mixed Parsed value
+        */
+       function parseScalar( $token ) {
+               if ( is_array( $token ) ) {
+                       $str = $token[1];
+               } else {
+                       $str = $token;
+               }
+               if ( $str !== '' && $str[0] == '\'' ) {
+                       // Single-quoted string
+                       // @fixme trim() call is due to mystery bug where whitespace gets
+                       // appended to the token; without it we ended up reading in the
+                       // extra quote on the end!
+                       return strtr( substr( trim( $str ), 1, -1 ),
+                               [ '\\\'' => '\'', '\\\\' => '\\' ] );
+               }
+
+               wfSuppressWarnings();
+               if ( $str !== '' && $str[0] == '"' ) {
+                       // Double-quoted string
+                       // @fixme trim() call is due to mystery bug where whitespace gets
+                       // appended to the token; without it we ended up reading in the
+                       // extra quote on the end!
+                       wfRestoreWarnings();
+                       return stripcslashes( substr( trim( $str ), 1, -1 ) );
+               }
+               wfRestoreWarnings();
+
+               if ( substr( $str, 0, 4 ) === 'true' ) {
+                       return true;
+               }
+
+               if ( substr( $str, 0, 5 ) === 'false' ) {
+                       return false;
+               }
+
+               if ( substr( $str, 0, 4 ) === 'null' ) {
+                       return null;
+               }
+
+               // Must be some kind of numeric value, so let PHP's weak typing
+               // be useful for a change
+               return $str;
+       }
+
+       /**
+        * @param $varname string
+        * @return null|string
+        */
+       function getVar( $varname ) {
+               if ( isset( $this->vars[$varname] ) ) {
+                       return $this->vars[$varname];
+               } else {
+                       return null;
+               }
+       }
+}