diff options
Diffstat (limited to 'www/wiki/extensions/LocalisationUpdate/QuickArrayReader.php')
-rw-r--r-- | www/wiki/extensions/LocalisationUpdate/QuickArrayReader.php | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/www/wiki/extensions/LocalisationUpdate/QuickArrayReader.php b/www/wiki/extensions/LocalisationUpdate/QuickArrayReader.php new file mode 100644 index 00000000..d8b7972b --- /dev/null +++ b/www/wiki/extensions/LocalisationUpdate/QuickArrayReader.php @@ -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; + } + } +} |