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