jenkins-bot has submitted this change and it was merged.

Change subject: Allow transclusion of templates inside PhpTags (v 5.1.0) Hook 
release 8 T99997
......................................................................


Allow transclusion of templates inside PhpTags (v 5.1.0) Hook release 8 T99997

This patch allows recursive calls PhpTags

* Rename \PhpTags class to \PhpTags\Renderer
* move functions incrementExpensiveFunctionCount(), addRuntimeErrorCategory(),
        disableParserCache() from Runtime class to Renderer
* add and enhance patch If3483900cb57d34b72cdad362316e8631af2cf79 from JoelKP
* fix illegal offset type in array definition

Bug: T99997
Bug: T99412
Change-Id: I1506de772b5a4f6e94500c972a4f70885853d437
---
D PhpTags.body.php
M PhpTags.php
M includes/Compiler.php
M includes/Hooks.php
M includes/PhpTagsException.php
A includes/Renderer.php
M includes/Runtime.php
M tests/phpunit/includes/!!!firstInit_Test.php
M tests/phpunit/includes/RuntimeTest.php
9 files changed, 628 insertions(+), 580 deletions(-)

Approvals:
  Pastakhov: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/PhpTags.body.php b/PhpTags.body.php
deleted file mode 100644
index 4af04a4..0000000
--- a/PhpTags.body.php
+++ /dev/null
@@ -1,292 +0,0 @@
-<?php
-
-/**
- * The main class of the extension PhpTags.
- *
- * @file PhpTags.body.php
- * @ingroup PhpTags
- * @author Pavel Astakhov <[email protected]>
- * @licence GNU General Public Licence 2.0 or later
- */
-class PhpTags {
-
-       static $compileTime = 0;
-       static $time = 0;
-       static $cacheHit = 0;
-       static $memoryHit = 0;
-       static $compileHit = 0;
-       static $needInitRuntime = true;
-
-       static $globalVariablesScript = array();
-
-       private static $bytecodeNeedsUpdate = array();
-       private static $frames=array();
-       private static $bytecodeCache = array();
-       private static $bytecodeLoaded = array();
-
-       /**
-        *
-        * @param Parser $parser
-        * @param PPFrame $frame
-        * @param array $args
-        */
-       public static function renderFunction( $parser, $frame, $args ) {
-               wfProfileIn( __METHOD__ );
-               $time = $parser->mOutput->getTimeSinceStart( 'cpu' );
-
-               $command = array_shift($args);
-               if ( count( $args ) > 0 ) {
-                       foreach ( $args as &$value ) {
-                               $value = $frame->expand( $value );
-                       }
-                       $command = "echo $command (" . implode( ',', $args ) . 
');';
-               } elseif ( preg_match( '/^\S+$/', $command ) == 1 ) {
-                       $command = "echo $command;";
-               }
-
-               $frameTitle = $frame->getTitle();
-               $frameTitleText = $frameTitle->getPrefixedText();
-               $arguments = array( $frameTitleText ) + $frame->getArguments();
-               $scope = self::getScopeID( $frame );
-
-               try {
-                       $bytecode = self::getBytecode( $command, $parser, 
$frame, $frameTitle, $frameTitleText, $time );
-                       $result = \PhpTags\Runtime::run( $bytecode, $arguments, 
$scope );
-                       $return = implode( $result );
-               } catch ( \PhpTags\PhpTagsException $exc ) {
-                       $return = (string) $exc;
-               } catch ( MWException $exc ) {
-                       throw $exc;
-               } catch ( Exception $exc ) {
-                       $return = $exc->getTraceAsString();
-               }
-
-               self::$time += $parser->mOutput->getTimeSinceStart( 'cpu' ) - 
$time;
-
-               wfProfileOut( __METHOD__ );
-               return \UtfNormal::cleanUp( $return );
-       }
-
-       public static function render( $input, array $args, Parser $parser, 
PPFrame $frame ) {
-               wfProfileIn( __METHOD__ );
-               $time = $parser->mOutput->getTimeSinceStart( 'cpu' );
-
-               $frameTitle = $frame->getTitle();
-               $frameTitleText = $frameTitle->getPrefixedText();
-               $arguments = array( $frameTitleText ) + $frame->getArguments();
-               $scope = self::getScopeID( $frame );
-
-               try {
-                       $bytecode = self::getBytecode( $input, $parser, $frame, 
$frameTitle, $frameTitleText, $time );
-                       $result = \PhpTags\Runtime::run( $bytecode, $arguments, 
$scope );
-               } catch ( \PhpTags\PhpTagsException $exc ) {
-                       $result = array( (string) $exc );
-                       $parser->addTrackingCategory( 
'phptags-compiler-error-category' );
-               } catch ( MWException $exc ) {
-                       throw $exc;
-               } catch ( Exception $exc ) {
-                       $result = array( $exc->getTraceAsString() );
-               }
-
-               self::$time += $parser->mOutput->getTimeSinceStart( 'cpu' ) - 
$time;
-               $return = self::insertGeneral( $parser, 
$parser->recursiveTagParse( implode($result), $frame ) );
-               wfProfileOut( __METHOD__ );
-               return $return;
-       }
-
-       /**
-        *
-        * @global int $wgPhpTagsBytecodeExptime
-        * @param string $source
-        * @param Parser $parser
-        * @param PPFrame $frame
-        * @param Title $frameTitle
-        * @param string $frameTitleText
-        * @param int $time
-        * @return array
-        */
-       private static function getBytecode( $source, $parser, $frame, 
$frameTitle, $frameTitleText, $time ) {
-               global $wgPhpTagsBytecodeExptime, $wgPhpTagsCounter;
-               $wgPhpTagsCounter++;
-
-               static $parserTitle = false;
-               if ( $parserTitle === false ) {
-                       $parserTitle = $parser->getTitle();
-               }
-               $revID = $parserTitle === $frameTitle ? 
$parser->getRevisionId() : $frameTitle->getLatestRevID();
-               $md5Source = md5( $source );
-
-               self::initialize( $parser, $frame, $frameTitle );
-
-               if ( true === isset( self::$bytecodeCache[$revID][$md5Source] ) 
) {
-                       \wfDebug( "[phptags] Memory hiting with key $revID" );
-                       self::$memoryHit++;
-                       return self::$bytecodeCache[$revID][$md5Source];
-               }
-
-               if ( $wgPhpTagsBytecodeExptime > 0 && $revID > 0 && false === 
isset( self::$bytecodeLoaded[$revID] ) ) {
-                       $cache = \wfGetCache( CACHE_ANYTHING );
-                       $key = \wfMemcKey( 'PhpTags', $revID );
-                       $data = $cache->get( $key );
-                       self::$bytecodeLoaded[$revID] = true;
-                       if ( $data !== false && $data[0] === 
PHPTAGS_RUNTIME_RELEASE ) {
-                               self::$bytecodeCache[$revID] = $data[1];
-                               if ( true === isset( 
self::$bytecodeCache[$revID][$md5Source] ) ) {
-                                       \wfDebug( "[phptags] Cache hiting with 
key $revID" );
-                                       self::$cacheHit++;
-                                       return 
self::$bytecodeCache[$revID][$md5Source];
-                               }
-                       }
-                       \wfDebug( "[phptags] Cache missing with key $revID" );
-               }
-
-               $bytecode = \PhpTags\Compiler::compile( $source, 
$frameTitleText );
-               self::$bytecodeCache[$revID][$md5Source] = $bytecode;
-               if ( $revID > 0 ) { // Don't save bytecode of unsaved pages
-                       self::$bytecodeNeedsUpdate[$revID][$md5Source] = 
unserialize( serialize( $bytecode ) );
-               }
-
-               self::$compileHit++;
-               self::$compileTime += $parser->mOutput->getTimeSinceStart( 
'cpu' ) - $time;
-               return $bytecode;
-       }
-
-       /**
-        *
-        * @param Parser $parser
-        * @param PPFrame $frame
-        * @throws \PhpTags\PhpTagsException
-        * @return null
-        */
-       private static function initialize( $parser, $frame, $frameTitle ) {
-               global $wgPhpTagsNamespaces, $wgPhpTagsMaxLoops;
-               if ( true !== $wgPhpTagsNamespaces && false === isset( 
$wgPhpTagsNamespaces[$frameTitle->getNamespace()] ) ) {
-                       throw new \PhpTags\PhpTagsException( 
\PhpTags\PhpTagsException::FATAL_DENIED_FOR_NAMESPACE, $frameTitle->getNsText() 
);
-               }
-
-               if ( true === self::$needInitRuntime ) {
-                       \wfDebug( '[phptags] ' . __METHOD__ . '() runs hook 
PhpTagsRuntimeFirstInit' );
-                       \wfRunHooks( 'PhpTagsRuntimeFirstInit' );
-                       \PhpTags\Hooks::loadData();
-                       \PhpTags\Runtime::$loopsLimit = $wgPhpTagsMaxLoops;
-                       self::$needInitRuntime = false;
-               }
-
-               \PhpTags\Runtime::$parser = $parser;
-               \PhpTags\Runtime::$frame = $frame;
-       }
-
-       private static function updateBytecodeCache() {
-               global $wgPhpTagsBytecodeExptime;
-
-               $cache = \wfGetCache( CACHE_ANYTHING );
-               foreach ( self::$bytecodeNeedsUpdate as $revID => $data ) {
-                       $key = wfMemcKey( 'PhpTags', $revID );
-                       $cache->set( $key, array(PHPTAGS_RUNTIME_RELEASE, 
$data), $wgPhpTagsBytecodeExptime );
-                       \wfDebug( "[phptags] Save compiled bytecode to cache 
with key $revID" );
-               }
-               self::$bytecodeNeedsUpdate = array();
-       }
-
-       public static function reset() {
-               self::writeLimitReport();
-
-               global $wgPhpTagsCounter;
-               $wgPhpTagsCounter = 0;
-               PhpTags\Runtime::reset();
-               self::$bytecodeCache = array();
-               self::$bytecodeLoaded = array();
-               self::$globalVariablesScript = array();
-       }
-
-       /**
-        *
-        * @param Parser $parser
-        * @param string $text
-        * @return string
-        */
-       private static function insertGeneral( $parser, $text ) {
-               return $parser->insertStripItem( $text );
-       }
-
-       private static function getScopeID( PPFrame $frame ) {
-               foreach ( self::$frames as $value ) {
-                       if ( $value[0] === $frame ) {
-                               return $value[1];
-                       }
-               }
-               static $scope = 0;
-               self::$frames[] = array( $frame, $scope );
-               return $scope++;
-       }
-
-       /**
-        *
-        * @param array $extResources
-        * @param array $extMode
-        */
-       public static function onCodeMirrorGetAdditionalResources( 
&$extResources, &$extMode ) {
-               $extResources['scripts']['lib/codemirror/mode/php/php.js'] = 
true;
-               
$extResources['scripts']['lib/codemirror/mode/htmlmixed/htmlmixed.js'] = true;
-               $extResources['scripts']['lib/codemirror/mode/xml/xml.js'] = 
true;
-               
$extResources['scripts']['lib/codemirror/mode/javascript/javascript.js'] = true;
-               $extResources['scripts']['lib/codemirror/mode/css/css.js'] = 
true;
-               $extResources['scripts']['lib/codemirror/mode/clike/clike.js'] 
= true;
-
-               $extMode['tag']['phptag'] = 'text/x-php';
-
-               return true;
-       }
-
-       public static function onPhpTagsRuntimeFirstInit() {
-               \PhpTags\Hooks::addJsonFile( __DIR__ . '/PhpTags.json', 
PHPTAGS_VERSION );
-               return true;
-       }
-
-       public static function writeLimitReport() {
-               global $wgPhpTagsCounter, $wgPhpTagsLimitReport;
-
-               $time = self::$time;
-               $compileTime = self::$compileTime;
-               $wgPhpTagsLimitReport = sprintf(
-                               'PhpTags usage count: %d
-Runtime : %.3f sec
-Compiler: %.3f sec ( usage: %d, cache: %d, memory: %d )
-Total   : %.3f sec
-',
-                               $wgPhpTagsCounter,
-                               $time - $compileTime,
-                               $compileTime,
-                               self::$compileHit,
-                               self::$cacheHit,
-                               self::$memoryHit,
-                               $time
-                       );
-               return true;
-       }
-
-       public static function onParserAfterTidy( &$parser, &$text ) {
-               global $wgPhpTagsBytecodeExptime;
-               wfProfileIn( __METHOD__ );
-
-               if ( self::$globalVariablesScript ) {
-                       $vars = array();
-                       foreach ( self::$globalVariablesScript as $key=> $value 
) {
-                               $vars["ext.phptags.$key"] = $value;
-                       }
-                       $text .= Html::inlineScript(
-                               ResourceLoader::makeLoaderConditionalScript(
-                                       ResourceLoader::makeConfigSetScript( 
$vars )
-                               )
-                       );
-               }
-               if ( $wgPhpTagsBytecodeExptime > 0 && 
self::$bytecodeNeedsUpdate ) {
-                       self::updateBytecodeCache();
-               }
-               self::reset();
-
-               wfProfileOut( __METHOD__ );
-               return true;
-       }
-
-}
diff --git a/PhpTags.php b/PhpTags.php
index 2d308cb..928322a 100644
--- a/PhpTags.php
+++ b/PhpTags.php
@@ -16,11 +16,11 @@
 }
 
 const PHPTAGS_MAJOR_VERSION = 5;
-const PHPTAGS_MINOR_VERSION = 0;
-const PHPTAGS_RELEASE_VERSION = 1;
+const PHPTAGS_MINOR_VERSION = 1;
+const PHPTAGS_RELEASE_VERSION = 0;
 define( 'PHPTAGS_VERSION', PHPTAGS_MAJOR_VERSION . '.' . PHPTAGS_MINOR_VERSION 
. '.' . PHPTAGS_RELEASE_VERSION );
 
-const PHPTAGS_HOOK_RELEASE = 7;
+const PHPTAGS_HOOK_RELEASE = 8;
 const PHPTAGS_RUNTIME_RELEASE = 4;
 const PHPTAGS_JSONLOADER_RELEASE = 3;
 
@@ -39,46 +39,9 @@
 $wgMessagesDirs['PhpTags'] = __DIR__ . '/i18n';
 $wgExtensionMessagesFiles['PhpTagsMagic'] = __DIR__ . 
'/PhpTags.i18n.magic.php';
 
-// Specify the function that will initialize the parser function.
-/**
- * @codeCoverageIgnore
- */
-$wgHooks['ParserFirstCallInit'][] = function( Parser &$parser ) {
-       $parser->setFunctionHook( 'phptag', 'PhpTags::renderFunction', 
Parser::SFH_OBJECT_ARGS );
-       $parser->setHook( 'phptag', 'PhpTags::render' );
-       return true;
-};
-
-$wgHooks['PhpTagsRuntimeFirstInit'][] = 'PhpTags::onPhpTagsRuntimeFirstInit';
-$wgHooks['CodeMirrorGetAdditionalResources'][] = 
'PhpTags::onCodeMirrorGetAdditionalResources';
-
-$wgPhpTagsLimitReport = false;
-$wgPhpTagsCounter = 0;
-
-/**
- * @codeCoverageIgnore
- */
-$wgHooks['ParserLimitReport'][] = function( $parser, &$limitReport ) use ( 
&$wgPhpTagsLimitReport ) {
-       if ( $wgPhpTagsLimitReport !== false ) {
-               $limitReport .= $wgPhpTagsLimitReport;
-               $wgPhpTagsLimitReport = false;
-       }
-       return true;
-};
-
-/**
- * @codeCoverageIgnore
- */
-$wgHooks['ParserAfterTidy'][] = function ( &$parser, &$text ) use ( 
&$wgPhpTagsCounter ) {
-       if ( $wgPhpTagsCounter > 0 ) {
-               \PhpTags::onParserAfterTidy( $parser, $text );
-       }
-       return true;
-};
-
 // Preparing classes for autoloading
-$wgAutoloadClasses['PhpTags'] = __DIR__ . '/PhpTags.body.php';
-
+$wgAutoloadClasses['PhpTags\\Renderer'] = __DIR__ . '/includes/Renderer.php';
+$wgAutoloadClasses['PhpTags\\Timer'] = __DIR__ . '/includes/Renderer.php';
 $wgAutoloadClasses['PhpTags\\iRawOutput'] = __DIR__ . 
'/includes/iRawOutput.php';
 $wgAutoloadClasses['PhpTags\\outPrint'] = __DIR__ . '/includes/outPrint.php';
 $wgAutoloadClasses['PhpTags\\ErrorHandler'] = __DIR__ . 
'/includes/ErrorHandler.php';
@@ -94,23 +57,65 @@
 $wgTrackingCategories[] = 'phptags-compiler-error-category';
 $wgTrackingCategories[] = 'phptags-runtime-error-category';
 
+$wgHooks['ParserFirstCallInit'][] = 'PhpTagsRegisterParserFunctions';
+$wgHooks['PhpTagsRuntimeFirstInit'][] = 
'PhpTags\\Renderer::onPhpTagsRuntimeFirstInit';
+$wgHooks['CodeMirrorGetAdditionalResources'][] = 
'PhpTags\\Renderer::onCodeMirrorGetAdditionalResources';
+$wgHooks['ParserLimitReport'][] = 'PhpTagsLimitReport';
+$wgHooks['ParserAfterTidy'][] = 'PhpTagsParserAfterTidy';
+$wgHooks['ExtensionTypes'][] = 'PhpTagsRegisterExtensionType';
+$wgHooks['UnitTestsList'][] = 'PhpTagsRegisterUnitTests';
+
+/**
+ * @codeCoverageIgnore
+ */
+function PhpTagsRegisterParserFunctions( Parser &$parser ) {
+       $parser->setFunctionHook( 'phptag', 
'PhpTags\\Renderer::runParserFunction', Parser::SFH_OBJECT_ARGS );
+       $parser->setHook( 'phptag', 'PhpTags\\Renderer::runTagHook' );
+       return true;
+}
+
+/**
+ * @codeCoverageIgnore
+ */
+function PhpTagsParserAfterTidy ( &$parser, &$text ) {
+       global $wgPhpTagsCounter;
+       if ( $wgPhpTagsCounter > 0 ) {
+               \PhpTags\Renderer::onParserAfterTidy( $parser, $text );
+       }
+       return true;
+}
+$wgPhpTagsLimitReport = false;
+$wgPhpTagsCounter = 0;
+
+/**
+ * @codeCoverageIgnore
+ */
+function PhpTagsLimitReport( $parser, &$limitReport ) {
+       global $wgPhpTagsLimitReport;
+       if ( $wgPhpTagsLimitReport !== false ) {
+               $limitReport .= $wgPhpTagsLimitReport;
+               $wgPhpTagsLimitReport = false;
+       }
+       return true;
+}
+
 // Register extension type for Special:Version used for PhpTags extensions
 /**
  * @codeCoverageIgnore
  */
-$wgHooks['ExtensionTypes'][] = function( &$extTypes ) {
+function PhpTagsRegisterExtensionType( &$extTypes ) {
        $extTypes['phptags'] = wfMessage( 'phptags-extension-type' )->text();
-};
+}
 
 /**
  * Add files to phpunit test
  * @codeCoverageIgnore
  */
-$wgHooks['UnitTestsList'][] = function ( &$files ) {
+function PhpTagsRegisterUnitTests( &$files ) {
        $testDir = __DIR__ . '/tests/phpunit';
        $files = array_merge( $files, glob( "$testDir/includes/*Test.php" ) );
        return true;
-};
+}
 
 $wgParserTestFiles[] = __DIR__ . '/tests/parser/PhpTagsTests.txt';
 
@@ -138,6 +143,6 @@
 
 /**
  * Storage time of the compiled bytecode at cache
- * By default is 30 days
+ * By default it is 30 days
  */
-$wgPhpTagsBytecodeExptime = 86400 * 30;
+$wgPhpTagsBytecodeExptime = 2592000;
diff --git a/includes/Compiler.php b/includes/Compiler.php
index 2f8f381..1b755f1 100644
--- a/includes/Compiler.php
+++ b/includes/Compiler.php
@@ -1292,6 +1292,7 @@
                                                                        
PHPTAGS_STACK_PARAM => array( &$array ) ,
                                                                        
PHPTAGS_STACK_PARAM_2 => array( array(null, null) ),
                                                                        
PHPTAGS_STACK_RESULT => null,
+                                                                       
PHPTAGS_STACK_TOKEN_LINE => $this->tokenLine,
                                                                );
                                                        } else {
                                                                
$result[PHPTAGS_STACK_PARAM][$r] = &$array;
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 572999e..64f4177 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -107,11 +107,11 @@
                                $cached['JSONLOADER'] === 
PHPTAGS_JSONLOADER_RELEASE  &&
                                $cached['jsonFiles'] === self::$jsonFiles &&
                                $cached['callbackConstants'] === 
self::$callbackConstants ) {
-                       \wfDebug( '[phptags] ' . __METHOD__ . '() using cache: 
yes' );
+                       \wfDebugLog( 'PhpTags', __METHOD__ . '() using cache: 
yes' );
                        $data = $cached;
                }
                if ( $data === false ) {
-                       \wfDebug( '[phptags] ' .  __METHOD__ . '() using cache: 
NO' );
+                       \wfDebugLog( 'PhpTags',  __METHOD__ . '() using cache: 
NO' );
                        $data = JsonLoader::load( self::$jsonFiles );
                        $data['constantValues'] += self::loadConstantValues();
                        $data['jsonFiles'] = self::$jsonFiles;
@@ -287,13 +287,9 @@
        private static function callFunction( $arguments, $calledFuncName ) {
                $funcKey = strtolower( $calledFuncName );
                $funcClass = self::getFunctionClass( $funcKey );
-               $e = self::checkFunctionArguments( $funcKey, $arguments );
-               if ( $e === true ) {
-                       ksort( $arguments );
-                       return call_user_func_array( array($funcClass, 
"f_$calledFuncName"), $arguments );
-               }
-               Runtime::pushException( $e );
-               return self::getCallInfo( self::INFO_RETURNS_ON_FAILURE );
+               ksort( $arguments );
+               self::checkFunctionArguments( $funcKey, $arguments );
+               return call_user_func_array( array($funcClass, 
"f_$calledFuncName"), $arguments );
        }
 
        /**
@@ -305,41 +301,25 @@
         * @throws PhpTagsException
         */
        private static function callObjectsMethod( $arguments, 
$calledMethodName, $calledObject ) {
-               if ( $calledObject instanceof GenericObject ) {
-                       ksort( $arguments );
-                       $e = self::checkObjectArguments( 
$calledObject->getObjectKey(), $calledMethodName, false, $arguments );
-                       if ( $e === true ) {
-                               return call_user_func_array( 
array($calledObject, "m_$calledMethodName"), $arguments );
-                       }
-                       if ( $e instanceof \PhpTags\PhpTagsException ) {
-                               Runtime::pushException( $e );
-                       }
-                       Runtime::pushException( $e );
-                       return self::getCallInfo( self::INFO_RETURNS_ON_FAILURE 
);
+               if ( false === $calledObject instanceof GenericObject ) {
+                       throw new PhpTagsException( 
PhpTagsException::FATAL_CALL_FUNCTION_ON_NON_OBJECT );
                }
-               throw new PhpTagsException( 
PhpTagsException::FATAL_CALL_FUNCTION_ON_NON_OBJECT );
+               ksort( $arguments );
+               self::checkObjectArguments( $calledObject->getObjectKey(), 
$calledMethodName, false, $arguments );
+               return call_user_func_array( array($calledObject, 
"m_$calledMethodName"), $arguments );
        }
 
        private static function callStaticMethod( $arguments, 
$calledMethodName, $calledObject ) {
-               ksort( $arguments );
-
                if ( $calledObject instanceof GenericObject ) {
                        $objectKey = $calledObject->getObjectKey();
                } else {
                        $objectKey = strtolower( $calledObject );
                }
 
-               $e = self::checkObjectArguments( $objectKey, $calledMethodName, 
true, $arguments );
-
-               if ( $e === true ) {
-                       $className = self::getClassNameByObjectName( $objectKey 
);
-                       return call_user_func_array( array($className, 
"s_$calledMethodName"), $arguments );
-               }
-               if ( $e instanceof \PhpTags\PhpTagsException ) {
-                       Runtime::pushException( $e );
-               }
-               Runtime::pushException( $e );
-               return self::getCallInfo( self::INFO_RETURNS_ON_FAILURE );
+               ksort( $arguments );
+               self::checkObjectArguments( $objectKey, $calledMethodName, 
true, $arguments );
+               $className = self::getClassNameByObjectName( $objectKey );
+               return call_user_func_array( array($className, 
"s_$calledMethodName"), $arguments );
        }
 
        public static function callGetObjectsConstant( $name, $object ) {
@@ -362,11 +342,11 @@
         * @throws PhpTagsException
         */
        public static function callGetObjectsProperty( $name, $object ) {
-               if ( $object instanceof GenericObject ) {
-                       $handler = "p_$name";
-                       return $object->$handler();
+               if ( false === $object instanceof GenericObject ) {
+                       Runtime::pushException( new PhpTagsException( 
PhpTagsException::NOTICE_GET_PROPERTY_OF_NON_OBJECT ) );
                }
-               Runtime::pushException( new PhpTagsException( 
PhpTagsException::NOTICE_GET_PROPERTY_OF_NON_OBJECT ) );
+               $handler = "p_$name";
+               return $object->$handler();
        }
 
        public static function callGetStaticProperty( $name, $object ) {
@@ -382,16 +362,12 @@
        }
 
        public static function callSetObjectsProperty( $name, $object, $value ) 
{
-               if ( $object instanceof GenericObject ) {
-                       $handler = "b_$name";
-                       $validValue = self::getValidPropertyValue( 
$object->getObjectKey(), $name, false, $value );
-                       if ( $validValue instanceof PhpTagsException ) {
-                               Runtime::pushException( $validValue );
-                               return;
-                       }
-                       return $object->$handler( $validValue );
+               if ( false === $object instanceof GenericObject ) {
+                       Runtime::pushException( new PhpTagsException( 
PhpTagsException::WARNING_ATTEMPT_TO_ASSIGN_PROPERTY ) );
                }
-               Runtime::pushException( new PhpTagsException( 
PhpTagsException::WARNING_ATTEMPT_TO_ASSIGN_PROPERTY ) );
+               $handler = "b_$name";
+               $validValue = self::getValidPropertyValue( 
$object->getObjectKey(), $name, false, $value );
+               return $object->$handler( $validValue );
        }
 
        public static function callSetStaticProperty( $name, $object, $value ) {
@@ -404,10 +380,6 @@
                $className = self::getClassNameByObjectName( $objectKey );
                $handler = "d_$name";
                $validValue = self::getValidPropertyValue( $objectKey, $name, 
true, $value );
-               if ( $validValue instanceof PhpTagsException ) {
-                       Runtime::pushException( $validValue );
-                       return;
-               }
                return $className::$handler( $validValue );
        }
 
@@ -415,10 +387,10 @@
         *
         * @param array $arguments
         * @param string $calledObjectName
-        * @param bool $showException
         * @return \PhpTags\GenericObject
+        * @throws PhpTagsException
         */
-       public static function createObject( $arguments, $calledObjectName, 
$showException = true ) {
+       public static function createObject( $arguments, $calledObjectName ) {
                self::$value = array(
                        PHPTAGS_STACK_HOOK_TYPE => PHPTAGS_HOOK_NEW_OBJECT,
                        PHPTAGS_STACK_PARAM => '__construct',
@@ -432,25 +404,18 @@
                ksort( $arguments );
 
                try {
-                       $e = self::checkObjectArguments( $objectKey, 
'__construct', false, $arguments );
-                       if ( $e === true && call_user_func_array( 
array($newObject, 'm___construct'), $arguments ) === true ) {
-                               return $newObject;
-                       }
-                       if ( $e instanceof \PhpTags\PhpTagsException ) {
-                               Runtime::pushException( $e );
-                       }
+                       self::checkObjectArguments( $objectKey, '__construct', 
false, $arguments );
+                       call_user_func_array( array($newObject, 
'm___construct'), $arguments );
                } catch ( \PhpTags\PhpTagsException $exc) {
-                       Runtime::pushException( $exc );
+                       throw $exc;
                } catch ( \Exception $exc ) {
-                       if ( $showException ) {
-                               list(, $message) = explode( ': ', 
$exc->getMessage(), 2 );
-                               if ( $message == '' ) {
-                                       $message = $exc->getMessage();
-                               }
-                               Runtime::pushException( new PhpTagsException( 
PhpTagsException::FATAL_OBJECT_NOT_CREATED, $message ) );
+                       list(, $message) = explode( ': ', $exc->getMessage(), 2 
);
+                       if ( $message == '' ) {
+                               $message = $exc->getMessage();
                        }
+                       throw new PhpTagsException( 
PhpTagsException::FATAL_OBJECT_NOT_CREATED, $message );
                }
-               return false;
+               return $newObject;
        }
 
        private static function getClassNameByObjectName( $objectKey ) {
@@ -519,14 +484,14 @@
        private static function checkArguments( $expects, $arguments ) {
                $argCount = count( $arguments );
                if( true === isset( $expects[self::EXPECTS_EXACTLY_PARAMETERS] 
) && $argCount != $expects[self::EXPECTS_EXACTLY_PARAMETERS] ) {
-                       return new PhpTagsException( 
PhpTagsException::WARNING_EXPECTS_EXACTLY_PARAMETER, 
array($expects[self::EXPECTS_EXACTLY_PARAMETERS], $argCount) );
+                       throw new PhpTagsException( 
PhpTagsException::WARNING_EXPECTS_EXACTLY_PARAMETER, 
array($expects[self::EXPECTS_EXACTLY_PARAMETERS], $argCount) );
                } else {
                        if ( true == isset( 
$expects[self::EXPECTS_MAXIMUM_PARAMETERS] ) && $argCount > 
$expects[self::EXPECTS_MAXIMUM_PARAMETERS] ) {
-                               return new PhpTagsException(
+                               throw new PhpTagsException(
                                        
PhpTagsException::WARNING_EXPECTS_AT_MOST_PARAMETERS, 
array($expects[self::EXPECTS_MAXIMUM_PARAMETERS], $argCount) );
                        }
                        if ( true == isset( 
$expects[self::EXPECTS_MINIMUM_PARAMETERS] ) && $argCount < 
$expects[self::EXPECTS_MINIMUM_PARAMETERS] ) {
-                               return new PhpTagsException( 
PhpTagsException::WARNING_EXPECTS_AT_LEAST_PARAMETER, 
array($expects[self::EXPECTS_MINIMUM_PARAMETERS], $argCount) );
+                               throw new PhpTagsException( 
PhpTagsException::WARNING_EXPECTS_AT_LEAST_PARAMETER, 
array($expects[self::EXPECTS_MINIMUM_PARAMETERS], $argCount) );
                        }
                }
 
@@ -587,15 +552,14 @@
                        }
                }
 
-               if ( $error === false ) {
-                       return true;
+               if ( $error !== false ) {
+                       $type = $arguments[$i] instanceof GenericObject ? 
$arguments[$i]->getName() : gettype( $arguments[$i] );
+                       throw new PhpTagsException(
+                                       
PhpTagsException::WARNING_EXPECTS_PARAMETER,
+                                       array( $i+1, $error, $type )
+                               );
                }
-
-               $type = $arguments[$i] instanceof GenericObject ? 
$arguments[$i]->getName() : gettype( $arguments[$i] );
-               return new PhpTagsException(
-                               PhpTagsException::WARNING_EXPECTS_PARAMETER,
-                               array( $i+1, $error, $type )
-                       );
+               return true;
        }
 
        private static function getValidPropertyValue( $objectKey, 
$calledPropertyName, $isStatic, $value ) {
@@ -604,7 +568,7 @@
                if ( isset(self::$objects[$objectKey][$point][$propertyKey]) ) {
                        $expect = 
self::$objects[$objectKey][$point][$propertyKey][0];
                } elseif( $isStatic === false ) {
-                       return new PhpTagsException( 
PhpTagsException::NOTICE_UNDEFINED_PROPERTY );
+                       throw new PhpTagsException( 
PhpTagsException::NOTICE_UNDEFINED_PROPERTY );
                } else {
                        throw new PhpTagsException( 
PhpTagsException::FATAL_ACCESS_TO_UNDECLARED_STATIC_PROPERTY );
                }
@@ -659,11 +623,10 @@
                                break;
                }
 
-               if ( $error === false ) {
-                       return $value;
+               if ( $error !== false ) {
+                       throw new PhpTagsException( 
PhpTagsException::NOTICE_EXPECTS_PROPERTY, array($error, gettype( $value )) );
                }
-
-               return new PhpTagsException( 
PhpTagsException::NOTICE_EXPECTS_PROPERTY, array($error, gettype( $value )) );
+               return $value;
        }
 
        public static function hasProperty( $objectKey, $propertyName ) {
diff --git a/includes/PhpTagsException.php b/includes/PhpTagsException.php
index 1c4b7e1..180080c 100644
--- a/includes/PhpTagsException.php
+++ b/includes/PhpTagsException.php
@@ -188,6 +188,9 @@
                        case self::FATAL_DENIED_FOR_NAMESPACE:
                                $message = wfMessage( 
'phptags-disabled-for-namespace', $arguments )->text();
                                break;
+                       case self::WARNING_ILLEGAL_OFFSET_TYPE:
+                               $message = 'Illegal offset type';
+                               break;
                        default:
                                $message = "Undefined error, code 
{$this->code}";
                                $this->code = self::EXCEPTION_FATAL * 1000;
@@ -254,6 +257,7 @@
        const WARNING_ATTEMPT_TO_ASSIGN_PROPERTY = 3012; // PHP Warning:  
Attempt to assign property of non-object
        const WARNING_EXPECTS_AT_MOST_PARAMETERS = 3013; // PHP Warning:  
round() expects at most 3 parameters, 4 given
        const WARNING_TOO_MANY_ARGUMENTS = 3014; //Warning: Too many arguments 
for date_format(), expected 2
+       const WARNING_ILLEGAL_OFFSET_TYPE = 3015; // PHP Warning:  Illegal 
offset type
 
        const EXCEPTION_FATAL = 4;
        const FATAL_CANNOT_USE_FOR_READING = 4001;  // PHP Fatal error:  Cannot 
use [] for reading in Command line code on line 1
diff --git a/includes/Renderer.php b/includes/Renderer.php
new file mode 100644
index 0000000..48bc336
--- /dev/null
+++ b/includes/Renderer.php
@@ -0,0 +1,401 @@
+<?php
+namespace PhpTags;
+
+/**
+ * The main class of the extension PhpTags.
+ *
+ * @file Renderer.php
+ * @ingroup PhpTags
+ * @author Pavel Astakhov <[email protected]>
+ * @licence GNU General Public Licence 2.0 or later
+ */
+class Renderer {
+
+       static $cacheHit = 0;
+       static $memoryHit = 0;
+       static $compileHit = 0;
+       static $needInitRuntime = true;
+
+       static $globalVariablesScript = array();
+
+       /**
+        * Array of PPFrame
+        * @var array
+        */
+       private static $frame = array();
+
+       /**
+        * Parser
+        * @var \Parser
+        */
+       private static $parser;
+
+       private static $bytecodeNeedsUpdate = array();
+       private static $bytecodeCache = array();
+       private static $bytecodeLoaded = array();
+       private static $parserCacheDisabled = false;
+       private static $errorCategoryAdded = false;
+
+       /**
+        *
+        * @param \Parser $parser
+        * @param \PPFrame $frame
+        * @param array $args
+        */
+       public static function runParserFunction( $parser, $frame, $args ) {
+               wfProfileIn( __METHOD__ );
+               Timer::start( $parser );
+
+               $command = array_shift($args);
+               if ( count( $args ) > 0 ) {
+                       foreach ( $args as &$value ) {
+                               $value = $frame->expand( $value );
+                       }
+                       $command = "echo $command (" . implode( ',', $args ) . 
');';
+               } elseif ( preg_match( '/^\S+$/', $command ) == 1 ) {
+                       $command = "echo $command;";
+               }
+
+               $frameTitle = $frame->getTitle();
+               $frameTitleText = $frameTitle->getPrefixedText();
+               $arguments = array( $frameTitleText ) + $frame->getArguments();
+               $scope = self::getScopeID( $frame );
+
+               try {
+                       $bytecode = self::getBytecode( $command, $parser, 
$frame, $frameTitle, $frameTitleText );
+                       $result = Runtime::run( $bytecode, $arguments, $scope );
+                       $return = implode( $result );
+               } catch ( PhpTagsException $exc ) {
+                       $return = (string) $exc;
+               } catch ( \MWException $exc ) {
+                       throw $exc;
+               } catch ( \Exception $exc ) {
+                       $return = $exc->getTraceAsString();
+               }
+
+               Timer::stop( $parser );
+
+               wfProfileOut( __METHOD__ );
+               return \UtfNormal::cleanUp( $return );
+       }
+
+       public static function runTagHook( $input, array $args, \Parser 
$parser, \PPFrame $frame ) {
+               wfProfileIn( __METHOD__ );
+               Timer::start( $parser );
+
+               $frameTitle = $frame->getTitle();
+               $frameTitleText = $frameTitle->getPrefixedText();
+               $arguments = array( $frameTitleText ) + $frame->getArguments();
+               $scope = self::getScopeID( $frame );
+
+               try {
+                       $bytecode = self::getBytecode( $input, $parser, $frame, 
$frameTitle, $frameTitleText );
+                       $result = Runtime::run( $bytecode, $arguments, $scope );
+               } catch ( PhpTagsException $exc ) {
+                       $result = array( (string) $exc );
+                       $parser->addTrackingCategory( 
'phptags-compiler-error-category' );
+               } catch ( \MWException $exc ) {
+                       throw $exc;
+               } catch ( \Exception $exc ) {
+                       $result = array( $exc->getTraceAsString() );
+               }
+
+               Timer::stop( $parser );
+               $return = self::insertGeneral( $parser, 
$parser->recursiveTagParse( implode($result), $frame ) );
+               wfProfileOut( __METHOD__ );
+               return $return;
+       }
+
+       /**
+        *
+        * @global int $wgPhpTagsBytecodeExptime
+        * @param string $source
+        * @param \Parser $parser
+        * @param \PPFrame $frame
+        * @param \Title $frameTitle
+        * @param string $frameTitleText
+        * @return array
+        */
+       private static function getBytecode( $source, $parser, $frame, 
$frameTitle, $frameTitleText ) {
+               global $wgPhpTagsBytecodeExptime, $wgPhpTagsCounter;
+               $wgPhpTagsCounter++;
+
+               static $parserTitle = false;
+               if ( $parserTitle === false ) {
+                       $parserTitle = $parser->getTitle();
+               }
+               $revID = $parserTitle === $frameTitle ? 
$parser->getRevisionId() : $frameTitle->getLatestRevID();
+               $md5Source = md5( $source );
+
+               self::initialize( $parser, $frame, $frameTitle );
+
+               if ( true === isset( self::$bytecodeCache[$revID][$md5Source] ) 
) {
+                       \wfDebugLog( 'PhpTags', 'Memory hiting with key ' . 
$revID );
+                       self::$memoryHit++;
+                       return self::$bytecodeCache[$revID][$md5Source];
+               }
+
+               if ( $wgPhpTagsBytecodeExptime > 0 && $revID > 0 && false === 
isset( self::$bytecodeLoaded[$revID] ) ) {
+                       $cache = \wfGetCache( CACHE_ANYTHING );
+                       $key = \wfMemcKey( 'PhpTags', $revID );
+                       $data = $cache->get( $key );
+                       self::$bytecodeLoaded[$revID] = true;
+                       if ( $data !== false && $data[0] === 
PHPTAGS_RUNTIME_RELEASE ) {
+                               self::$bytecodeCache[$revID] = $data[1];
+                               if ( true === isset( 
self::$bytecodeCache[$revID][$md5Source] ) ) {
+                                       \wfDebugLog( 'PhpTags', 'Cache hiting 
with key ' . $revID );
+                                       self::$cacheHit++;
+                                       return 
self::$bytecodeCache[$revID][$md5Source];
+                               }
+                       }
+                       \wfDebugLog( 'PhpTags', 'Cache missing with key ' . 
$revID );
+               }
+
+               $bytecode = Compiler::compile( $source, $frameTitleText );
+               self::$bytecodeCache[$revID][$md5Source] = $bytecode;
+               if ( $revID > 0 ) { // Don't save bytecode of unsaved pages
+                       self::$bytecodeNeedsUpdate[$revID][$md5Source] = 
unserialize( serialize( $bytecode ) );
+               }
+
+               self::$compileHit++;
+               Timer::addCompileTime( $parser );
+               return $bytecode;
+       }
+
+       /**
+        *
+        * @param \Parser $parser
+        * @param \PPFrame $frame
+        * @throws \PhpTags\PhpTagsException
+        * @return null
+        */
+       private static function initialize( $parser, $frame, $frameTitle ) {
+               global $wgPhpTagsNamespaces, $wgPhpTagsMaxLoops;
+               if ( true !== $wgPhpTagsNamespaces && false === isset( 
$wgPhpTagsNamespaces[$frameTitle->getNamespace()] ) ) {
+                       throw new PhpTagsException( 
PhpTagsException::FATAL_DENIED_FOR_NAMESPACE, $frameTitle->getNsText() );
+               }
+
+               if ( true === self::$needInitRuntime ) {
+                       \wfDebugLog( 'PhpTags', 'Run hook 
PhpTagsRuntimeFirstInit' );
+                       \wfRunHooks( 'PhpTagsRuntimeFirstInit' );
+                       Hooks::loadData();
+                       Runtime::$loopsLimit = $wgPhpTagsMaxLoops;
+                       self::$needInitRuntime = false;
+               }
+
+               self::$parser = $parser;
+               array_unshift( self::$frame, $frame );
+       }
+
+       private static function updateBytecodeCache() {
+               global $wgPhpTagsBytecodeExptime;
+
+               $cache = \wfGetCache( CACHE_ANYTHING );
+               foreach ( self::$bytecodeNeedsUpdate as $revID => $data ) {
+                       $key = wfMemcKey( 'PhpTags', $revID );
+                       $cache->set( $key, array(PHPTAGS_RUNTIME_RELEASE, 
$data), $wgPhpTagsBytecodeExptime );
+                       \wfDebugLog( 'PhpTags', 'Save compiled bytecode to 
cache with key ' . $revID );
+               }
+               self::$bytecodeNeedsUpdate = array();
+       }
+
+       public static function reset() {
+               self::writeLimitReport();
+
+               global $wgPhpTagsCounter;
+               $wgPhpTagsCounter = 0;
+               Runtime::reset();
+               Timer::reset();
+               self::$bytecodeCache = array();
+               self::$bytecodeLoaded = array();
+               self::$globalVariablesScript = array();
+               self::$parserCacheDisabled = false;
+               self::$errorCategoryAdded = false;
+       }
+
+       /**
+        *
+        * @param \Parser $parser
+        * @param string $text
+        * @return string
+        */
+       private static function insertGeneral( \Parser $parser, $text ) {
+               return $parser->insertStripItem( $text );
+       }
+
+       private static function getScopeID( \PPFrame $frame ) {
+               static $frames = array(), $scope = 0;
+
+               foreach ( $frames as $value ) {
+                       if ( $value[0] === $frame ) {
+                               return $value[1];
+                       }
+               }
+               $frames[] = array( $frame, $scope );
+               return $scope++;
+       }
+
+       /**
+        *
+        * @param array $extResources
+        * @param array $extMode
+        */
+       public static function onCodeMirrorGetAdditionalResources( 
&$extResources, &$extMode ) {
+               $extResources['scripts']['lib/codemirror/mode/php/php.js'] = 
true;
+               
$extResources['scripts']['lib/codemirror/mode/htmlmixed/htmlmixed.js'] = true;
+               $extResources['scripts']['lib/codemirror/mode/xml/xml.js'] = 
true;
+               
$extResources['scripts']['lib/codemirror/mode/javascript/javascript.js'] = true;
+               $extResources['scripts']['lib/codemirror/mode/css/css.js'] = 
true;
+               $extResources['scripts']['lib/codemirror/mode/clike/clike.js'] 
= true;
+
+               $extMode['tag']['phptag'] = 'text/x-php';
+
+               return true;
+       }
+
+       public static function onPhpTagsRuntimeFirstInit() {
+               Hooks::addJsonFile( __DIR__ . '/../PhpTags.json', 
PHPTAGS_VERSION );
+               return true;
+       }
+
+       public static function writeLimitReport() {
+               global $wgPhpTagsCounter, $wgPhpTagsLimitReport;
+
+               $time = Timer::getRunTime();
+               $compileTime = Timer::getCompileTime();
+               $wgPhpTagsLimitReport = sprintf(
+                               '
+PhpTags usage count: %d
+Runtime : %.3f sec
+Compiler: %.3f sec ( usage: %d, cache: %d, memory: %d )
+Total   : %.3f sec
+
+',
+                               $wgPhpTagsCounter,
+                               $time - $compileTime,
+                               $compileTime,
+                               self::$compileHit,
+                               self::$cacheHit,
+                               self::$memoryHit,
+                               $time
+                       );
+               return true;
+       }
+
+       public static function onParserAfterTidy( $parser, &$text ) {
+               global $wgPhpTagsBytecodeExptime;
+               wfProfileIn( __METHOD__ );
+
+               if ( self::$globalVariablesScript ) {
+                       $vars = array();
+                       foreach ( self::$globalVariablesScript as $key=> $value 
) {
+                               $vars["ext.phptags.$key"] = $value;
+                       }
+                       $text .= Html::inlineScript(
+                               ResourceLoader::makeLoaderConditionalScript(
+                                       ResourceLoader::makeConfigSetScript( 
$vars )
+                               )
+                       );
+               }
+               if ( $wgPhpTagsBytecodeExptime > 0 && 
self::$bytecodeNeedsUpdate ) {
+                       self::updateBytecodeCache();
+               }
+               self::reset();
+
+               wfProfileOut( __METHOD__ );
+               return true;
+       }
+
+       /**
+        * Returns Parser
+        * @return \Parser
+        */
+       public static function getParser() {
+               return self::$parser;
+       }
+
+       /**
+        * Returns Frame
+        * @return \PPFrame
+        */
+       public static function getFrame() {
+               return self::$frame[0];
+       }
+
+       /**
+        * Set a flag in the output object indicating that the content is 
dynamic and
+        * shouldn't be cached.
+        * @global \OutputPage $wgOut
+        * @staticvar boolean $done
+        * @return null
+        */
+       public static function disableParserCache() {
+               if ( self::$parserCacheDisabled === true ) {
+                       return;
+               }
+
+               global $wgOut;
+
+               self::$parser->disableCache();
+               $wgOut->enableClientCache( false );
+               self::$parserCacheDisabled = true;
+       }
+
+       public static function addRuntimeErrorCategory() {
+               if ( self::$errorCategoryAdded === true || self::$parser === 
null ) {
+                       return;
+               }
+
+               self::$parser->addTrackingCategory( 
'phptags-runtime-error-category' );
+               self::$errorCategoryAdded = true;
+       }
+
+       /**
+        * Increment the expensive function count
+        * @param string $functionName
+        * @return null
+        * @throws PhpTagsException
+        */
+       public static function incrementExpensiveFunctionCount() {
+               if ( true !== self::$parser->incrementExpensiveFunctionCount() 
) {
+                       throw new PhpTagsException( 
PhpTagsException::FATAL_CALLED_MANY_EXPENSIVE_FUNCTION );
+               }
+       }
+
+}
+
+class Timer {
+       private static $times = array();
+       private static $runTime = 0;
+       private static $compile = 0;
+
+       public static function start( $parser ) {
+               array_unshift( self::$times, 
$parser->mOutput->getTimeSinceStart( 'cpu' ) );
+       }
+
+       public static function stop( $parser ) {
+               if ( false === isset(self::$times[1]) ) {
+                       self::$runTime += $parser->mOutput->getTimeSinceStart( 
'cpu' ) - self::$times[0];
+               }
+               array_shift( self::$times );
+       }
+
+       public static function addCompileTime( $parser ) {
+               self::$compile += $parser->mOutput->getTimeSinceStart( 'cpu' ) 
- self::$times[0];
+       }
+
+       public static function getRunTime() {
+               return self::$runTime;
+       }
+
+       public static function getCompileTime() {
+               return self::$compile;
+       }
+
+       public static function reset() {
+               self::$times = array();
+               self::$runTime = 0;
+               self::$compile = 0;
+       }
+
+}
diff --git a/includes/Runtime.php b/includes/Runtime.php
index a79063a..8d428ed 100644
--- a/includes/Runtime.php
+++ b/includes/Runtime.php
@@ -273,25 +273,20 @@
 
        public static $loopsLimit = 0;
 
-       /**
-        * PPFrame
-        * @var \PPFrame
-        */
-       static $frame;
-
-       /**
-        * Parser
-        * @var \Parser
-        */
-       static $parser;
-
        private static $variables = array();
        private static $staticVariables = array();
        private static $globalVariables = array();
        private static $ignoreErrors = false;
-       private static $scope;
-       private static $parserDisabled = false;
-       private static $errorCategoryAdded = false;
+       private static $stack = array();
+
+       const S_RETURN = 0;
+       const S_RUNNING = 1;
+       const S_RUN_INDEX = 2;
+       const S_COUNT = 3;
+       const S_LOOPS_OWNER = 4;
+       const S_MEMORY = 5;
+       const S_PLACE = 6;
+       const S_VARIABLES = 7;
 
        private static $operators = array(
                PHPTAGS_T_QUOTE => 'doQuote',
@@ -370,37 +365,25 @@
                self::$staticVariables = array();
                self::$globalVariables = array();
                self::$loopsLimit = $wgPhpTagsMaxLoops;
-               self::$parserDisabled = false;
-               self::$errorCategoryAdded = false;
+               self::$ignoreErrors = false;
        }
 
        public static function runSource( $code, array $args = array(), $scope 
= '' ) {
                return self::run( Compiler::compile($code), $args, $scope );
        }
 
-       /**
-        * The output cache
-        * @var array
-        */
-       private static $return;
-       private static $running;
-       private static $runIndex;
-       private static $c;
-       private static $loopsOwner;
-       private static $memory;
-       private static $place;
-       private static $thisVariables;
-
        private static function pushDown( $newCode, $newLoopsOwner, &$refReturn 
) {
-               self::$memory[] = array( &$refReturn, self::$running, 
self::$runIndex, self::$c, self::$loopsOwner );
-               self::$running = $newCode;
-               self::$runIndex = -1;
-               self::$c = count( $newCode );
-               self::$loopsOwner = $newLoopsOwner;
+               $stack =& self::$stack[0];
+               $stack[self::S_MEMORY][] = array( &$refReturn, 
$stack[self::S_RUNNING], $stack[self::S_RUN_INDEX], $stack[self::S_COUNT], 
$stack[self::S_LOOPS_OWNER] );
+               $stack[self::S_RUNNING] = $newCode;
+               $stack[self::S_RUN_INDEX] = -1;
+               $stack[self::S_COUNT] = count( $newCode );
+               $stack[self::S_LOOPS_OWNER] = $newLoopsOwner;
        }
 
        private static function popUp() {
-               list( self::$running[self::$runIndex][PHPTAGS_STACK_RESULT], 
self::$running, self::$runIndex, self::$c, self::$loopsOwner ) = array_pop( 
self::$memory );
+               $stack =& self::$stack[0];
+               list( $stack[self::S_RUNNING][ $stack[self::S_RUN_INDEX] 
][PHPTAGS_STACK_RESULT], $stack[self::S_RUNNING], $stack[self::S_RUN_INDEX], 
$stack[self::S_COUNT], $stack[self::S_LOOPS_OWNER] ) = array_pop( 
$stack[self::S_MEMORY] );
        }
 
        /**
@@ -593,9 +576,9 @@
         */
        private static function doPrint ( &$value ) {
                if( $value[PHPTAGS_STACK_PARAM] instanceof GenericObject ) {
-                       self::$return[] = 
$value[PHPTAGS_STACK_PARAM]->toString();
+                       self::$stack[0][self::S_RETURN][] = 
$value[PHPTAGS_STACK_PARAM]->toString();
                } else {
-                       self::$return[] = $value[PHPTAGS_STACK_PARAM];
+                       self::$stack[0][self::S_RETURN][] = 
$value[PHPTAGS_STACK_PARAM];
                }
        }
 
@@ -668,9 +651,10 @@
         * @param array $value
         */
        private static function doVariable ( &$value ) {
+               $variables =& self::$stack[0][self::S_VARIABLES];
                $aim = $value[PHPTAGS_STACK_AIM];
-               if ( isset(self::$thisVariables[ $value[PHPTAGS_STACK_PARAM] ]) 
|| array_key_exists($value[PHPTAGS_STACK_PARAM], self::$thisVariables) ) {
-                       $value[PHPTAGS_STACK_PARAM_2][$aim] =& 
self::$thisVariables[ $value[PHPTAGS_STACK_PARAM] ];
+               if ( isset( $variables[ $value[PHPTAGS_STACK_PARAM] ] ) || 
array_key_exists( $value[PHPTAGS_STACK_PARAM], $variables ) ) {
+                       $value[PHPTAGS_STACK_PARAM_2][$aim] =& $variables[ 
$value[PHPTAGS_STACK_PARAM] ];
                        if ( isset($value[PHPTAGS_STACK_ARRAY_INDEX]) ) { // 
Example: $foo[1]
                                foreach ( $value[PHPTAGS_STACK_ARRAY_INDEX] as 
$v ) {
                                        if ( is_array( 
$value[PHPTAGS_STACK_PARAM_2][$aim] ) ) { // Variable is array. Examle: $foo = 
['string']; echo $foo[0];
@@ -774,9 +758,10 @@
                        self::popUp();
                }
 
-               self::$thisVariables[ $value[PHPTAGS_STACK_PARAM] ] = $tmp[1]; 
// save value
+               $variables =& self::$stack[0][self::S_VARIABLES];
+               $variables[ $value[PHPTAGS_STACK_PARAM] ] = $tmp[1]; // save 
value
                if ( $value[PHPTAGS_STACK_PARAM_2] !== false ) { // 
T_DOUBLE_ARROW Example: while ( $foo as $key=>$value )
-                       self::$thisVariables[ $value[PHPTAGS_STACK_PARAM_2] ] = 
$tmp[0]; // save key
+                       $variables[ $value[PHPTAGS_STACK_PARAM_2] ] = $tmp[0]; 
// save key
                }
        }
 
@@ -785,8 +770,8 @@
         * @param array $value
         */
        private static function doBreak ( &$value ) {
-               $loopsOwner =& self::$loopsOwner;
-               $memory =& self::$memory;
+               $loopsOwner =& self::$stack[0][self::S_LOOPS_OWNER];
+               $memory =& self::$stack[0][self::S_MEMORY];
                $originalBreakLevel = $breakLevel = 
$value[PHPTAGS_STACK_RESULT];
 
                for ( ; $breakLevel > 0; ) {
@@ -811,8 +796,9 @@
                if( --self::$loopsLimit <= 0 ) {
                        throw new PhpTagsException( 
PhpTagsException::FATAL_LOOPS_LIMIT_REACHED, null );
                }
-               $loopsOwner =& self::$loopsOwner;
-               $memory =& self::$memory;
+               $stack =& self::$stack[0];
+               $loopsOwner =& $stack[self::S_LOOPS_OWNER];
+               $memory =& $stack[self::S_MEMORY];
                $originalBreakLevel = $value[PHPTAGS_STACK_RESULT];
                $breakLevel = $originalBreakLevel - 1;
 
@@ -829,7 +815,7 @@
                        }
                        self::popUp();
                }
-               self::$runIndex = -1;
+               $stack[self::S_RUN_INDEX] = -1;
        }
 
        /**
@@ -841,7 +827,13 @@
                $i = 1;
                foreach ( $value[PHPTAGS_STACK_PARAM_2] as $t ) {
                        list ( $k, $v ) = $t;
-                       $newArray[$k] = $v;
+
+                       if ( is_scalar( $k ) ) {
+                               $newArray[$k] = $v;
+                       } else {
+                               self::pushException( new PhpTagsException( 
PhpTagsException::WARNING_ILLEGAL_OFFSET_TYPE ) );
+                       }
+
                        if ( isset($value[PHPTAGS_STACK_PARAM][$i]) ) {
                                foreach ( $value[PHPTAGS_STACK_PARAM][$i] as $n 
) {
                                        $newArray[] = $n;
@@ -857,12 +849,13 @@
         * @param array $value
         */
        private static function doStatic ( &$value ) {
+               $place = self::$stack[0][self::S_PLACE];
                $name = $value[PHPTAGS_STACK_PARAM]; // variable name
-               if ( false === (isset( self::$staticVariables[self::$place] ) 
&& (isset( self::$staticVariables[self::$place][$name] ) || array_key_exists( 
$name, self::$staticVariables[self::$place] ))) ) {
+               if ( false === (isset( self::$staticVariables[$place] ) && 
(isset( self::$staticVariables[$place][$name] ) || array_key_exists( $name, 
self::$staticVariables[$place] ))) ) {
                        // It is not initialised variable, initialise it
-                       self::$staticVariables[self::$place][$name] = 
$value[PHPTAGS_STACK_RESULT];
+                       self::$staticVariables[$place][$name] = 
$value[PHPTAGS_STACK_RESULT];
                }
-               self::$thisVariables[$name] =& 
self::$staticVariables[self::$place][$name];
+               self::$stack[0][self::S_VARIABLES][$name] =& 
self::$staticVariables[$place][$name];
        }
 
        /**
@@ -870,12 +863,13 @@
         * @param array $value
         */
        private static function doGlobal ( &$value ) {
+               $stack =& self::$stack[0];
                $gVars =& self::$globalVariables;
                foreach( $value[PHPTAGS_STACK_PARAM] as $name ) { // variable 
names
                        if( !array_key_exists($name, $gVars) ) {
                                $gVars[$name] = null;
                        }
-                       self::$thisVariables[$name] =& $gVars[$name];
+                       $stack[self::S_VARIABLES][$name] =& $gVars[$name];
                }
        }
 
@@ -903,19 +897,11 @@
         * @param array $value
         */
        private static function doCallingHook ( &$value ) {
-               try {
-                       $result = Hooks::callHook( $value );
-               } catch ( \PhpTags\HookException $exc ) {
-                       if ( $exc->isFatal() ) {
-                               throw $exc;
-                       }
-                       self::pushException( $exc );
-                       $result = Hooks::getCallInfo( 
Hooks::INFO_RETURNS_ON_FAILURE );
-               }
+               $result = Hooks::callHook( $value );
 
                if ( $result instanceof outPrint ) {
                        $value[PHPTAGS_STACK_RESULT] = $result->returnValue;
-                       self::$return[] = $result;
+                       self::$stack[0][self::S_RETURN][] = $result;
                } else {
                        $value[PHPTAGS_STACK_RESULT] = $result;
                }
@@ -931,7 +917,8 @@
         * @param array $value
         */
        private static function doNewObject ( &$value ) {
-               $value[PHPTAGS_STACK_RESULT] = Hooks::createObject( 
$value[PHPTAGS_STACK_PARAM_2], $value[PHPTAGS_STACK_PARAM_3] );
+               $result = Hooks::createObject( $value[PHPTAGS_STACK_PARAM_2], 
$value[PHPTAGS_STACK_PARAM_3] );
+               $value[PHPTAGS_STACK_RESULT] = $result;
        }
 
        /**
@@ -939,12 +926,12 @@
         * @param array $value
         */
        private static function doUnset ( &$value ) {
-               $thisVariables =& self::$thisVariables;
+               $variables =& self::$stack[0][self::S_VARIABLES];
                foreach ( $value[PHPTAGS_STACK_PARAM] as $val ) {
                        $name = $val[PHPTAGS_STACK_PARAM]; // Variable Name
-                       if ( isset($thisVariables[$name]) || 
array_key_exists($name, $thisVariables) ) { // defined variable
+                       if ( isset($variables[$name]) || 
array_key_exists($name, $variables) ) { // defined variable
                                if ( isset($val[PHPTAGS_STACK_ARRAY_INDEX]) ) { 
// There is array index. Example: unset($foo[0])
-                                       $ref =& $thisVariables[$name];
+                                       $ref =& $variables[$name];
                                        $tmp = array_pop( 
$val[PHPTAGS_STACK_ARRAY_INDEX] );
                                        foreach ( 
$val[PHPTAGS_STACK_ARRAY_INDEX] as $v ) {
                                                if ( is_string($ref) ) {
@@ -960,7 +947,7 @@
                                                throw new PhpTagsException( 
PhpTagsException::FATAL_CANNOT_UNSET_STRING_OFFSETS, null );
                                        }
                                }else{ // There is no array index. Example: 
unset($foo)
-                                       unset( $thisVariables[$name] );
+                                       unset( $variables[$name] );
                                }
                        } elseif ( isset($val[PHPTAGS_STACK_ARRAY_INDEX]) ) { 
// undefined variable with array index. Example: unset($foo[1])
                                self::pushException( new PhpTagsException( 
PhpTagsException::NOTICE_UNDEFINED_VARIABLE, $name ) );
@@ -973,14 +960,14 @@
         * @param array $value
         */
        private static function doIsSet ( &$value ) {
-               $thisVariables =& self::$thisVariables;
+               $variables =& self::$stack[0][self::S_VARIABLES];
                foreach($value[PHPTAGS_STACK_PARAM] as $val) {
-                       if( !isset($thisVariables[ $val[PHPTAGS_STACK_PARAM] ]) 
) { // undefined variable or variable is null
+                       if( !isset($variables[ $val[PHPTAGS_STACK_PARAM] ]) ) { 
// undefined variable or variable is null
                                $value[PHPTAGS_STACK_RESULT] = false;
                                return;
                        } // true, variable is defined
                        if( isset($val[PHPTAGS_STACK_ARRAY_INDEX]) ) { // 
Example: isset($foo[1])
-                               $ref =& $thisVariables[ 
$val[PHPTAGS_STACK_PARAM] ];
+                               $ref =& $variables[ $val[PHPTAGS_STACK_PARAM] ];
                                $tmp = array_pop( 
$val[PHPTAGS_STACK_ARRAY_INDEX] );
                                foreach( $val[PHPTAGS_STACK_ARRAY_INDEX] as $v 
) {
                                        if( !isset($ref[$v]) ) { // undefined 
array index
@@ -1004,12 +991,12 @@
         * @param array $value
         */
        private static function doIsEmpty ( &$value ) {
-               $thisVariables =& self::$thisVariables;
+               $variables =& self::$stack[0][self::S_VARIABLES];
                foreach($value[PHPTAGS_STACK_PARAM] as $val) {
-                       if( !array_key_exists($val[PHPTAGS_STACK_PARAM], 
$thisVariables) ) { // undefined variable
+                       if( !array_key_exists($val[PHPTAGS_STACK_PARAM], 
$variables) ) { // undefined variable
                                continue;
                        }
-                       $ref =& $thisVariables[ $val[PHPTAGS_STACK_PARAM] ];
+                       $ref =& $variables[ $val[PHPTAGS_STACK_PARAM] ];
                        if( isset($val[PHPTAGS_STACK_ARRAY_INDEX]) ) { // 
Example: empty($foo[1])
                                $tmp = array_pop( 
$val[PHPTAGS_STACK_ARRAY_INDEX] );
                                foreach( $val[PHPTAGS_STACK_ARRAY_INDEX] as $v 
) {
@@ -1036,7 +1023,7 @@
         * @param array $value
         */
        private static function doReturn ( &$value ) {
-               self::$return = self::$return ? new PhpTagsException() : 
$value[PHPTAGS_STACK_PARAM];
+               self::$stack[0][self::S_RETURN] = 
self::$stack[0][self::S_RETURN] ? new PhpTagsException() : 
$value[PHPTAGS_STACK_PARAM];
        }
 
        /**
@@ -1199,18 +1186,18 @@
        }
 
        private static function & getVariableRef( $value ) {
-               $thisVariables =& self::$thisVariables;
-               $variable = $value[PHPTAGS_STACK_PARAM];
-               $variableName = $variable[PHPTAGS_STACK_PARAM];
-               if( !(isset($thisVariables[$variableName]) || 
array_key_exists($variableName, $thisVariables)) ) { // Use undefined variable
-                       $thisVariables[$variableName] = null;
+               $variables =& self::$stack[0][self::S_VARIABLES];
+               $var = $value[PHPTAGS_STACK_PARAM];
+               $variableName = $var[PHPTAGS_STACK_PARAM];
+               if( !(isset($variables[$variableName]) || 
array_key_exists($variableName, $variables)) ) { // Use undefined variable
+                       $variables[$variableName] = null;
                        if( $value[PHPTAGS_STACK_COMMAND] !== PHPTAGS_T_EQUAL ) 
{
                                self::pushException( new PhpTagsException( 
PhpTagsException::NOTICE_UNDEFINED_VARIABLE, $variableName ) );
                        }
                }
-               $ref =& $thisVariables[$variableName];
-               if ( isset($variable[PHPTAGS_STACK_ARRAY_INDEX]) ) { // 
Example: $foo[1]++
-                       foreach ( $variable[PHPTAGS_STACK_ARRAY_INDEX] as $v ) {
+               $ref =& $variables[$variableName];
+               if ( isset($var[PHPTAGS_STACK_ARRAY_INDEX]) ) { // Example: 
$foo[1]++
+                       foreach ( $var[PHPTAGS_STACK_ARRAY_INDEX] as $v ) {
                                if ( $v === INF ) { // Example: $foo[]
                                        $t = null;
                                        $ref[] = &$t;
@@ -1249,29 +1236,33 @@
        public static function run( $code, array $args, $scope = '' ) {
                set_error_handler( '\\PhpTags\\ErrorHandler::onError' );
                try {
-                       self::$scope = $scope;
-                       self::$running = $code;
-                       self::$memory = array();
-                       self::$return = array();
-                       self::$loopsOwner = null;
-                       self::$place = isset($args[0]) ? $args[0] : ''; // Page 
name for static variables and error messages
-                       self::$c = count( $code );
-                       if( false === isset(self::$variables[$scope]) ) {
+                       if( false === isset( self::$variables[$scope] ) ) {
                                self::$variables[$scope] = array();
                        }
-                       self::$thisVariables =& self::$variables[$scope];
-                       self::$thisVariables['argv'] = $args;
-                       self::$thisVariables['argc'] = count($args);
-                       self::$thisVariables['GLOBALS'] =& 
self::$globalVariables;
+                       $stack = array(
+                               self::S_RETURN => array(),
+                               self::S_RUNNING => $code,
+                               self::S_RUN_INDEX => -1,
+                               self::S_COUNT => count( $code ),
+                               self::S_LOOPS_OWNER => null,
+                               self::S_MEMORY => array(),
+                               self::S_PLACE => isset( $args[0] ) ? $args[0] : 
'', // Page name for static variables and error messages
+                               self::S_VARIABLES => & self::$variables[$scope],
+                       );
+                       $stack[self::S_VARIABLES]['argv'] = $args;
+                       $stack[self::S_VARIABLES]['argc'] = count( $args );
+                       $stack[self::S_VARIABLES]['GLOBALS'] =& 
self::$globalVariables;
 
-                       $runCode =& self::$running;
-                       $runIndex =& self::$runIndex;
-                       $loopsOwner =& self::$loopsOwner;
-                       $memory =& self::$memory;
-                       $c =& self::$c;
+                       $runCode =& $stack[self::S_RUNNING];
+                       $runIndex =& $stack[self::S_RUN_INDEX];
+                       $loopsOwner =& $stack[self::S_LOOPS_OWNER];
+                       $memory =& $stack[self::S_MEMORY];
+                       $c =& $stack[self::S_COUNT];
                        $operators = self::$operators;
 
-                       $runIndex = -1;
+                       array_unshift( self::$stack, null );
+                       self::$stack[0] =& $stack;
+doit:
                        do {
                                for ( ++$runIndex; $runIndex < $c; ++$runIndex 
) {
                                        $value =& $runCode[$runIndex];
@@ -1280,19 +1271,23 @@
                                }
                        } while( 
list($runCode[$runIndex][PHPTAGS_STACK_RESULT], $runCode, $runIndex, $c, 
$loopsOwner) = array_pop($memory) );
                } catch ( PhpTagsException $e ) {
-                       if ( self::$ignoreErrors ) {
-                               self::$ignoreErrors = false;
-                       } else {
-                               self::pushException( $e );
+                       self::pushException( $e );
+                       if ( $e->isFatal() !== true && ($call === 
$operators[PHPTAGS_T_HOOK] || $call === $operators[PHPTAGS_T_NEW]) ) {
+                               $runCode[$runIndex][PHPTAGS_STACK_RESULT] = 
Hooks::getCallInfo( Hooks::INFO_RETURNS_ON_FAILURE );
+                               goto doit;
                        }
-                       self::$running[self::$runIndex][PHPTAGS_STACK_RESULT] = 
null;
+                       $runCode[$runIndex][PHPTAGS_STACK_RESULT] = null;
+                       self::$ignoreErrors = false;
                } catch ( \Exception $e ) {
-                       self::addRuntimeErrorCategory();
+                       Renderer::addRuntimeErrorCategory();
                        restore_error_handler();
+                       self::$ignoreErrors = false;
+                       array_shift( self::$stack );
                        throw $e;
                }
                restore_error_handler();
-               return self::$return;
+               array_shift( self::$stack );
+               return $stack[self::S_RETURN];
        }
 
        static function fillList( &$values, &$parametrs, $offset = false ) {
@@ -1313,7 +1308,7 @@
                                continue;
                        }
                        // $param is variable
-                       $ref = &self::$thisVariables[ 
$param[PHPTAGS_STACK_PARAM] ];
+                       $ref =& self::$stack[0][self::S_VARIABLES][ 
$param[PHPTAGS_STACK_PARAM] ];
                        if ( isset($param[PHPTAGS_STACK_ARRAY_INDEX]) ) { // 
Example: list($foo[0], $foo[1]) = $array;
                                foreach ( $param[PHPTAGS_STACK_ARRAY_INDEX] as 
$v ) {
                                        if (  $v === INF ) { // Example: $foo[]
@@ -1343,63 +1338,25 @@
                }
        }
 
-       /**
-        * Set a flag in the output object indicating that the content is 
dynamic and
-        * shouldn't be cached.
-        * @global \OutputPage $wgOut
-        * @staticvar boolean $done
-        * @return null
-        */
-       public static function disableParserCache() {
-               if ( self::$parserDisabled === true ) {
-                       return;
-               }
-
-               global $wgOut;
-               self::$parser->disableCache();
-               $wgOut->enableClientCache( false );
-               self::$parserDisabled = true;
-       }
-
-       private static function addRuntimeErrorCategory() {
-               if ( self::$errorCategoryAdded === true || self::$parser === 
null ) {
-                       return;
-               }
-
-               self::$parser->addTrackingCategory( 
'phptags-runtime-error-category' );
-               self::$errorCategoryAdded = true;
-       }
-
-       /**
-        * Increment the expensive function count
-        * @param string $functionName
-        * @return null
-        * @throws PhpTagsException
-        */
-       public static function incrementExpensiveFunctionCount( $functionName ) 
{
-               if ( false === self::$parser->incrementExpensiveFunctionCount() 
) {
-                       throw new PhpTagsException( 
PhpTagsException::FATAL_CALLED_MANY_EXPENSIVE_FUNCTION );
-               }
-               return null;
-       }
-
        public static function pushException( PhpTagsException $exc ) {
                if ( self::$ignoreErrors === false ) {
+                       $stack =& self::$stack[0];
                        if ( $exc->tokenLine === null ) {
-                               $exc->tokenLine = 
self::$running[self::$runIndex][PHPTAGS_STACK_TOKEN_LINE];
+                               $exc->tokenLine = $stack[self::S_RUNNING][ 
$stack[self::S_RUN_INDEX] ][PHPTAGS_STACK_TOKEN_LINE];
                        }
-                       $exc->place = self::$place;
-                       self::$return[] = (string) $exc;
-                       self::addRuntimeErrorCategory();
+                       $exc->place = $stack[self::S_PLACE];
+                       $stack[self::S_RETURN][] = (string) $exc;
+                       Renderer::addRuntimeErrorCategory();
                }
        }
 
        public static function getCurrentOperator() {
-               return self::$running[self::$runIndex];
+               $stack =& self::$stack[0];
+               return $stack[self::S_RUNNING][ $stack[self::S_RUN_INDEX] ];
        }
 
        public static function getVariables() {
-               return self::$variables[ self::$scope ];
+               return self::$stack[0][self::S_VARIABLES];
        }
 
 }
diff --git "a/tests/phpunit/includes/\041\041\041firstInit_Test.php" 
"b/tests/phpunit/includes/\041\041\041firstInit_Test.php"
index d668485..2459f15 100644
--- "a/tests/phpunit/includes/\041\041\041firstInit_Test.php"
+++ "b/tests/phpunit/includes/\041\041\041firstInit_Test.php"
@@ -1,8 +1,8 @@
 <?php
 
-if ( PhpTags::$needInitRuntime ) {
+if ( \PhpTags\Renderer::$needInitRuntime ) {
        wfRunHooks( 'PhpTagsRuntimeFirstInit' );
        \PhpTags\Hooks::loadData();
        \PhpTags\Runtime::$loopsLimit = 1000;
-       PhpTags::$needInitRuntime = false;
+       \PhpTags\Renderer::$needInitRuntime = false;
 }
diff --git a/tests/phpunit/includes/RuntimeTest.php 
b/tests/phpunit/includes/RuntimeTest.php
index 425b913..24b6d2a 100644
--- a/tests/phpunit/includes/RuntimeTest.php
+++ b/tests/phpunit/includes/RuntimeTest.php
@@ -1941,6 +1941,15 @@
                }
                $this->fail( 'An expected exception has not been raised.' );
        }
+       public function testRun_echo_empty_array_exception_1() {
+               $this->assertEquals(
+                               Runtime::runSource( 'echo [4] === [ 
[666]=>"test", 4] ? "true" : "false";', array('test') ),
+                               array(
+                                       (string) new PhpTagsException( 
PhpTagsException::WARNING_ILLEGAL_OFFSET_TYPE, null, 1, 'test' ),
+                                       'true',
+                               )
+                       );
+       }
        public function testRun_echo_array_encapsed_1() {
                $this->assertEquals(
                                Runtime::runSource('$foo=(array)5; echo 
"*$foo[0]*";'),

-- 
To view, visit https://gerrit.wikimedia.org/r/209703
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I1506de772b5a4f6e94500c972a4f70885853d437
Gerrit-PatchSet: 8
Gerrit-Project: mediawiki/extensions/PhpTags
Gerrit-Branch: master
Gerrit-Owner: Pastakhov <[email protected]>
Gerrit-Reviewer: JoelKP <[email protected]>
Gerrit-Reviewer: Pastakhov <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to