Pastakhov has submitted this change and it was merged. Change subject: Update to master version 5.9 (v 5.9.0.26) ......................................................................
Update to master version 5.9 (v 5.9.0.26) * Add params checking in Runtime class (v 5.9) * Runtime::VERSION = 11 * remove ErrorHandler * Commented out warning message about deprecated PHP entry point * Fix Renderer::getFrame() * Add extension.json file and move setup instructions to it (v 5.8.0) * Fix string to number conversion in Compiler (v 5.7.2) Runtime 10 Problem depends of conversion string by operator (int): Example: echo 'int ' . (int)420000000000000000000, "\nint string " . (int)"420000000000000000000", "\nstring+0 " . ("420000000000000000000" + 0); Output for 5.3.22 - 5.4.45, hhvm-3.6.1 - 3.12.0 int 0 int string 9223372036854775807 string+0 4.2E+20 Output for 5.5.0 - 7.0.3 int -4275113695319687168 int string 9223372036854775807 string+0 4.2E+20 Now it doing through operation string + 0 * Fix error messages when Hooks class is used recursively (v 5.7.1) * Add heredoc, nowdoc. Fix 'OR', 'AND', '[]' operators (v 5.7.0 runtime 9) Also fix fatal error when PhpTags adds value to string: $string = 'foo'; $string[] = 'bar'; * add outStrip class (v 5.6.0) * Fix memory cache (v 5.5.0 Runtime 8) Now, function getBytecode() always return bytecode that never used early. * Fix array constructor (v 5.4.0) Runtime release 7 error when array constructor is: array( $foo, $bar ); it was as array( null, $bar ); * optimize class outPrint (v 5.3.0) * add function Renderer::insertNoWiki() * add funnction iRawOutput::getReturnValue() * add funnction iRawOutput::placeAsStripItem() * Fix test initialization * Fix debug messages * Skip PhpTagsRuntimeFirstInit hook tests if PHPTAGS_TEST is not defined * add debug mesaages to test files * Update indentation to use tabs Change-Id: I81b2c89a3a8a13ebf9f3ea1c14c3acbd91c0b2e7 --- M .gitignore M PhpTags.hooks.php M PhpTags.json M PhpTags.php A extension.json M i18n/en.json M i18n/it.json A i18n/ko.json M i18n/ru.json M i18n/sv.json M includes/Compiler.php D includes/ErrorHandler.php M includes/GenericObject.php M includes/Hooks.php M includes/JsonLoader.php A includes/PhpTagsConstants.php M includes/PhpTagsException.php M includes/Renderer.php M includes/Runtime.php M includes/iRawOutput.php M includes/outPrint.php A includes/outStrip.php M tests/phpunit/includes/!!!firstInit_Test.php M tests/phpunit/includes/RuntimeTest.php 24 files changed, 1,507 insertions(+), 343 deletions(-) Approvals: Pastakhov: Verified; Looks good to me, approved diff --git a/.gitignore b/.gitignore index 98b092a..afc53f9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *~ *.kate-swp .*.swp +.directory diff --git a/PhpTags.hooks.php b/PhpTags.hooks.php index ea011d3..9e27ec9 100644 --- a/PhpTags.hooks.php +++ b/PhpTags.hooks.php @@ -60,14 +60,14 @@ /** * - * @global int $wgPhpTagsCounter + * @global int $wgPhpTagsCallsCounter * @param Parser $parser * @param string $text * @return boolean */ public static function onParserAfterTidy( $parser, &$text ) { - global $wgPhpTagsCounter; - if ( $wgPhpTagsCounter > 0 ) { + global $wgPhpTagsCallsCounter; + if ( $wgPhpTagsCallsCounter > 0 ) { \PhpTags\Renderer::onParserAfterTidy( $parser, $text ); } return true; @@ -100,4 +100,14 @@ return true; } + public static function onRegistration() { + global $wgPhpTagsLimitReport, $wgPhpTagsCallsCounter; + + $wgPhpTagsLimitReport = false; + $wgPhpTagsCallsCounter = 0; + + define ( 'PHPTAGS_HOOK_RELEASE', 8 ); + define ( 'PHPTAGS_VERSION', '5.9.0.26' ); //@todo remove later, it only for backward compatibility + } + } diff --git a/PhpTags.json b/PhpTags.json index 7adb405..1928822 100644 --- a/PhpTags.json +++ b/PhpTags.json @@ -2,18 +2,22 @@ "constants": { "PHPTAGS_MAJOR_VERSION": { "desc": "The current 'major' version of the PhpTags extension as an integer", + "class": "PhpTagsConstants", "type": "int" }, "PHPTAGS_MINOR_VERSION": { "desc": "The current 'minor' version of the PhpTags extension as an integer", + "class": "PhpTagsConstants", "type": "int" }, "PHPTAGS_RELEASE_VERSION": { "desc": "The current 'release' version of the PhpTags extension as an integer", + "class": "PhpTagsConstants", "type": "int" }, "PHPTAGS_VERSION": { "desc": "The current version of the PhpTags extension as a string", + "class": "PhpTagsConstants", "type": "string" } } diff --git a/PhpTags.php b/PhpTags.php index 3eb3698..d57d90e 100644 --- a/PhpTags.php +++ b/PhpTags.php @@ -1,104 +1,15 @@ <?php -/** - * Main entry point for the PhpTags extension. - * - * @link https://www.mediawiki.org/wiki/Extension:PhpTags Documentation - * @file PhpTags.php - * @defgroup PhpTags - * @ingroup Extensions - * @author Pavel Astakhov <pastak...@yandex.ru> - * @licence GNU General Public Licence 2.0 or later - */ - -// Check to see if we are being called as an extension or directly -if ( !defined( 'MEDIAWIKI' ) ) { - die( 'This file is an extension to MediaWiki and thus not a valid entry point.' ); +if ( function_exists( 'wfLoadExtension' ) ) { + wfLoadExtension( 'PhpTags' ); + // Keep i18n globals so mergeMessageFileList.php doesn't break + $wgMessagesDirs['PhpTags'] = __DIR__ . '/i18n'; + $wgExtensionMessagesFiles['PhpTagsMagic'] = __DIR__ . '/PhpTags.i18n.magic.php'; +// wfWarn( +// 'Deprecated PHP entry point used for PhpTags extension. ' . +// 'Please use wfLoadExtension instead, ' . +// 'see https://www.mediawiki.org/wiki/Extension_registration for more details.' +// ); + return; +} else { + die( 'This version of the PhpTags extension requires MediaWiki 1.25+' ); } - -const PHPTAGS_MAJOR_VERSION = 5; -const PHPTAGS_MINOR_VERSION = 2; -const PHPTAGS_RELEASE_VERSION = 0; -define( 'PHPTAGS_VERSION', PHPTAGS_MAJOR_VERSION . '.' . PHPTAGS_MINOR_VERSION . '.' . PHPTAGS_RELEASE_VERSION ); - -const PHPTAGS_HOOK_RELEASE = 8; -const PHPTAGS_RUNTIME_RELEASE = 6; -const PHPTAGS_JSONLOADER_RELEASE = 3; - -// Register this extension on Special:Version -$wgExtensionCredits['parserhook'][] = array( - 'path' => __FILE__, - 'name' => 'PhpTags', - 'version' => PHPTAGS_VERSION, - 'url' => 'https://www.mediawiki.org/wiki/Extension:PhpTags', - 'author' => '[https://www.mediawiki.org/wiki/User:Pastakhov Pavel Astakhov]', - 'descriptionmsg' => 'phptags-desc', - 'license-name' => 'GPL-2.0+', -); - -// Allow translations for this extension -$wgMessagesDirs['PhpTags'] = __DIR__ . '/i18n'; -$wgExtensionMessagesFiles['PhpTagsMagic'] = __DIR__ . '/PhpTags.i18n.magic.php'; - -// -$wgPhpTagsLimitReport = false; -$wgPhpTagsCounter = 0; - -// Preparing classes for autoloading -$wgAutoloadClasses['PhpTagsHooks'] = __DIR__ . '/PhpTags.hooks.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'; -$wgAutoloadClasses['PhpTags\\PhpTagsException'] = __DIR__ . '/includes/PhpTagsException.php'; -$wgAutoloadClasses['PhpTags\\HookException'] = __DIR__ . '/includes/HookException.php'; -$wgAutoloadClasses['PhpTags\\Compiler'] = __DIR__ . '/includes/Compiler.php'; -$wgAutoloadClasses['PhpTags\\Runtime'] = __DIR__ . '/includes/Runtime.php'; -$wgAutoloadClasses['PhpTags\\GenericObject'] = __DIR__ . '/includes/GenericObject.php'; -$wgAutoloadClasses['PhpTags\\Hooks'] = __DIR__ . '/includes/Hooks.php'; -$wgAutoloadClasses['PhpTags\\JsonLoader'] = __DIR__ . '/includes/JsonLoader.php'; -$wgAutoloadClasses['PhpTagsObjects\\PhpTagsTestClass'] = __DIR__ . '/tests/phpunit/includes/PhpTagsTestClass.php'; - -// Add tracking categories -$wgTrackingCategories[] = 'phptags-compiler-error-category'; -$wgTrackingCategories[] = 'phptags-runtime-error-category'; - -// -$wgHooks['ParserFirstCallInit'][] = 'PhpTagsHooks::onParserFirstCallInit'; -$wgHooks['PhpTagsRuntimeFirstInit'][] = 'PhpTagsHooks::onPhpTagsRuntimeFirstInit'; -$wgHooks['CodeMirrorGetAdditionalResources'][] = 'PhpTagsHooks::onCodeMirrorGetAdditionalResources'; -$wgHooks['ParserLimitReport'][] = 'PhpTagsHooks::onParserLimitReport'; -$wgHooks['ParserAfterTidy'][] = 'PhpTagsHooks::onParserAfterTidy'; -$wgHooks['ExtensionTypes'][] = 'PhpTagsHooks::onExtensionTypes'; -$wgHooks['UnitTestsList'][] = 'PhpTagsHooks::onUnitTestsList'; - -// add parser tests -$wgParserTestFiles[] = __DIR__ . '/tests/parser/PhpTagsTests.txt'; - -/** - * You can specify the namespaces in which allowed to use this extension. - * - * Thus it is possible to give permission to use this extension only for a special user group, example: - * define("NS_PHPTAGS", 1000); - * define("NS_PHPTAGS_TALK", 1001); - * $wgExtraNamespaces[NS_PHPTAGS] = "PhpTags"; - * $wgExtraNamespaces[NS_PHPTAGS_TALK] = "PhpTags_Talk"; - * - * $wgPhpTagsNamespaces = array( NS_PHPTAGS => true ); - * $wgNamespaceProtection[NS_PHPTAGS] = array( 'phptags_editor' ); - * $wgGroupPermissions['sysop']['phptags_editor'] = true; - * - * @var mixed $wgPhpTagsNamespaces Array of namespaces in which allowed to use the extension PhpTags, and if boolean 'true' then it is unlimited namespaces - */ -$wgPhpTagsNamespaces = true; // By default, this is unlimited namespaces - -/** - * Maximum number of allowed loops - */ -$wgPhpTagsMaxLoops = 1000; - -/** - * Storage time of the compiled bytecode at cache - * By default it is 30 days - */ -$wgPhpTagsBytecodeExptime = 2592000; diff --git a/extension.json b/extension.json new file mode 100644 index 0000000..8ade0e0 --- /dev/null +++ b/extension.json @@ -0,0 +1,58 @@ +{ + "name": "PhpTags", + "version": "5.9.0.26", + "author": "[https://www.mediawiki.org/wiki/User:Pastakhov Pavel Astakhov]", + "url": "https://www.mediawiki.org/wiki/Extension:PhpTags", + "descriptionmsg": "phptags-desc", + "license-name": "GPL-2.0+", + "type": "phptags", + "MessagesDirs": { + "PhpTags": [ + "i18n" + ] + }, + "ExtensionMessagesFiles": { + "PhpTagsMagic": "PhpTags.i18n.magic.php" + }, + "AutoloadClasses": { + "PhpTagsHooks": "PhpTags.hooks.php", + "PhpTags\\Renderer": "includes/Renderer.php", + "PhpTags\\Timer": "includes/Renderer.php", + "PhpTags\\iRawOutput": "includes/iRawOutput.php", + "PhpTags\\outPrint": "includes/outPrint.php", + "PhpTags\\outStrip": "includes/outStrip.php", + "PhpTags\\PhpTagsException": "includes/PhpTagsException.php", + "PhpTags\\HookException": "includes/HookException.php", + "PhpTags\\Compiler": "includes/Compiler.php", + "PhpTags\\Runtime": "includes/Runtime.php", + "PhpTags\\GenericObject": "includes/GenericObject.php", + "PhpTags\\Hooks": "includes/Hooks.php", + "PhpTags\\JsonLoader": "includes/JsonLoader.php", + "PhpTagsObjects\\PhpTagsConstants": "includes/PhpTagsConstants.php", + "PhpTagsObjects\\PhpTagsTestClass": "tests/phpunit/includes/PhpTagsTestClass.php" + }, + "Hooks": { + "ParserFirstCallInit": "PhpTagsHooks::onParserFirstCallInit", + "PhpTagsRuntimeFirstInit": "PhpTagsHooks::onPhpTagsRuntimeFirstInit", + "CodeMirrorGetAdditionalResources": "PhpTagsHooks::onCodeMirrorGetAdditionalResources", + "ParserLimitReport": "PhpTagsHooks::onParserLimitReport", + "ParserAfterTidy": "PhpTagsHooks::onParserAfterTidy", + "ExtensionTypes": "PhpTagsHooks::onExtensionTypes", + "UnitTestsList": "PhpTagsHooks::onUnitTestsList" + }, + "ParserTestFiles": [ + "tests/parser/PhpTagsTests.txt" + ], + "config": { + "TrackingCategories": [ + "phptags-compiler-error-category", + "phptags-runtime-error-category" + ], + "PhpTagsNamespaces": true, + "PhpTagsMaxLoops": 1000, + "PhpTagsBytecodeExptime": 2592000 + }, + "callback": "PhpTagsHooks::onRegistration", + "manifest_version": 1 +} + diff --git a/i18n/en.json b/i18n/en.json index 107412d..02dd57c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1,14 +1,14 @@ { - "@metadata": { - "authors": [ - "pastakhov" - ] - }, - "phptags-compiler-error-category": "Pages with invalid PhpTags code", - "phptags-compiler-error-category-desc": "The PhpTags code has an error in it.", - "phptags-runtime-error-category": "Pages with PhpTags errors", - "phptags-runtime-error-category-desc": "There was an error when processing PhpTags code on the page.", - "phptags-desc": "Allows users to use the Magic expressions with PHP language syntax", - "phptags-disabled-for-namespace": "Extension PhpTags is disabled for the namespace \"$1\".", - "phptags-extension-type": "PhpTags extensions" + "@metadata": { + "authors": [ + "pastakhov" + ] + }, + "phptags-compiler-error-category": "Pages with invalid PhpTags code", + "phptags-compiler-error-category-desc": "The PhpTags code has an error in it.", + "phptags-runtime-error-category": "Pages with PhpTags errors", + "phptags-runtime-error-category-desc": "There was an error when processing PhpTags code on the page.", + "phptags-desc": "Allows users to use the Magic expressions with PHP language syntax", + "phptags-disabled-for-namespace": "Extension PhpTags is disabled for the namespace \"$1\".", + "phptags-extension-type": "PhpTags extensions" } diff --git a/i18n/it.json b/i18n/it.json index 7b786a5..1b1f813 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -4,6 +4,11 @@ "Beta16" ] }, + "phptags-compiler-error-category": "Pagine con codice PhpTags non valido", + "phptags-compiler-error-category-desc": "Il codice PhpTags contiene un errore.", + "phptags-runtime-error-category": "Pagine con errori PhpTags", + "phptags-runtime-error-category-desc": "Si è verificato un errore durante l'elaborazione del codice PhpTags nella pagina.", "phptags-desc": "Consente agli utenti di utilizzare le espressioni Magic con la sintassi del linguaggio PHP", - "phptags-disabled-for-namespace": "L'estensione PhpTags è disabilitata per il namespace \"$1\"." + "phptags-disabled-for-namespace": "L'estensione PhpTags è disabilitata per il namespace \"$1\".", + "phptags-extension-type": "Estensioni PhpTags" } diff --git a/i18n/ko.json b/i18n/ko.json new file mode 100644 index 0000000..df85032 --- /dev/null +++ b/i18n/ko.json @@ -0,0 +1,14 @@ +{ + "@metadata": { + "authors": [ + "Revi", + "Ykhwong" + ] + }, + "phptags-compiler-error-category": "잘못된 PhpTags 코드를 포함한 문서", + "phptags-compiler-error-category-desc": "PhpTags 코드에 문제가 있습니다.", + "phptags-runtime-error-category": "PhpTags 오류가 있는 문서", + "phptags-runtime-error-category-desc": "PhpTags 코드를 처리하던 중 오류가 발생했습니다.", + "phptags-disabled-for-namespace": "PhpTags 확장 기능이 \"$1\" 이름공간에서 활성화되지 않았습니다.", + "phptags-extension-type": "PhpTags 확장 기능" +} diff --git a/i18n/ru.json b/i18n/ru.json index efafaab..e6c05c3 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -4,6 +4,11 @@ "Okras" ] }, + "phptags-compiler-error-category": "Страницы с неверным PhpTags-кодом", + "phptags-compiler-error-category-desc": "PhpTags-код, содержащий ошибку.", + "phptags-runtime-error-category": "Страницы с PhpTags-ошибками", + "phptags-runtime-error-category-desc": "Произошла ошибка при обработке кода PhpTags на этой странице.", "phptags-desc": "Позволяет пользователям использовать волшебные выражения в синтаксисе языка PHP", - "phptags-disabled-for-namespace": "Расширение PhpTags отключено для пространства имён «$1»." + "phptags-disabled-for-namespace": "Расширение PhpTags отключено для пространства имён «$1».", + "phptags-extension-type": "Расширения PhpTags" } diff --git a/i18n/sv.json b/i18n/sv.json index 6276cf9..e75551c 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -11,5 +11,6 @@ "phptags-runtime-error-category": "Sidor med PhpTags-fel", "phptags-runtime-error-category-desc": "Det uppstod ett fel vid bearbetningen av PhpTags-koden på sidan.", "phptags-desc": "Tillåter användare att använda Magic-uttrycken med PHP-språksyntax", - "phptags-disabled-for-namespace": "Tillägget PhpTags är inaktiverat för namnrymden \"$1\"." + "phptags-disabled-for-namespace": "Tillägget PhpTags är inaktiverat för namnrymden \"$1\".", + "phptags-extension-type": "PhpTags-tillägg" } diff --git a/includes/Compiler.php b/includes/Compiler.php index ea7eb9a..d78f0f5 100644 --- a/includes/Compiler.php +++ b/includes/Compiler.php @@ -453,17 +453,12 @@ $tmp = intval( $text, 8 ); } } else { - $tmp = (int)$text; + $tmp = $text + 0; } $result = array( Runtime::B_COMMAND=>null, Runtime::B_RESULT=>$tmp, Runtime::B_TOKEN_LINE=>$this->tokenLine, Runtime::B_DEBUG=>$text ); break; case T_DNUMBER: - $epos = stripos($text, 'e'); - if ( $epos === false ) { - $tmp = (float)$text; - } else { - $tmp = (float)( substr($text, 0, $epos) * pow(10, substr($text, $epos+1)) ); - } + $tmp = $text + 0; $result = array( Runtime::B_COMMAND=>null, Runtime::B_RESULT=>$tmp, Runtime::B_TOKEN_LINE=>$this->tokenLine, Runtime::B_DEBUG=>$text ); break; case T_CONSTANT_ENCAPSED_STRING: @@ -534,11 +529,25 @@ } $result = array( Runtime::B_COMMAND=>null, Runtime::B_RESULT=>$tmp, Runtime::B_TOKEN_LINE=>$this->tokenLine, Runtime::B_DEBUG=>$text ); break; + case T_START_HEREDOC: + if( substr( $text, 3, 1 ) === '\'' ) { // heredoc + $this->stepUP(); + if ( $this->id !== T_ENCAPSED_AND_WHITESPACE ) { + throw new PhpTagsException( PhpTagsException::PARSE_SYNTAX_ERROR_UNEXPECTED, array( $this->id, "T_ENCAPSED_AND_WHITESPACE" ), $this->tokenLine, $this->place ); + } + $result = array( Runtime::B_COMMAND=>null, Runtime::B_RESULT=>$this->text, Runtime::B_TOKEN_LINE=>$this->tokenLine, Runtime::B_DEBUG=>$text ); + $this->stepUP(); + if ( $this->id !== T_END_HEREDOC ) { + throw new PhpTagsException( PhpTagsException::PARSE_SYNTAX_ERROR_UNEXPECTED, array( $this->id, "T_ENCAPSED_AND_WHITESPACE" ), $this->tokenLine, $this->place ); + } + break; + } // nowdoc + // break is not necessary here case '"': $this->stepUP(); $strings = array(); $i = 0; - while ( $this->id != '"' ) { + while ( $this->id !== '"' ) { if ( $this->id === T_CURLY_OPEN || $this->id === '}' ) { $this->stepUP(); } else { @@ -546,6 +555,8 @@ if ( $val ) { // echo "abcd$foo $strings[$i] = null; $this->addValueIntoStack( $val, $strings, $i ); + } else if ( $this->id === T_END_HEREDOC ) { + break; } else { // PHP Parse error: syntax error, unexpected $id, expecting '"' throw new PhpTagsException( PhpTagsException::PARSE_SYNTAX_ERROR_UNEXPECTED, array( $this->id, "'\"'" ), $this->tokenLine, $this->place ); @@ -595,7 +606,7 @@ // PHP Parse error: syntax error, unexpected $id throw new PhpTagsException( PhpTagsException::PARSE_SYNTAX_ERROR_UNEXPECTED, array( $this->id ), $this->tokenLine, $this->place ); } - if ( $owner === false && $variable[Runtime::B_COMMAND] === Runtime::T_VARIABLE ) { + if ( $owner === false && $variable[Runtime::B_COMMAND] === Runtime::T_VARIABLE ) { // $foo = <value> or $foo += <value> $return = array( Runtime::B_COMMAND => self::$runtimeOperators[$id], Runtime::B_PARAM_1 => $variable, @@ -603,6 +614,7 @@ Runtime::B_RESULT => null, Runtime::B_TOKEN_LINE => $this->tokenLine, Runtime::B_DEBUG => $text, + Runtime::B_FLAGS => ($cannotRead === true && $id !== '=') ? Runtime::F_DONT_CHECK_PARAM1 : 0, ); $this->addValueIntoStack( $val, $return, Runtime::B_PARAM_2 ); return $return; // *********** EXIT *********** @@ -920,6 +932,8 @@ $result =& $this->stepValue( $owner ); $this->ignoreErrors = null; return $result; + case T_END_HEREDOC: + return $result; // false } if ( $result !== false ) { $this->stepUP(); @@ -954,6 +968,7 @@ Runtime::B_RESULT => null, Runtime::B_TOKEN_LINE => $this->tokenLine, Runtime::B_DEBUG => $text, + Runtime::B_FLAGS => 0, ); $didit = $this->addValueIntoStack( $value, $operator, Runtime::B_PARAM_1, false ); // Add the first value into the stack @@ -1079,9 +1094,17 @@ throw new PhpTagsException( PhpTagsException::PARSE_SYNTAX_ERROR_UNEXPECTED, array( $this->id ), $this->tokenLine, $this->place ); } - $operator = array( - Runtime::B_COMMAND => self::$runtimeOperators[$id], + $result = array( + Runtime::B_COMMAND => Runtime::T_TERNARY, Runtime::B_PARAM_1 => null, + Runtime::B_PARAM_2 => array(Runtime::B_DO_FALSE=>false, Runtime::B_PARAM_2=>false), + Runtime::B_RESULT => null, + Runtime::B_TOKEN_LINE => $this->tokenLine, + Runtime::B_DEBUG => $text, + ); + + $operator = array( + Runtime::B_COMMAND => Runtime::T_BOOL_CAST, Runtime::B_PARAM_2 => null, Runtime::B_RESULT => null, Runtime::B_TOKEN_LINE => $this->tokenLine, @@ -1091,13 +1114,13 @@ $this->addValueIntoStack( $nextValue, $operator, Runtime::B_PARAM_2, true ); $stack_true = $this->stack ?: false; $this->stack_pop_memory(); - $doit = $this->addValueIntoStack( $value, $operator, Runtime::B_PARAM_1, true ); + $done = $this->addValueIntoStack( $value, $result, Runtime::B_PARAM_1, true ); - if ( $stack_true === false && $operator[Runtime::B_PARAM_2] == false || $doit === true && $operator[Runtime::B_PARAM_1] == false ) { + if ( $stack_true === false && $operator[Runtime::B_PARAM_2] == false || $done === true && $operator[Runtime::B_PARAM_1] == false ) { $result = array( Runtime::B_COMMAND => null, Runtime::B_RESULT => false ); // it's always false break; } - if ( $stack_true === false && $operator[Runtime::B_PARAM_2] == true && $doit === true && $operator[Runtime::B_PARAM_1] == true ) { + if ( $stack_true === false && $operator[Runtime::B_PARAM_2] == true && $done === true && $operator[Runtime::B_PARAM_1] == true ) { $result = array( Runtime::B_COMMAND => null, Runtime::B_RESULT => true ); // it's always true break; } @@ -1108,22 +1131,8 @@ $stack_true[] =& $operator; } - $param2 = array( - Runtime::B_RESULT => null, - Runtime::B_PARAM_1 => &$operator[Runtime::B_RESULT], - Runtime::B_DO_TRUE => $stack_true, - Runtime::B_PARAM_2 => false, - Runtime::B_DO_FALSE => false, - ); - $result = array( - Runtime::B_COMMAND => Runtime::T_TERNARY, - Runtime::B_PARAM_1 => null, - Runtime::B_PARAM_2 => $param2, - Runtime::B_RESULT => null, - Runtime::B_TOKEN_LINE => $this->tokenLine, - Runtime::B_DEBUG => $text, - ); - $this->addValueIntoStack( $value, $result, Runtime::B_PARAM_1, true ); + $result[Runtime::B_PARAM_2][Runtime::B_DO_TRUE] = $stack_true; + $result[Runtime::B_PARAM_2][Runtime::B_PARAM_1] =& $operator[Runtime::B_RESULT]; break; case T_BOOLEAN_OR: // || case T_LOGICAL_OR: // or @@ -1135,9 +1144,17 @@ throw new PhpTagsException( PhpTagsException::PARSE_SYNTAX_ERROR_UNEXPECTED, array( $this->id ), $this->tokenLine, $this->place ); } - $operator = array( - Runtime::B_COMMAND => self::$runtimeOperators[$id], + $result = array( + Runtime::B_COMMAND => Runtime::T_TERNARY, Runtime::B_PARAM_1 => null, + Runtime::B_PARAM_2 => array(Runtime::B_DO_TRUE=>false, Runtime::B_PARAM_1=>true), + Runtime::B_RESULT => null, + Runtime::B_TOKEN_LINE => $this->tokenLine, + Runtime::B_DEBUG => $text, + ); + + $operator = array( + Runtime::B_COMMAND => Runtime::T_BOOL_CAST, Runtime::B_PARAM_2 => null, Runtime::B_RESULT => null, Runtime::B_TOKEN_LINE => $this->tokenLine, @@ -1147,13 +1164,13 @@ $this->addValueIntoStack( $nextValue, $operator, Runtime::B_PARAM_2, true ); $stack_false = $this->stack ?: false; $this->stack_pop_memory(); - $doit = $this->addValueIntoStack( $value, $operator, Runtime::B_PARAM_1, true ); + $done = $this->addValueIntoStack( $value, $result, Runtime::B_PARAM_1, true ); - if ( $stack_false === false && $operator[Runtime::B_PARAM_2] == true || $doit === true && $operator[Runtime::B_PARAM_1] == true ) { + if ( $stack_false === false && $operator[Runtime::B_PARAM_2] == true || $done === true && $operator[Runtime::B_PARAM_1] == true ) { $result = array( Runtime::B_COMMAND => null, Runtime::B_RESULT => true ); // it's always true break; } - if ( $stack_false === false && $operator[Runtime::B_PARAM_2] == false && $doit === true && $operator[Runtime::B_PARAM_1] == false ) { + if ( $stack_false === false && $operator[Runtime::B_PARAM_2] == false && $done === true && $operator[Runtime::B_PARAM_1] == false ) { $result = array( Runtime::B_COMMAND => null, Runtime::B_RESULT => false ); // it's always false break; } @@ -1164,22 +1181,8 @@ $stack_false[] =& $operator; } - $param2 = array( - Runtime::B_RESULT => null, - Runtime::B_PARAM_1 => true, - Runtime::B_DO_TRUE => false, - Runtime::B_PARAM_2 => &$operator[Runtime::B_RESULT], - Runtime::B_DO_FALSE => $stack_false, - ); - $result = array( - Runtime::B_COMMAND => Runtime::T_TERNARY, - Runtime::B_PARAM_1 => null, - Runtime::B_PARAM_2 => $param2, - Runtime::B_RESULT => null, - Runtime::B_TOKEN_LINE => $this->tokenLine, - Runtime::B_DEBUG => $text, - ); - $this->addValueIntoStack( $value, $result, Runtime::B_PARAM_1, true ); + $result[Runtime::B_PARAM_2][Runtime::B_DO_FALSE] = $stack_false; + $result[Runtime::B_PARAM_2][Runtime::B_PARAM_2] =& $operator[Runtime::B_RESULT]; break; } return $result; @@ -1293,6 +1296,7 @@ $copy = array( Runtime::B_COMMAND=>Runtime::T_COPY, Runtime::B_PARAM_1=>null, Runtime::B_RESULT=>&$array[$i] ); $this->addValueIntoStack( $value, $copy, Runtime::B_PARAM_1, false ); $this->stack[] =& $copy; + unset( $copy ); } else { $this->addValueIntoStack( $value, $array, $i ); } @@ -1347,7 +1351,7 @@ if ( $result === false ) { // It is simple array and can be compiled, example: $foo = array( 1, 2, 3 ); $result = array( Runtime::B_COMMAND=>false, Runtime::B_RESULT=>&$array ); } elseif ( $array ) { - $result[Runtime::B_PARAM_1][] = &$array; + $result[Runtime::B_PARAM_1][] =& $array; } return $result; } diff --git a/includes/ErrorHandler.php b/includes/ErrorHandler.php deleted file mode 100644 index d81cb55..0000000 --- a/includes/ErrorHandler.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php -namespace PhpTags; - -/** - * Description of ErrorHandler - * - * @author pastakhov - */ -class ErrorHandler { - - public static function onError( $errno, $errstr, $errfile, $errline, $errcontext, $object = false ) { - if ( $object === false ) { // @todo wfIsHHVM() - return self::onPhpError( $errno, $errstr, $errfile ); - } - return self::onHhvmError( $errno, $errstr, $errfile ); - } - - private static function onPhpError( $errno, $errstr, $errfile ) { - $backtrace = debug_backtrace(); - if ( true === isset($backtrace[1]['file']) && strpos( $backtrace[1]['file'], 'PhpTags/includes/Runtime.php' ) !== false ) { - if ( strpos( $errstr, 'Division by zero' ) !== false ) { - Runtime::pushException( new PhpTagsException( PhpTagsException::WARNING_DIVISION_BY_ZERO, null ) ); - return true; - } - $matches = null; - if ( preg_match('/^Object of class ([\\w:\\\\]+) could not be converted to (\\w+).*?/', $errstr, $matches) ) { - return self::onRuntimeObjectConvertionError( $matches ); - } - } - return false; - } - - private static function onRuntimeObjectConvertionError( $matches ) { - $object = Runtime::getCurrentOperator(); - static $previousOperator = false; - if ( isset($object[Runtime::B_PARAM_1]) && is_a($object[Runtime::B_PARAM_1], $matches[1]) ) { - if ( $previousOperator === $object && is_a($object[Runtime::B_PARAM_2], $matches[1]) ) { - $matches[1] = $object[Runtime::B_PARAM_2]->getName(); - } else { - $matches[1] = $object[Runtime::B_PARAM_1]->getName(); - } - } else { - $matches[1] = $object[Runtime::B_PARAM_2]->getName(); - } - Runtime::pushException( new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array($matches[1], $matches[2]) ) ); - $previousOperator = $object; - return true; - } - - private static function onHhvmError( $errno, $errstr, $errfile ) { - $backtrace = debug_backtrace(); - $matches = null; - if ( false !== strpos( $errfile, 'PhpTags/includes/Runtime.php' ) ) { - if ( strpos( $errstr, 'Division by zero' ) !== false ) { - Runtime::pushException( new PhpTagsException( PhpTagsException::WARNING_DIVISION_BY_ZERO, null ) ); - return true; - } - $matches = null; - if ( preg_match('/^Object of class ([\\w:\\\\]+) could not be converted to (\\w+).*?/', $errstr, $matches) ) { - return self::onRuntimeObjectConvertionError( $matches ); - } - } - return false; - } - -} diff --git a/includes/GenericObject.php b/includes/GenericObject.php index e8136e9..aec9281 100644 --- a/includes/GenericObject.php +++ b/includes/GenericObject.php @@ -77,6 +77,19 @@ return $this->value; } + /** + * Returns value for operator (array) and functions var_dump and etc... + * @since 5.9 + * @return array + */ + public function getDumpValue() { + return (array)('(' . Runtime::R_DUMP_OBJECT . ' <' . $this->getName() . '>)'); + } + + /** + * Returns object's name + * @return string + */ public function getName() { return $this->objectName; } @@ -115,7 +128,7 @@ return \PhpTags\Hooks::getCallInfo( \PhpTags\Hooks::INFO_RETURNS_ON_FAILURE ); } - // It doesn't allow illegal access to public properties inside phptag code by using foreach operator + // It doesn't allow illegal access to public properties inside phptag code through using foreach operator public function current() {} public function key() {} public function next() {} diff --git a/includes/Hooks.php b/includes/Hooks.php index ae165cf..74aca6a 100644 --- a/includes/Hooks.php +++ b/includes/Hooks.php @@ -105,7 +105,7 @@ $key = wfMemcKey( 'phptags', 'loadJsonFiles' ); $cached = $cache->get( $key ); if ( $cached !== false && - $cached['JSONLOADER'] === PHPTAGS_JSONLOADER_RELEASE && + $cached['JSONLOADER'] === JsonLoader::VERSION && $cached['jsonFiles'] === self::$jsonFiles && $cached['callbackConstants'] === self::$callbackConstants ) { \wfDebugLog( 'PhpTags', __METHOD__ . '() using cache: yes' ); @@ -117,7 +117,7 @@ $data['constantValues'] += self::loadConstantValues(); $data['jsonFiles'] = self::$jsonFiles; $data['callbackConstants'] = self::$callbackConstants; - $data['JSONLOADER'] = PHPTAGS_JSONLOADER_RELEASE; + $data['JSONLOADER'] = JsonLoader::VERSION; $cache->set( $key, $data ); } self::$jsonFiles = null; @@ -406,13 +406,24 @@ } /** - * @deprecated since version 5.2.0 + * * @param type $name * @param type $object * @param type $value */ public static function callSetObjectsProperty( $name, $object, $value ) { - return self::callSetObjectProperty( $value, strtolower( $name ), $object ); + $oldValue = self::$value; + self::$value = array( + Runtime::B_HOOK_TYPE => Runtime::H_SET_OBJECT_PROPERTY, + Runtime::B_METHOD => $name, + Runtime::B_METHOD_KEY => null, + Runtime::B_OBJECT => $object, + ); + + $return = self::callSetObjectProperty( $value, strtolower( $name ), $object ); + + self::$value = $oldValue; + return $return; } /** @@ -422,7 +433,7 @@ * @param \PhpTags\GenericObject $object * @return mixded */ - public static function callSetObjectProperty( $value, $propertyKey, $object ) { + private static function callSetObjectProperty( $value, $propertyKey, $object ) { if ( false === $object instanceof GenericObject ) { Runtime::pushException( new PhpTagsException( PhpTagsException::WARNING_ATTEMPT_TO_ASSIGN_PROPERTY ) ); } @@ -464,6 +475,7 @@ $objectKey = strtolower( $calledObjectName ); } + $oldValue = self::$value; self::$value = array( Runtime::B_HOOK_TYPE => Runtime::H_NEW_OBJECT, Runtime::B_METHOD => '__construct', @@ -492,6 +504,7 @@ } throw new PhpTagsException( PhpTagsException::FATAL_OBJECT_NOT_CREATED, $message ); } + self::$value = $oldValue; return $newObject; } diff --git a/includes/JsonLoader.php b/includes/JsonLoader.php index 5e4e2cf..81c630a 100644 --- a/includes/JsonLoader.php +++ b/includes/JsonLoader.php @@ -11,6 +11,8 @@ */ class JsonLoader { + const VERSION = 3; + public static function load( $files ) { $objects = array(); $functions = array(); diff --git a/includes/PhpTagsConstants.php b/includes/PhpTagsConstants.php new file mode 100644 index 0000000..392d081 --- /dev/null +++ b/includes/PhpTagsConstants.php @@ -0,0 +1,36 @@ +<?php +namespace PhpTagsObjects; + +/** + * + * + * @file PhpTagsConstants.php + * @author Pavel Astakhov <pastak...@yandex.ru> + * @licence GNU General Public Licence 2.0 or later + */ +class PhpTagsConstants extends \PhpTags\GenericObject { + + public static function getConstantValue( $constantName ) { + static $phptagsVersion = false; + + if ( $phptagsVersion === false ) { + $allThings = \ExtensionRegistry::getInstance()->getAllThings(); + $phptagsVersion = $allThings['PhpTags']['version']; + } + + switch ( $constantName ) { + case 'PHPTAGS_VERSION': + return $phptagsVersion; + case 'PHPTAGS_MAJOR_VERSION': + $v = (int)split( $phptagsVersion, '.' ); + return $v[0]; + case 'PHPTAGS_MINOR_VERSION': + $v = split( $phptagsVersion, '.' ); + return isset( $v[1] ) ? (int)$v[1] : 0; + case 'PHPTAGS_RELEASE_VERSION': + $v = split( $phptagsVersion, '.' ); + return isset( $v[2] ) ? (int)$v[2] : 0; + } + return parent::getConstantValue( $constantName ); + } +} diff --git a/includes/PhpTagsException.php b/includes/PhpTagsException.php index 180080c..e9e549d 100644 --- a/includes/PhpTagsException.php +++ b/includes/PhpTagsException.php @@ -191,6 +191,15 @@ case self::WARNING_ILLEGAL_OFFSET_TYPE: $message = 'Illegal offset type'; break; + case self::FATAL_CANNOT_USE_OBJECT_AS_ARRAY: + $message = 'Cannot use object as array'; + break; + case self::FATAL_UNSUPPORTED_OPERAND_TYPES: + $message = 'Unsupported operand types'; + break; + case self::FATAL_INTERNAL_ERROR: + $message = 'Unexpected behavior of PhpTags (Internal Error):' . $arguments; + break; default: $message = "Undefined error, code {$this->code}"; $this->code = self::EXCEPTION_FATAL * 1000; @@ -281,11 +290,15 @@ const FATAL_ACCESS_TO_UNDECLARED_STATIC_PROPERTY = 4020; // PHP Fatal error: Access to undeclared static property: F::$rsrr const FATAL_UNEXPECTED_OBJECT_TYPE = 4021; // Fatal error: Unexpected object type stdClass. in const FATAL_WRONG_BREAK_LEVELS = 4022; // PHP Fatal error: Cannot break/continue 4 levels + const FATAL_CANNOT_USE_OBJECT_AS_ARRAY = 4023; // Cannot use object of type %%%%%% as array + const FATAL_UNSUPPORTED_OPERAND_TYPES = 4024; // Unsupported operand types, example: [1] + 1 const FATAL_DENIED_FOR_NAMESPACE = 4500; const FATAL_CALLFUNCTION_INVALID_HOOK = 4501; const FATAL_CALLCONSTANT_INVALID_HOOK = 4502; + const FATAL_INTERNAL_ERROR = 4999; // Unexpected behavior + const EXCEPTION_CATCHABLE_FATAL = 5; const FATAL_OBJECT_COULD_NOT_BE_CONVERTED = 5001; //PHP Catchable fatal error: Object of class stdClass could not be converted to string diff --git a/includes/Renderer.php b/includes/Renderer.php index 7960aea..a994669 100644 --- a/includes/Renderer.php +++ b/includes/Renderer.php @@ -121,8 +121,8 @@ * @return array */ private static function getBytecode( $source, $parser, $frame, $frameTitle, $frameTitleText ) { - global $wgPhpTagsBytecodeExptime, $wgPhpTagsCounter; - $wgPhpTagsCounter++; + global $wgPhpTagsBytecodeExptime, $wgPhpTagsCallsCounter; + $wgPhpTagsCallsCounter++; static $parserTitle = false; if ( $parserTitle === false ) { @@ -136,7 +136,7 @@ if ( true === isset( self::$bytecodeCache[$revID][$md5Source] ) ) { \wfDebugLog( 'PhpTags', 'Memory hiting with key ' . $revID ); self::$memoryHit++; - return self::$bytecodeCache[$revID][$md5Source]; + return unserialize( self::$bytecodeCache[$revID][$md5Source] ); } if ( $wgPhpTagsBytecodeExptime > 0 && $revID > 0 && false === isset( self::$bytecodeLoaded[$revID] ) ) { @@ -144,26 +144,26 @@ $key = \wfMemcKey( 'PhpTags', $revID ); $data = $cache->get( $key ); self::$bytecodeLoaded[$revID] = true; - if ( $data !== false && $data[0] === PHPTAGS_RUNTIME_RELEASE ) { + if ( $data !== false && $data[0] === Runtime::VERSION ) { 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]; + return unserialize( self::$bytecodeCache[$revID][$md5Source] ); } } \wfDebugLog( 'PhpTags', 'Cache missing with key ' . $revID ); } - $bytecode = Compiler::compile( $source, $frameTitleText ); + $bytecode = serialize( 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::$bytecodeNeedsUpdate[$revID][$md5Source] = $bytecode; } self::$compileHit++; Timer::addCompileTime( $parser ); - return $bytecode; + return unserialize( $bytecode ); } /** @@ -180,7 +180,7 @@ } if ( true === self::$needInitRuntime ) { - \wfDebugLog( 'PhpTags', 'Run hook PhpTagsRuntimeFirstInit' ); + \wfDebug( 'PhpTags: Run hook PhpTagsRuntimeFirstInit' ); \Hooks::run( 'PhpTagsRuntimeFirstInit' ); Hooks::loadData(); Runtime::$loopsLimit = $wgPhpTagsMaxLoops; @@ -197,7 +197,7 @@ $cache = \wfGetCache( CACHE_ANYTHING ); foreach ( self::$bytecodeNeedsUpdate as $revID => $data ) { $key = wfMemcKey( 'PhpTags', $revID ); - $cache->set( $key, array(PHPTAGS_RUNTIME_RELEASE, $data), $wgPhpTagsBytecodeExptime ); + $cache->set( $key, array(Runtime::VERSION, $data), $wgPhpTagsBytecodeExptime ); \wfDebugLog( 'PhpTags', 'Save compiled bytecode to cache with key ' . $revID ); } self::$bytecodeNeedsUpdate = array(); @@ -206,8 +206,8 @@ public static function reset() { self::writeLimitReport(); - global $wgPhpTagsCounter; - $wgPhpTagsCounter = 0; + global $wgPhpTagsCallsCounter; + $wgPhpTagsCallsCounter = 0; Runtime::reset(); Timer::reset(); self::$bytecodeCache = array(); @@ -240,7 +240,7 @@ } public static function writeLimitReport() { - global $wgPhpTagsCounter, $wgPhpTagsLimitReport; + global $wgPhpTagsCallsCounter, $wgPhpTagsLimitReport; $time = Timer::getRunTime(); $compileTime = Timer::getCompileTime(); @@ -252,7 +252,7 @@ Total : %.3f sec ----------------------------------------------------------- ', - $wgPhpTagsCounter, + $wgPhpTagsCallsCounter, $time - $compileTime, $compileTime, self::$compileHit, @@ -343,6 +343,32 @@ } } + /** + * + * @see Parser::insertStripItem() + * @param string $text + * @return string + */ + public static function insertNoWiki( $text ) { + $parser = self::$parser; + if ( $parser === null ) { // skip in php unit tests + return $text; + } + $rnd = "{$parser->mUniqPrefix}-phptags-{$parser->mMarkerIndex}-" . \Parser::MARKER_SUFFIX; + $parser->mMarkerIndex++; + $parser->mStripState->addNoWiki( $rnd, $text ); + return $rnd; + } + + + public static function insertStripItem( $text ) { + $parser = self::$parser; + if ( $parser === null ) { // skip in php unit tests + return $text; + } + return $parser->insertStripItem( $text ); + } + } class Timer { diff --git a/includes/Runtime.php b/includes/Runtime.php index eb3f4fe..2bf5210 100644 --- a/includes/Runtime.php +++ b/includes/Runtime.php @@ -11,6 +11,8 @@ */ class Runtime { + const VERSION = 11; + ##### Bytecode array indexes ##### const B_COMMAND = 0; // Token ID const B_RESULT = 1; @@ -27,6 +29,9 @@ const B_HOOK_TYPE = 12; // describes PhpTags hook type, constant, function etc (self::H_...) const B_METHOD = 13; // method or function name const B_METHOD_KEY = 14; // method or function name in lower case + const B_FLAGS = 15; + + const F_DONT_CHECK_PARAM1 = 1; ##### Hook types ##### const H_GET_CONSTANT = '_'; @@ -57,6 +62,9 @@ const S_MEMORY = 5; const S_PLACE = 6; const S_VARIABLES = 7; + + const R_ARRAY = 'Array'; + const R_DUMP_OBJECT = 'object'; private static $operators = array( self::T_QUOTE => 'doQuote', @@ -161,7 +169,20 @@ * @param array $value */ private static function doQuote ( &$value ) { - $value[self::B_RESULT] = implode( $value[self::B_PARAM_1] ); + $implode = array(); + foreach ( $value[self::B_PARAM_1] as $v ) { + if ( $v === null || is_scalar( $v ) ) { + $implode[] = $v; + } else if ( is_array( $v ) ) { + self::pushException( new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING ) ); + $implode[] = self::R_ARRAY; + } else if ( $v instanceof GenericObject ) { + $implode[] = $v->toString(); + } else { + throw new PhpTagsException( PhpTagsException::FATAL_INTERNAL_ERROR, __LINE__ ); + } + } + $value[self::B_RESULT] = implode( $implode ); } /** @@ -169,7 +190,8 @@ * @param array $value */ private static function doConcat ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] . $value[self::B_PARAM_2]; + $v = self::checkStringParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2], $value[self::B_FLAGS] ); + $value[self::B_RESULT] = $v[0] . $v[1]; } /** @@ -177,7 +199,8 @@ * @param array $value */ private static function doPlus ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] + $value[self::B_PARAM_2]; + $v = self::checkArrayParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] + $v[1]; } /** @@ -185,7 +208,8 @@ * @param array $value */ private static function doMinus ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] - $value[self::B_PARAM_2]; + $v = self::checkScalarParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] - $v[1]; } /** @@ -193,7 +217,10 @@ * @param array $value */ private static function doMul ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] * $value[self::B_PARAM_2]; + $v1 = $value[self::B_PARAM_1]; + $v2 = $value[self::B_PARAM_2]; + self::checkScalarParams( $v1, $v2 ); + $value[self::B_RESULT] = $v1 * $v2; } /** @@ -201,7 +228,15 @@ * @param array $value */ private static function doDiv ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] / $value[self::B_PARAM_2]; + $v1 = $value[self::B_PARAM_1]; + $v2 = $value[self::B_PARAM_2]; + self::checkScalarParams( $v1, $v2 ); + if ( $v2 == 0 ) { + self::pushException( new PhpTagsException( PhpTagsException::WARNING_DIVISION_BY_ZERO ) ); + $value[self::B_RESULT] = false; + } else { + $value[self::B_RESULT] = $v1 / $v2; + } } /** @@ -209,7 +244,15 @@ * @param array $value */ private static function doMod ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] % $value[self::B_PARAM_2]; + $v1 = $value[self::B_PARAM_1]; + $v2 = $value[self::B_PARAM_2]; + self::checkScalarParams( $v1, $v2 ); + if ( $v2 == 0 ) { + self::pushException( new PhpTagsException( PhpTagsException::WARNING_DIVISION_BY_ZERO ) ); + $value[self::B_RESULT] = false; + } else { + $value[self::B_RESULT] = $v1 % $v2; + } } /** @@ -217,7 +260,8 @@ * @param array $value */ private static function doAnd ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] & $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] & $v[1]; } /** @@ -225,7 +269,8 @@ * @param array $value */ private static function doOr ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] | $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] | $v[1]; } /** @@ -233,7 +278,8 @@ * @param array $value */ private static function doXor ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] ^ $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] ^ $v[1]; } /** @@ -241,7 +287,8 @@ * @param array $value */ private static function doShiftLeft ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] << $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] << $v[1]; } /** @@ -249,7 +296,8 @@ * @param array $value */ private static function doShiftRight ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] >> $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] >> $v[1]; } /** @@ -281,7 +329,8 @@ * @param array $value */ private static function doIsSmaller ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] < $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] < $v[1]; } /** @@ -289,7 +338,8 @@ * @param array $value */ private static function doIsGreater ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] > $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] > $v[1]; } /** @@ -297,7 +347,8 @@ * @param array $value */ private static function doIsSmallerOrEqual ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] <= $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] <= $v[1]; } /** @@ -305,7 +356,8 @@ * @param array $value */ private static function doIsGreaterOrEqual ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] >= $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] >= $v[1]; } /** @@ -313,7 +365,8 @@ * @param array $value */ private static function doIsEqual ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] == $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] == $v[1]; } /** @@ -321,7 +374,8 @@ * @param array $value */ private static function doIsNotEqual ( &$value ) { - $value[self::B_RESULT] = $value[self::B_PARAM_1] != $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $value[self::B_PARAM_1], $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $v[0] != $v[1]; } /** @@ -345,10 +399,16 @@ * @param array $value */ private static function doPrint ( &$value ) { - if( $value[self::B_PARAM_1] instanceof GenericObject ) { - self::$stack[0][self::S_RETURN][] = $value[self::B_PARAM_1]->toString(); - } else { + $v = $value[self::B_PARAM_1]; + if ( is_scalar( $v ) || $v === null ) { self::$stack[0][self::S_RETURN][] = $value[self::B_PARAM_1]; + } else if( $v instanceof GenericObject ) { + self::$stack[0][self::S_RETURN][] = $value[self::B_PARAM_1]->toString(); + } else if ( is_array( $v ) ) { + self::pushException( new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING ) ); + self::$stack[0][self::S_RETURN][] = self::R_ARRAY; + } else { // Should never happen + throw new PhpTagsException( PhpTagsException::FATAL_INTERNAL_ERROR, __LINE__ ); } } @@ -357,7 +417,12 @@ * @param array $value */ private static function doNot ( &$value ) { - $value[self::B_RESULT] = ~$value[self::B_PARAM_2]; + $v = $value[self::B_PARAM_2]; + if ( $v === null || is_scalar( $v ) ) { + $value[self::B_RESULT] = ~$v; + } else { + throw new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES ); + } } /** @@ -373,7 +438,9 @@ * @param array $value */ private static function doIntCast ( &$value ) { - $value[self::B_RESULT] = (int)$value[self::B_PARAM_2]; + $tmp = null; + $v = self::checkObjectParams( $value[self::B_PARAM_2], $tmp ); + $value[self::B_RESULT] = (int)$v[0]; } /** @@ -381,7 +448,9 @@ * @param array $value */ private static function doDoubleCast ( &$value ) { - $value[self::B_RESULT] = (double)$value[self::B_PARAM_2]; + $tmp = null; + $v = self::checkObjectParams( $value[self::B_PARAM_2], $tmp, 'double' ); + $value[self::B_RESULT] = (double)$v[0]; } /** @@ -389,7 +458,10 @@ * @param array $value */ private static function doStringCast ( &$value ) { - $value[self::B_RESULT] = (string)$value[self::B_PARAM_2]; + $tmp = null; + $flags = 0; + $v = self::checkStringParams( $value[self::B_PARAM_2], $tmp, $flags ); + $value[self::B_RESULT] = (string)$v[0]; } /** @@ -397,7 +469,15 @@ * @param array $value */ private static function doArrayCast ( &$value ) { - $value[self::B_RESULT] = (array)$value[self::B_PARAM_2]; + $v = $value[self::B_PARAM_2]; + if ( is_object( $v ) ) { + if ( $v instanceof GenericObject ) { + $v = $v->getDumpValue(); + } else { + throw new PhpTagsException( PhpTagsException::FATAL_INTERNAL_ERROR, __LINE__ ); + } + } + $value[self::B_RESULT] = (array)$v; } /** @@ -413,7 +493,7 @@ * @param array $value */ private static function doUnsetCast ( &$value ) { - $value[self::B_RESULT] = (unset)$value[self::B_PARAM_2]; + $value[self::B_RESULT] = null; //(unset)$value[self::B_PARAM_2]; } /** @@ -673,9 +753,9 @@ private static function doCallingHook ( &$value ) { $result = Hooks::callHook( $value ); - if ( $result instanceof outPrint ) { - $value[self::B_RESULT] = $result->returnValue; - self::$stack[0][self::S_RETURN][] = $result; + if ( $result instanceof iRawOutput ) { + $value[self::B_RESULT] = $result->getReturnValue(); + self::$stack[0][self::S_RETURN][] = $result->placeAsStripItem(); } else { $value[self::B_RESULT] = $result; } @@ -735,16 +815,16 @@ */ private static function doIsSet ( &$value ) { $variables =& self::$stack[0][self::S_VARIABLES]; - foreach($value[self::B_PARAM_1] as $val) { - if( !isset($variables[ $val[self::B_PARAM_1] ]) ) { // undefined variable or variable is null + foreach ( $value[self::B_PARAM_1] as $val ) { + if ( !isset( $variables[ $val[self::B_PARAM_1] ] ) ) { // undefined variable or variable is null $value[self::B_RESULT] = false; return; } // true, variable is defined - if( isset($val[self::B_ARRAY_INDEX]) ) { // Example: isset($foo[1]) + if( isset( $val[self::B_ARRAY_INDEX] ) ) { // Example: isset($foo[1]) $ref =& $variables[ $val[self::B_PARAM_1] ]; $tmp = array_pop( $val[self::B_ARRAY_INDEX] ); foreach( $val[self::B_ARRAY_INDEX] as $v ) { - if( !isset($ref[$v]) ) { // undefined array index + if( !isset( $ref[$v] ) ) { // undefined array index $value[self::B_RESULT] = false; return; } @@ -866,7 +946,8 @@ */ private static function doSetConcatVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref .= $value[self::B_PARAM_2]; + $v = self::checkStringParams( $ref, $value[self::B_PARAM_2], $value[self::B_FLAGS] ); + $value[self::B_RESULT] = $ref = $v[0] . $v[1]; } /** @@ -875,7 +956,8 @@ */ private static function doSetPlusVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref += $value[self::B_PARAM_2]; + $v = self::checkArrayParams( $ref, $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $ref = $v[0] + $v[1]; } /** @@ -884,7 +966,8 @@ */ private static function doSetMinusVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref -= $value[self::B_PARAM_2]; + $v = self::checkScalarParams( $ref, $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $ref = $v[0] - $v[1]; } /** @@ -893,7 +976,8 @@ */ private static function doSetMulVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref *= $value[self::B_PARAM_2]; + $v = self::checkScalarParams( $ref, $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $ref = $v[0] * $v[1]; } /** @@ -902,7 +986,13 @@ */ private static function doSetDivVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref /= $value[self::B_PARAM_2]; + $v = self::checkScalarParams( $ref, $value[self::B_PARAM_2] ); + if ( $v[1] == 0 ) { + self::pushException( new PhpTagsException( PhpTagsException::WARNING_DIVISION_BY_ZERO ) ); + $value[self::B_RESULT] = $ref = false; + } else { + $value[self::B_RESULT] = $ref = $v[0] / $v[1]; + } } /** @@ -911,7 +1001,13 @@ */ private static function doSetModVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref %= $value[self::B_PARAM_2]; + $v = self::checkScalarParams( $ref, $value[self::B_PARAM_2] ); + if ( $v[1] == 0 ) { + self::pushException( new PhpTagsException( PhpTagsException::WARNING_DIVISION_BY_ZERO ) ); + $value[self::B_RESULT] = $ref = false; + } else { + $value[self::B_RESULT] = $ref = $v[0] % $v[1]; + } } /** @@ -920,7 +1016,8 @@ */ private static function doSetAndVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref &= $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $ref, $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $ref = $v[0] & $v[1]; } /** @@ -929,7 +1026,8 @@ */ private static function doSetOrVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref |= $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $ref, $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $ref = $v[0] | $v[1]; } /** @@ -938,7 +1036,8 @@ */ private static function doSetXorVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref ^= $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $ref, $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $ref = $v[0] ^ $v[1]; } /** @@ -947,7 +1046,8 @@ */ private static function doSetShiftLeftVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref <<= $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $ref, $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $ref = $v[0] << $v[1]; } /** @@ -956,7 +1056,86 @@ */ private static function doSetShiftRightVal ( &$value ) { $ref =& self::getVariableRef( $value ); - $value[self::B_RESULT] = $ref <<= $value[self::B_PARAM_2]; + $v = self::checkObjectParams( $ref, $value[self::B_PARAM_2] ); + $value[self::B_RESULT] = $ref = $v[0] >> $v[1]; + } + + private static function checkStringParams( &$v1, &$v2, &$flags ) { + $return = array( $v1, $v2 ); + + if ( $v1 !== null && !is_scalar( $v1 ) ) { + if ( is_array( $v1 ) ) { + if ( !($flags & self::F_DONT_CHECK_PARAM1) ) { + self::pushException( new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING ) ); + $return[0] = self::R_ARRAY; + } + } else if ( $v1 instanceof GenericObject ) { + $return[0] = $v1->toString(); + } else { + throw new PhpTagsException( PhpTagsException::FATAL_INTERNAL_ERROR, __LINE__ ); + } + } + if ( $v2 !== null && !is_scalar( $v2 ) ) { + if ( is_array( $v2 ) ) { + self::pushException( new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING ) ); + $return[1] = self::R_ARRAY; + } else if ( $v2 instanceof GenericObject ) { + $return[1] = $v2->toString(); + } else { + throw new PhpTagsException( PhpTagsException::FATAL_INTERNAL_ERROR, __LINE__ ); + } + } + + return $return; + } + + private static function checkArrayParams( &$v1, &$v2 ) { + if ( !($v1 === null || is_scalar( $v1 )) || !($v2 === null || is_scalar( $v2 )) ) { + $return = self::checkObjectParams( $v1, $v2 ); + if ( is_array( $v1 ) xor is_array( $v2 ) ) { // [1] + 1 or 1 + [1] + throw new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES ); + } + } else { + $return = array( $v1, $v2 ); + } + + return $return; + } + + private static function checkScalarParams( &$v1, &$v2 ) { + if ( !($v1 === null || is_scalar( $v1 )) || !($v2 === null || is_scalar( $v2 )) ) { + $return = self::checkObjectParams( $v1, $v2 ); + if ( is_array( $v1 ) || is_array( $v2 ) ) { // [1] + 1 or 1 + [1] + throw new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES ); + } + } else { + $return = array( $v1, $v2 ); + } + + return $return; + } + + private static function checkObjectParams( &$v1, &$v2, $to = 'int' ) { + $return = array( $v1, $v2 ); + + if ( is_object( $v1 ) ) { + if ( $v1 instanceof GenericObject ) { + self::pushException( new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array($v1->getName(), $to) ) ); + $return[0] = 1; + } else { + throw new PhpTagsException( PhpTagsException::FATAL_INTERNAL_ERROR, __LINE__ ); + } + } + if ( is_object( $v2 ) ) { + if ( $v2 instanceof GenericObject ) { + self::pushException( new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array($v2->getName(), $to) ) ); + $return[1] = 1; + } else { + throw new PhpTagsException( PhpTagsException::FATAL_INTERNAL_ERROR, __LINE__ ); + } + } + + return $return; } private static function & getVariableRef( $value ) { @@ -972,6 +1151,18 @@ $ref =& $variables[$variableName]; if ( isset($var[self::B_ARRAY_INDEX]) ) { // Example: $foo[1]++ foreach ( $var[self::B_ARRAY_INDEX] as $v ) { + if ( $ref === true || $ref === false ) { + if ( $v === INF ) { // Example: $foo[] + $ref = array(); + } + } else if ( is_scalar( $ref ) ) { + self::pushException( new PhpTagsException( PhpTagsException::WARNING_SCALAR_VALUE_AS_ARRAY, null ) ); + unset( $ref ); + $ref = null; + break; + } else if ( is_object( $ref ) ) { + throw new PhpTagsException( PhpTagsException::FATAL_CANNOT_USE_OBJECT_AS_ARRAY, $ref ); + } if ( $v === INF ) { // Example: $foo[] $t = null; $ref[] = &$t; @@ -985,7 +1176,7 @@ } $ref[$v] = null; $ref =& $ref[$v]; - } elseif ( is_array($ref) ) { + } else { if ( !( isset($ref[$v]) || array_key_exists($v, $ref) ) ) { $ref[$v] = null; if( $value[self::B_COMMAND] !== self::T_EQUAL ) { @@ -994,12 +1185,6 @@ } } $ref =& $ref[$v]; - } else { // scalar - // PHP Warning: Cannot use a scalar value as an array - self::pushException( new PhpTagsException( PhpTagsException::WARNING_SCALAR_VALUE_AS_ARRAY, null ) ); - unset( $ref ); - $ref = null; - break; } } } @@ -1008,7 +1193,6 @@ } public static function run( $code, array $args, $scope = '' ) { - set_error_handler( '\\PhpTags\\ErrorHandler::onError' ); try { if( false === isset( self::$variables[$scope] ) ) { self::$variables[$scope] = array(); @@ -1054,17 +1238,15 @@ self::$ignoreErrors = false; } catch ( \Exception $e ) { Renderer::addRuntimeErrorCategory(); - restore_error_handler(); self::$ignoreErrors = false; array_shift( self::$stack ); throw $e; } - restore_error_handler(); array_shift( self::$stack ); return $stack[self::S_RETURN]; } - static function fillList( &$values, &$parametrs, $offset = false ) { + private static function fillList( &$values, &$parametrs, $offset = false ) { $return = array(); for ( $pkey = count( $parametrs ) - 1; $pkey >= 0; --$pkey ) { diff --git a/includes/iRawOutput.php b/includes/iRawOutput.php index 33813cb..6e3abc0 100644 --- a/includes/iRawOutput.php +++ b/includes/iRawOutput.php @@ -9,5 +9,11 @@ * @licence GNU General Public Licence 2.0 or later */ interface iRawOutput { + public function __toString(); -} \ No newline at end of file + + public function getReturnValue(); + + public function placeAsStripItem(); + +} diff --git a/includes/outPrint.php b/includes/outPrint.php index 22f4540..4d41582 100644 --- a/includes/outPrint.php +++ b/includes/outPrint.php @@ -9,28 +9,41 @@ * @licence GNU General Public Licence 2.0 or later */ class outPrint implements iRawOutput { - public $returnValue=null; - private $contents; - private $raw; + private $returnValue; + private $content; private $element; private $attribs; - public function __construct( $returnValue, $contents, $raw=false, $element='pre', $attribs = array() ) { + /** + * + * @param mixed $returnValue + * @param string $content + * @param bool $raw + * @param string|false $element + * @param array $attribs + * @param array $sheath + */ + public function __construct( $returnValue, $content, $raw=false, $element='pre', $attribs = array() ) { $this->returnValue = $returnValue; - $this->raw = $raw; - $this->contents = (string)$contents; + $this->content = $raw ? (string)$content : strtr( $content, array('&'=>'&', '<'=>'<') ); $this->element = $element; $this->attribs = $attribs; } public function __toString() { - if( $this->element !== false ){ - if( $this->raw ) { - return \Html::rawElement( $this->element, array(), $this->contents ) . "\n"; - }else{ - return \Html::element( $this->element, array(), $this->contents ) . "\n"; - } + if ( $this->element ) { + return \Html::rawElement( $this->element, $this->attribs, $this->content ); } - return $this->raw ? "{$this->contents}\n" : strtr( $this->contents, array('&'=>'&', '<'=>'<') ) . "\n"; + return $this->content; } + + public function getReturnValue() { + return $this->returnValue; + } + + public function placeAsStripItem() { + $this->content = Renderer::insertNoWiki( $this->content ); + return Renderer::insertStripItem( $this ); + } + } diff --git a/includes/outStrip.php b/includes/outStrip.php new file mode 100644 index 0000000..387e80f --- /dev/null +++ b/includes/outStrip.php @@ -0,0 +1,41 @@ +<?php +namespace PhpTags; +/** + * The outStrip class of the extension PHP Tags. + * + * @file outPrint.php + * @ingroup PhpTags + * @author Pavel Astakhov <pastak...@yandex.ru> + * @licence GNU General Public Licence 2.0 or later + */ +class outStrip implements iRawOutput { + private $returnValue; + private $strip; + + /** + * + * @param mixed $returnValue + * @param string $strip + * @param bool $raw + * @param string|false $element + * @param array $attribs + * @param array $sheath + */ + public function __construct( $returnValue, $strip ) { + $this->returnValue = $returnValue; + $this->strip = $strip; + } + + public function __toString() { + return $this->strip; + } + + public function getReturnValue() { + return $this->returnValue; + } + + public function placeAsStripItem() { + return $this->strip; + } + +} diff --git "a/tests/phpunit/includes/\041\041\041firstInit_Test.php" "b/tests/phpunit/includes/\041\041\041firstInit_Test.php" index f2c021b..3815e37 100644 --- "a/tests/phpunit/includes/\041\041\041firstInit_Test.php" +++ "b/tests/phpunit/includes/\041\041\041firstInit_Test.php" @@ -1,20 +1,24 @@ <?php -\PhpTags\Hooks::addJsonFile( __DIR__ . '/PhpTags_test.json' ); -const PHPTAGS_TEST = 'Test'; -const PHPTAGS_TEST_BANNED = 'Test'; -\Hooks::register( 'PhpTagsBeforeCallRuntimeHook', function ( $hookType, $objectName, $methodName, $values ) { - if ( $hookType === \PhpTags\Runtime::H_GET_CONSTANT || $hookType === \PhpTags\Runtime::H_GET_OBJECT_CONSTANT ) { - $methodName = strtolower( $methodName ); - } - if ( substr( $methodName, -6 ) === 'banned' ) { - $hookTypeString = \PhpTags\Hooks::getCallInfo( \PhpTags\Hooks::INFO_HOOK_TYPE_STRING ); - \PhpTags\Runtime::pushException( new \PhpTags\HookException( "Sorry, you cannot use this $hookTypeString" ) ); - return false; - } - return true; -} ); if ( \PhpTags\Renderer::$needInitRuntime ) { + wfDebug( 'PHPTags: test initialization ' . __FILE__ ); + + \PhpTags\Hooks::addJsonFile( __DIR__ . '/PhpTags_test.json' ); + define( 'PHPTAGS_TEST', 'Test' ); + define( 'PHPTAGS_TEST_BANNED', 'Test' ); + \Hooks::register( 'PhpTagsBeforeCallRuntimeHook', function ( $hookType, $objectName, $methodName, $values ) { + if ( $hookType === \PhpTags\Runtime::H_GET_CONSTANT || $hookType === \PhpTags\Runtime::H_GET_OBJECT_CONSTANT ) { + $methodName = strtolower( $methodName ); + } + if ( substr( $methodName, -6 ) === 'banned' ) { + $hookTypeString = \PhpTags\Hooks::getCallInfo( \PhpTags\Hooks::INFO_HOOK_TYPE_STRING ); + \PhpTags\Runtime::pushException( new \PhpTags\HookException( "Sorry, you cannot use this $hookTypeString" ) ); + return false; + } + return true; + } ); + + wfDebug( 'PHPTags: run hook PhpTagsRuntimeFirstInit ' . __FILE__ ); \Hooks::run( 'PhpTagsRuntimeFirstInit' ); \PhpTags\Hooks::loadData(); \PhpTags\Runtime::$loopsLimit = 1000; diff --git a/tests/phpunit/includes/RuntimeTest.php b/tests/phpunit/includes/RuntimeTest.php index ed08b5d..c47c554 100644 --- a/tests/phpunit/includes/RuntimeTest.php +++ b/tests/phpunit/includes/RuntimeTest.php @@ -53,6 +53,131 @@ ); } + public function testRun_echo_heredoc_1() { + $this->assertEquals( + Runtime::runSource(' +echo <<<EOT +Example of string +spanning multiple lines +using heredoc syntax. +EOT; +'), + array('Example of string +spanning multiple lines +using heredoc syntax. +') + ); + } + public function testRun_echo_heredoc_2() { + $this->assertEquals( + Runtime::runSource(' +echo <<<EOT +Example of string +spanning multiple lines +using "heredoc" syntax. +EOT; +'), + array('Example of string +spanning multiple lines +using "heredoc" syntax. +') + ); + } + public function testRun_echo_heredoc_3() { + $this->assertEquals( + Runtime::runSource(' +echo <<<EOT +Example of string +spanning multiple\n lines\n +using "heredoc" syntax. +EOT; +'), + array('Example of string +spanning multiple + lines + +using "heredoc" syntax. +') + ); + } + public function testRun_echo_heredoc_4() { + $this->assertEquals( + Runtime::runSource(' +$foo = "BAR"; +echo <<<"EOT" +Example of string $foo +spanning multiple lines +using heredoc syntax. +EOT; +'), + array('Example of string BAR +spanning multiple lines +using heredoc syntax. +') + ); + } + public function testRun_echo_nowdoc_1() { + $this->assertEquals( + Runtime::runSource(' +echo <<<\'EOT\' +Example of string +spanning multiple lines +using nowdoc syntax. +EOT; +'), + array('Example of string +spanning multiple lines +using nowdoc syntax. +') + ); + } + public function testRun_echo_nowdoc_2() { + $this->assertEquals( + Runtime::runSource(' +echo <<<\'EOT\' +Example of string +spanning multiple lines +using "nowdoc" syntax. +EOT; +'), + array('Example of string +spanning multiple lines +using "nowdoc" syntax. +') + ); + } + public function testRun_echo_nowdoc_3() { + $this->assertEquals( + Runtime::runSource(' +echo <<<\'EOT\' +Example of string +spanning multiple\n lines\n +using "nowdoc" syntax. +EOT; +'), + array('Example of string +spanning multiple\n lines\n +using "nowdoc" syntax. +') + ); + } + public function testRun_echo_nowdoc_4() { + $this->assertEquals( + Runtime::runSource(' +$foo = "BAR"; +echo <<<\'EOT\' +Example of string $foo +spanning multiple lines +using nowdoc syntax. +EOT; +'), + array('Example of string $foo +spanning multiple lines +using nowdoc syntax. +') + ); + } + public function testRun_echo_union_1() { $this->assertEquals( Runtime::runSource('echo "String" . "Union";'), @@ -977,6 +1102,328 @@ $this->assertEquals( Runtime::runSource('$foo=1; $bar=2; $foo+=$bar; echo $foo,$bar;'), array('3', '2') + ); + } + public function testRun_echo_assignment_7() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo = $foo . $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY . 4, + '4', + ) + ); + } + public function testRun_echo_assignment_8() { + $this->assertEquals( + Runtime::runSource( '$foo="4"; $bar=["rrr"]; $foo = $foo . $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + 4 . Runtime::R_ARRAY, + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY, + ) + ); + } + public function testRun_echo_assignment_9() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar=["rrr"]; $foo = $foo . $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY . Runtime::R_ARRAY, + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY, + ) + ); + } + public function testRun_echo_assignment_10() { + $this->assertEquals( + Runtime::runSource( '$foo="4"; $bar=["rrr"]; $foo .= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + 4 . Runtime::R_ARRAY, + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY, + ) + ); + } + public function testRun_echo_assignment_11() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo .= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY . 'rrr', + 'rrr', + ) + ); + } + public function testRun_echo_assignment_12() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar=["rrr"]; $foo .= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY . Runtime::R_ARRAY, + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY, + ) + ); + } + public function testRun_echo_assignment_13() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo=$foo+$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_14() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo+=$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_15() { + $this->assertEquals( + Runtime::runSource( '$foo="rrr"; $bar=["4"]; $foo+=$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_16() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar=["4"]; $foo+=$bar; echo $foo[0];', array('test'), 77777 ), + array( 'rrr' ) + ); + } + public function testRun_echo_assignment_17() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar=["4"]; $foo = $foo + $bar; echo $foo[0];', array('test'), 77777 ), + array( 'rrr' ) + ); + } + public function testRun_echo_assignment_18() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo=$foo-$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_19() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo-=$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_20() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo=$foo*$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_21() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo*=$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_22() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo=$foo/$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_23() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar="4"; $foo/=$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_24() { + $this->assertEquals( + Runtime::runSource( '$foo="rrr"; $bar=["4"]; $foo=$foo%$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_25() { + $this->assertEquals( + Runtime::runSource( '$foo="rrr"; $bar=["4"]; $foo%=$bar; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_26() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = ($foo and $bar); echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_27() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = ($foo or $bar); echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_28() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = ($foo Xor $bar); echo $foo,$bar;', array('test'), 77777 ), + array( '', 'rrr' ) + ); + } + public function testRun_echo_assignment_29() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = ($foo & $bar); echo $foo,$bar;', array('test'), 77777 ), + array( '0', 'rrr' ) + ); + } + public function testRun_echo_assignment_30() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = ($foo | $bar); echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_31() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = ($foo ^ $bar); echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_32() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo |= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_33() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo ^= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_34() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo &= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( '0', 'rrr' ) + ); + } + public function testRun_echo_assignment_35() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = ($foo >> $bar); echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_36() { + $this->assertEquals( + Runtime::runSource( '$foo="rrr"; $bar=["rrr"]; $foo = ($foo << $bar); echo $foo,$bar[0];', array('test'), 77777 ), + array( '0', 'rrr' ) + ); + } + public function testRun_echo_assignment_37() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo >>= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_38() { + $this->assertEquals( + Runtime::runSource( '$foo="rrr"; $bar=["rrr"]; $foo <<= $bar; echo $foo,$bar[0];', array('test'), 77777 ), + array( '0', 'rrr' ) + ); + } + public function testRun_echo_assignment_39() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = $foo > $bar; echo $foo,$bar;', array('test'), 77777 ), + array( '1', 'rrr' ) + ); + } + public function testRun_echo_assignment_40() { + $this->assertEquals( + Runtime::runSource( '$foo="rrr"; $bar=["rrr"]; $foo = $foo < $bar; echo $foo,$bar[0];', array('test'), 77777 ), + array( true, 'rrr' ) + ); + } + public function testRun_echo_assignment_41() { + $this->assertEquals( + Runtime::runSource( '$foo=["4"]; $bar="rrr"; $foo = $foo == $bar; echo $foo,$bar;', array('test'), 77777 ), + array( false, 'rrr' ) + ); + } + public function testRun_echo_assignment_42() { + $this->assertEquals( + Runtime::runSource( '$foo="rrr"; $bar=["rrr"]; $foo = $foo != $bar; echo $foo,$bar[0];', array('test'), 77777 ), + array( true, 'rrr' ) + ); + } + public function testRun_echo_assignment_43() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $foo = ~$bar; echo $foo,$bar[0];', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_echo_assignment_44() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $foo = !$bar; echo $foo === false;', array('test'), 77777 ), + array( true ) + ); + } + public function testRun_echo_assignment_45() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $foo=(int)$bar; echo $foo === 1;', array('test'), 77777 ), + array( true ) + ); + } + public function testRun_echo_assignment_46() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $foo=(double)$bar; echo $foo === (double)1;', array('test'), 77777 ), + array( true ) + ); + } + public function testRun_echo_assignment_47() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $foo=(string)$bar; echo $foo;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY, + ) + ); + } + public function testRun_echo_assignment_48() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $foo=(array)$bar; echo $foo[0];', array('test'), 77777 ), + array( 'rrr' ) + ); + } + public function testRun_echo_assignment_49() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $foo=(bool)$bar; echo $foo === true;', array('test'), 77777 ), + array( true ) + ); + } + public function testRun_echo_assignment_50() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $foo=(unset)$bar; echo $foo === null;', array('test'), 77777 ), + array( true ) + ); + } + + public function testRun_array_increase_test_1() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $bar++; echo $bar[0];', array('test'), 77777 ), + array( 'rrr' ) + ); + } + public function testRun_array_increase_test_2() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; ++$bar; echo $bar[0];', array('test'), 77777 ), + array( 'rrr' ) + ); + } + public function testRun_array_increase_test_3() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; --$bar; echo $bar[0];', array('test'), 77777 ), + array( 'rrr' ) + ); + } + public function testRun_array_increase_test_4() { + $this->assertEquals( + Runtime::runSource( '$bar=["rrr"]; $bar--; echo $bar[0];', array('test'), 77777 ), + array( 'rrr' ) ); } @@ -2045,10 +2492,50 @@ Runtime::runSource('$foo=array(1); echo (string)++$foo[0];'), array('2') ); - }public function testRun_echo_array_right_increment_2() { + } + public function testRun_echo_array_right_increment_2() { $this->assertEquals( Runtime::runSource('$foo=array(1); echo (string)++$foo[0], $foo[0];'), array('2', 2) + ); + } + public function testRun_echo_array_constructor_1() { + $this->assertEquals( + Runtime::runSource(' +$b = array( "a", "b", "c" ); +$a = array( $b[0], $b[1], $b[2] ); +echo "(" . $b[0] . $b[1] . $b[2] .")--"; +echo "(" . $a[0] . $a[1] . $a[2] .")";'), + array('(abc)--', '(abc)') + ); + } + public function testRun_echo_array_exception_1() { + $this->assertEquals( + Runtime::runSource( '$t = 5; $t[] = 4; echo $t;', array('test') ), + array( + (string) new PhpTagsException( PhpTagsException::WARNING_SCALAR_VALUE_AS_ARRAY, null, 1, 'test' ), + '5', + ) + ); + } + public function testRun_echo_array_exception_2() { + $this->assertEquals( + Runtime::runSource( '$t = "5"; $t[] = 4; echo $t;', array('test') ), + array( + (string) new PhpTagsException( PhpTagsException::WARNING_SCALAR_VALUE_AS_ARRAY, null, 1, 'test' ), + '5', + ) + ); + } + public function testRun_echo_array_no_exception_3() { + $this->assertEquals( + Runtime::runSource( '$t = null; $t[] = 4; echo $t[0];', array('test') ), + array( '4' ) + ); + }public function testRun_echo_array_no_exception_4() { + $this->assertEquals( + Runtime::runSource( '$t = false; $t[] = 4; echo $t[0];', array('test') ), + array( '4' ) ); } @@ -2746,6 +3233,30 @@ array('false') ); } + public function testRun_echo_isset_boolean_and_1() { + $this->assertEquals( + Runtime::runSource( ' + $g = ["a" => 0]; $x = "a"; + if ( isset( $g[$x] ) && $x !== "kf" ) { + echo "TRUE"; + } else { + echo "FALSE"; + }'), + array( 'TRUE' ) + ); + } + public function testRun_echo_isset_boolean_or_1() { + $this->assertEquals( + Runtime::runSource( ' + $g = ["a" => 0]; $x = "a"; + if ( isset( $g[$x] ) || $x === "kf" ) { + echo "TRUE"; + } else { + echo "FALSE"; + }'), + array( 'TRUE' ) + ); + } public function testRun_echo_unset_1() { $this->assertEquals( @@ -2963,6 +3474,11 @@ Runtime::runSource('echo PHPTAGS_VERSION;'), array(PHPTAGS_VERSION) ); + $allThings = \ExtensionRegistry::getInstance()->getAllThings(); + $this->assertEquals( + Runtime::runSource('echo PHPTAGS_VERSION;'), + array($allThings['PhpTags']['version']) + ); } public function testRun_echo_exception_1() { @@ -3049,14 +3565,33 @@ public function testRun_echo_exception_11() { $this->assertEquals( array( false ), Runtime::runSource( 'echo @(5/0);', array('Test') ) ); } + public function testRun_echo_exception_12() { + $this->assertEquals( + array( + (string) new PhpTagsException( PhpTagsException::WARNING_DIVISION_BY_ZERO, null, 1, 'Test' ), + false, + ), + Runtime::runSource( 'echo 5%0;', array('Test') ) + ); + } public function testRun_constant_test() { + wfDebug( 'PHPTags: this message must be after PHPTags test initialization. ' . __METHOD__ ); + if ( !defined( 'PHPTAGS_TEST' ) ) { + wfDebug( 'PHPTags: constant PHPTAGS_TEST is not defined, skip PhpTagsRuntimeFirstInit hook tests. ' . __METHOD__ ); + return; + } + $this->assertEquals( Runtime::runSource( 'echo PHPTAGS_TEST;' ), array( 'Test' ) ); } public function testRun_constant_test_banned() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $result = Runtime::runSource( 'echo PHPTAGS_TEST_BANNED;', array('Test ban') ); $exc1 = new \PhpTags\HookException( 'Sorry, you cannot use this constant' ); @@ -3069,12 +3604,20 @@ $this->assertEquals( $result, array( (string)$exc1, (string)$exc2, false ) ); } public function testRun_constant_class_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $this->assertEquals( Runtime::runSource( 'echo PHPTAGS_TEST_IN_CLASS;' ), array( 'I am constant PHPTAGS_TEST_IN_CLASS' ) ); } public function testRun_constant_class_banned_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $result = Runtime::runSource( 'echo PHPTAGS_TEST_IN_CLASS_BANNED;', array('Test ban') ); $exc1 = new \PhpTags\HookException( 'Sorry, you cannot use this constant' ); @@ -3087,12 +3630,20 @@ $this->assertEquals( $result, array( (string)$exc1, (string)$exc2, false ) ); } public function testRun_object_constant_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $this->assertEquals( Runtime::runSource( 'echo PhpTagsTest::OBJ_TEST;' ), array( 'c_OBJ_TEST' ) ); } public function testRun_object_constant_banned_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $result = Runtime::runSource( 'echo PhpTagsTest::OBJ_TEST_BANNED;', array('Test ban') ); $exc1 = new \PhpTags\HookException( 'Sorry, you cannot use this object constant' ); @@ -3105,12 +3656,20 @@ $this->assertEquals( $result, array( (string)$exc1, (string)$exc2, false ) ); } public function testRun_function_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $this->assertEquals( Runtime::runSource( 'echo PhpTagsTestfunction();' ), array( 'f_phptagstestfunction' ) ); } public function testRun_function_banned_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $result = Runtime::runSource( 'echo PhpTagsTestfunction_BANNED();', array('Test ban') ); $exc1 = new \PhpTags\HookException( 'Sorry, you cannot use this function' ); @@ -3123,12 +3682,20 @@ $this->assertEquals( $result, array( (string)$exc1, (string)$exc2, false ) ); } public function testRun_object_method_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $this->assertEquals( Runtime::runSource( '$obj = new PhpTagsTest(); echo $obj->myMETHOD();' ), array( 'm_mymethod' ) ); } public function testRun_object_method_banned_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $result = Runtime::runSource( '$obj = new PhpTagsTest(); echo $obj->myMETHOD_BaNnEd();', array('Test ban') ); $exc1 = new \PhpTags\HookException( 'Sorry, you cannot use this method' ); @@ -3141,12 +3708,20 @@ $this->assertEquals( $result, array( (string)$exc1, (string)$exc2, false ) ); } public function testRun_static_method_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $this->assertEquals( Runtime::runSource( 'echo PhpTagsTest::mystaticMETHOD();' ), array( 's_mystaticmethod' ) ); } public function testRun_static_method_banned_test() { + if ( !defined( 'PHPTAGS_TEST' ) ) { + return; + } + $result = Runtime::runSource( 'echo PhpTagsTest::mystaticMETHOD_BaNnEd();', array('Test ban') ); $exc1 = new \PhpTags\HookException( 'Sorry, you cannot use this static method' ); @@ -3159,4 +3734,297 @@ $this->assertEquals( $result, array( (string)$exc1, (string)$exc2, false ) ); } + public function testRun_object_operation_test_1() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar=new PhpTagsTest(); $foo = $foo . $bar; echo $foo[0],$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + (string) new PhpTagsException( PhpTagsException::FATAL_OBJECT_COULD_NOT_BE_CONVERTED, array('PhpTagsTest', 'string'), 1, 'test' ), + ) + ); + } + public function testRun_object_operation_test_2() { + $this->assertEquals( + Runtime::runSource( '$foo=["rrr"]; $bar=new PhpTagsTest(); $foo = $foo + $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ), + ) + ); + } + public function testRun_object_operation_test_3() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=["rrr"]; $foo += $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ), + ) + ); + } + public function testRun_object_operation_test_4() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $bar + $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + 6, + 5, + ) + ); + } + public function testRun_object_operation_test_5() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $bar - $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + 4, + 5, + ) + ); + } + public function testRun_object_operation_test_6() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo -= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + -4, + 5, + ) + ); + } + public function testRun_object_operation_test_7() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $foo > $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + false, + 5, + ) + ); + } + public function testRun_object_operation_test_8() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $foo < $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + true, + 5, + ) + ); + } + public function testRun_object_operation_test_9() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $bar <= $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + false, + 5, + ) + ); + } + public function testRun_object_operation_test_10() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $bar >= $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + true, + 5, + ) + ); + } + public function testRun_object_operation_test_11() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $bar == $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + false, + 5, + ) + ); + } + public function testRun_object_operation_test_12() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $bar != $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + true, + 5, + ) + ); + } + public function testRun_object_operation_test_13() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $bar === $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + false, + 5, + ) + ); + } + public function testRun_object_operation_test_14() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=5; $foo = $bar !== $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + true, + 5, + ) + ); + } + public function testRun_object_operation_test_15() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=8; $foo = $bar | $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + 9, + 8, + ) + ); + } + public function testRun_object_operation_test_16() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=8; $foo |= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + 9, + 8, + ) + ); + } + public function testRun_object_operation_test_17() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=8; $foo = $bar >> $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + 4, + 8, + ) + ); + } + public function testRun_object_operation_test_18() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=8; $foo <<= $bar; echo $foo,$bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + 256, + 8, + ) + ); + } + public function testRun_object_operation_test_19() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=8; $foo = $bar && $foo; echo $foo,$bar;', array('test'), 77777 ), + array( + true, + 8, + ) + ); + } + public function testRun_object_operation_test_20() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar= ~$foo; echo $foo,$bar;', array('test'), 77777 ), + array( (string) new PhpTagsException( PhpTagsException::FATAL_UNSUPPORTED_OPERAND_TYPES, null, 1, 'test' ) ) + ); + } + public function testRun_object_operation_test_21() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar= !$foo; echo $bar === false;', array('test'), 77777 ), + array( true ) + ); + } + public function testRun_object_operation_test_22() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=(int)$foo; echo $bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'int'), 1, 'test' ), + 1, + ) + ); + } + public function testRun_object_operation_test_23() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=(double)$foo; echo $bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_OBJECT_CONVERTED, array('PhpTagsTest', 'double'), 1, 'test' ), + 1, + ) + ); + } + public function testRun_object_operation_test_24() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=(string)$foo; echo $bar;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::FATAL_OBJECT_COULD_NOT_BE_CONVERTED, array('PhpTagsTest', 'string'), 1, 'test' ), + ) + ); + } + public function testRun_object_operation_test_25() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=(array)$foo; echo $bar[0];', array('test'), 77777 ), + array( '(object <PhpTagsTest>)' ) + ); + } + public function testRun_object_operation_test_26() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=(bool)$foo; echo $bar === true;', array('test'), 77777 ), + array( true ) + ); + } + public function testRun_object_operation_test_27() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $bar=(unset)$foo; echo $bar === null;', array('test'), 77777 ), + array( true ) + ); + } + + public function testRun_object_increase_test_1() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $foo++; echo $foo;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::FATAL_OBJECT_COULD_NOT_BE_CONVERTED, array('PhpTagsTest', 'string'), 1, 'test' ), + ) + ); + } + public function testRun_object_increase_test_2() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); ++$foo; echo $foo;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::FATAL_OBJECT_COULD_NOT_BE_CONVERTED, array('PhpTagsTest', 'string'), 1, 'test' ), + ) + ); + } + public function testRun_object_increase_test_3() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); --$foo; echo $foo;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::FATAL_OBJECT_COULD_NOT_BE_CONVERTED, array('PhpTagsTest', 'string'), 1, 'test' ), + ) + ); + } + public function testRun_object_increase_test_4() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); $foo--; echo $foo;', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::FATAL_OBJECT_COULD_NOT_BE_CONVERTED, array('PhpTagsTest', 'string'), 1, 'test' ), + ) + ); + } + + public function testRun_array_quote_test_1() { + $this->assertEquals( + Runtime::runSource( '$foo=["bar"]; echo "$foo";', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::NOTICE_ARRAY_TO_STRING, null, 1, 'test' ), + Runtime::R_ARRAY, + ) + ); + } + public function testRun_object_quote_test_1() { + $this->assertEquals( + Runtime::runSource( '$foo=new PhpTagsTest(); echo "$foo";', array('test'), 77777 ), + array( + (string) new PhpTagsException( PhpTagsException::FATAL_OBJECT_COULD_NOT_BE_CONVERTED, array('PhpTagsTest', 'string'), 1, 'test' ), + ) + ); + } + + } -- To view, visit https://gerrit.wikimedia.org/r/304433 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I81b2c89a3a8a13ebf9f3ea1c14c3acbd91c0b2e7 Gerrit-PatchSet: 3 Gerrit-Project: mediawiki/extensions/PhpTags Gerrit-Branch: REL1_26 Gerrit-Owner: Pastakhov <pastak...@yandex.ru> Gerrit-Reviewer: Pastakhov <pastak...@yandex.ru> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits