Pastakhov has submitted this change and it was merged. Change subject: add class BaseHooks ......................................................................
add class BaseHooks Change-Id: I3aa6723f9377a03732dfb262aa815a5c4caa9a42 --- M PhpTags.php A includes/BaseHooks.php M includes/ExceptionPhpTags.php M includes/Runtime.php M tests/phpunit/includes/RuntimeTest.php 5 files changed, 157 insertions(+), 97 deletions(-) Approvals: Pastakhov: Verified; Looks good to me, approved diff --git a/PhpTags.php b/PhpTags.php index e00f77c..a911d05 100644 --- a/PhpTags.php +++ b/PhpTags.php @@ -73,6 +73,7 @@ $wgAutoloadClasses['PhpTags\\ExceptionPhpTags'] = __DIR__ . '/includes/ExceptionPhpTags.php'; $wgAutoloadClasses['PhpTags\\Compiler'] = __DIR__ . '/includes/Compiler.php'; $wgAutoloadClasses['PhpTags\\Runtime'] = __DIR__ . '/includes/Runtime.php'; +$wgAutoloadClasses['PhpTags\\BaseHooks'] = __DIR__ . '/includes/BaseHooks.php'; /** * Add files to phpunit test diff --git a/includes/BaseHooks.php b/includes/BaseHooks.php new file mode 100644 index 0000000..51aec92 --- /dev/null +++ b/includes/BaseHooks.php @@ -0,0 +1,84 @@ +<?php +namespace PhpTags; + +define( 'PHPTAGS_HOOK_INVOKE', '=' ); +define( 'PHPTAGS_HOOK_VALUE_TYPE', 0 ); +define( 'PHPTAGS_HOOK_NEED_LINK', 1 ); +define( 'PHPTAGS_HOOK_DEFAULT_VALUE', 2 ); +define( 'PHPTAGS_HOOK_RETURNS_ON_FAIL', 1 ); + +define( 'PHPTAGS_TYPE_ARRAY', 'a' ); +define( 'PHPTAGS_TYPE_INT', 'i' ); + +/** + * This class is base for all constants, functions and objects hooks in the extension PhpTags + * + * @file BaseHooks.php + * @ingroup PhpTags + * @author Pavel Astakhov <pastak...@yandex.ru> + * @licence GNU General Public Licence 2.0 or later + */ +abstract class BaseHooks { + protected static $functions_definition = array(); + + abstract static function getClassName(); + + public static function onFunctionHook( $name, $params ) { + if ( !isset(static::$functions_definition[$name]) ) { + return new ExceptionPhpTags( PHPTAGS_EXCEPTION_WARNING_INVALID_HOOK, array($name, static::getClassName()) ); + } + $definition = static::$functions_definition[$name]; + $args = array(); + for ( $i=0, $c=count($params); $i < $c; $i++ ) { + if ( !isset($definition[$i+1]) ) { + Runtime::addException( + new ExceptionPhpTags( PHPTAGS_EXCEPTION_WARNING_WRONG_PARAMETER_COUNT, $name ) + ); + return; + } + + if ( $definition[$i+1][PHPTAGS_HOOK_NEED_LINK] ) { + if ( $params[$i][PHPTAGS_STACK_COMMAND] != T_VARIABLE ) { + return new ExceptionPhpTags( PHPTAGS_EXCEPTION_FATAL_VALUE_PASSED_BY_REFERENCE ); + } + $args[$i] = &$params[$i][PHPTAGS_STACK_RESULT]; + } else { + $args[$i] = $params[$i][PHPTAGS_STACK_RESULT]; + } + + switch ( $definition[$i+1][PHPTAGS_HOOK_VALUE_TYPE] ) { + case PHPTAGS_TYPE_ARRAY: + if ( !is_array($args[$i]) ) { + Runtime::addException( + new ExceptionPhpTags( PHPTAGS_EXCEPTION_WARNING_EXPECTS_PARAMETER, array($name, $i+1, 'array', gettype($args[$i])) ) + ); + return $definition[0][PHPTAGS_HOOK_RETURNS_ON_FAIL]; + } + break; + case PHPTAGS_TYPE_INT: + if ( is_object($args[$i]) ) { + Runtime::addException( + // @todo unknown + new ExceptionPhpTags( PHPTAGS_EXCEPTION_NOTICE_OBJECT_CONVERTED, array('unknown', 'int') ) + ); + unset( $args[$i] ); + $args[$i] = 1; + } + break; + default: + break; + } + } + while ( !isset($definition[PHPTAGS_HOOK_INVOKE][$i]) ) { + if ( !isset($definition[$i+1][PHPTAGS_HOOK_DEFAULT_VALUE]) ) { + Runtime::addException( + new ExceptionPhpTags( PHPTAGS_EXCEPTION_WARNING_WRONG_PARAMETER_COUNT, $name ) + ); + return; + } + $args[$i] = $definition[$i+1][PHPTAGS_HOOK_DEFAULT_VALUE]; + $i++; + } + return call_user_func_array( 'static::' . $definition[PHPTAGS_HOOK_INVOKE][$i], $args ); + } +} \ No newline at end of file diff --git a/includes/ExceptionPhpTags.php b/includes/ExceptionPhpTags.php index 1a2e6c1..28a4983 100644 --- a/includes/ExceptionPhpTags.php +++ b/includes/ExceptionPhpTags.php @@ -7,14 +7,22 @@ define( 'PHPTAGS_EXCEPTION_NOTICE_UNINIT_STRING_OFFSET', 2002 ); // PHP Notice: Uninitialized string offset: $1 define( 'PHPTAGS_EXCEPTION_NOTICE_UNDEFINED_OFFSET', 2003 ); // PHP Notice: Undefined offset: 4 in Command line code on line 1 define( 'PHPTAGS_EXCEPTION_NOTICE_UNDEFINED_CONSTANT', 2004 ); // PHP Notice: Use of undefined constant $1 - assumed '$1' +define( 'PHPTAGS_EXCEPTION_NOTICE_OBJECT_CONVERTED', 2005 ); // PHP Notice: Object of class Exception could not be converted to int define( 'PHPTAGS_EXCEPTION_WARNING_DIVISION_BY_ZERO', 3001 ); // PHP Warning: Division by zero define( 'PHPTAGS_EXCEPTION_WARNING_SCALAR_VALUE_AS_ARRAY', 3002 ); // PHP Warning: Cannot use a scalar value as an array define( 'PHPTAGS_EXCEPTION_WARNING_INVALID_ARGUMENT_FOR_FOREACH', 3003 ); // PHP Warning: Invalid argument supplied for foreach() define( 'PHPTAGS_EXCEPTION_WARNING_RETURNED_INVALID_VALUE', 3004 ); +define( 'PHPTAGS_EXCEPTION_WARNING_INVALID_HOOK', 3005 ); +define( 'PHPTAGS_EXCEPTION_WARNING_EXPECTS_PARAMETER', 3006 ); // PHP Warning: func() expects parameter 1 to be array, integer given +define( 'PHPTAGS_EXCEPTION_WARNING_WRONG_PARAMETER_COUNT', 3007 ); // PHP Warning: Wrong parameter count for $1() define( 'PHPTAGS_EXCEPTION_FATAL_CANNOT_USE_FOR_READING', 4001 ); // PHP Fatal error: Cannot use [] for reading in Command line code on line 1 define( 'PHPTAGS_EXCEPTION_FATAL_STRING_OFFSET_AS_ARRAY', 4002 ); // PHP Fatal error: Cannot use string offset as an array +define( 'PHPTAGS_EXCEPTION_FATAL_VALUE_PASSED_BY_REFERENCE', 4003 ); // PHP Fatal error: Only variables can be passed by reference +define( 'PHPTAGS_EXCEPTION_FATAL_CALL_TO_UNDEFINED_FUNCTION', 4004 ); // PHP Fatal error: Call to undefined function $1() +define( 'PHPTAGS_EXCEPTION_FATAL_NONEXISTENT_HOOK_CLASS', 4005 ); +define( 'PHPTAGS_EXCEPTION_FATAL_INVALID_HOOK_CLASS', 4006 ); // pcre define( 'PHPTAGS_EXCEPTION_WARNING_WRONG_DELIMITER', 2009 ); // PHP Warning: preg_replace(): Delimiter must not be alphanumeric or backslash @@ -28,7 +36,6 @@ define( 'PHPTAGS_FATAL_UNABLE_CALL_TO_FUNCTION', 105 ); // $foxwayFunctions[$1][$2] is not callable define( 'PHPTAGS_FATAL_ERROR_CALL_TO_FUNCTION', 106 ); // Error in $foxwayFunctions[$1] define( 'PHPTAGS_WARNING_WRONG_PARAMETER_COUNT', 107 ); // PHP Warning: Wrong parameter count for $1() -define( 'PHPTAGS_FATAL_VALUE_PASSED_BY_REFERENCE', 108 ); // PHP Fatal error: Only variables can be passed by reference define( 'PHPTAGS_FATAL_CANNOT_UNSET_STRING_OFFSETS', 115 ); // PHP Fatal error: Cannot unset string offsets // PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 73 bytes) @@ -102,7 +109,28 @@ // @todo $message = "PHP Warning: constant, function or object '$params' returns an invalid value"; break; - + case PHPTAGS_EXCEPTION_FATAL_VALUE_PASSED_BY_REFERENCE: + $message = "PHP Fatal error: Only variables can be passed by reference"; + break; + case PHPTAGS_EXCEPTION_WARNING_EXPECTS_PARAMETER: + $message = "PHP Warning: {$params[0]} expects parameter {$params[1]} to be {$params[2]}, {$params[3]} given"; + break; + case PHPTAGS_EXCEPTION_NOTICE_OBJECT_CONVERTED: + $message = "PHP Notice: Object of class {$params[0]} could not be converted to {$params[1]}"; + break; + case PHPTAGS_EXCEPTION_WARNING_WRONG_PARAMETER_COUNT: + $message = "PHP Warning: Wrong parameter count for $params()"; + break; + case PHPTAGS_EXCEPTION_FATAL_CALL_TO_UNDEFINED_FUNCTION: + $message = "PHP Fatal error: Call to undefined function $params()"; + break; + case PHPTAGS_EXCEPTION_FATAL_NONEXISTENT_HOOK_CLASS: + $message = "PHP Fatal error: For the function {$params[0]} was registered nonexistent hook class {$params[1]}"; + break; + case PHPTAGS_EXCEPTION_FATAL_INVALID_HOOK_CLASS: + $message = "PHP Fatal error: For the function {$params[0]} was registered invalid hook class {$params[1]}"; + break; + // pcre case PHPTAGS_EXCEPTION_WARNING_WRONG_DELIMITER: $message = "PHP Warning: preg_replace(): Delimiter must not be alphanumeric or backslash"; @@ -115,18 +143,13 @@ break; - case PHPTAGS_FATAL_CALL_TO_UNDEFINED_FUNCTION: - $message = "PHP Fatal error: Call to undefined function $params()"; - break; case PHPTAGS_FATAL_UNABLE_CALL_TO_FUNCTION: $message = "PHP Fatal error: \$foxwayFunctions[{$params[0]}][{$params[1]}] is not callable"; break; case PHPTAGS_FATAL_ERROR_CALL_TO_FUNCTION: $message = "PHP Fatal error: Error at \$foxwayFunctions[$params]"; break; - case PHPTAGS_WARNING_WRONG_PARAMETER_COUNT: - $message = "PHP Warning: Wrong parameter count for $params()"; - break; + case PHPTAGS_FATAL_CANNOT_UNSET_STRING_OFFSETS: $message = 'PHP Fatal error: Cannot unset string offsets'; break; diff --git a/includes/Runtime.php b/includes/Runtime.php index 6b90976..0f27d9c 100644 --- a/includes/Runtime.php +++ b/includes/Runtime.php @@ -26,18 +26,19 @@ */ class Runtime { - static private $constantsValue = array(); - static private $constantsHook = array(); - static private $functionsHook = array(); - static private $objectsHook = array(); + private static $constantsValue = array(); + private static $constantsHook = array(); + private static $functionsHook = array(); + private static $objectsHook = array(); static public $time = 0; static public $permittedTime = true; protected static $startTime = array(); - protected static $variables = array(); - protected static $staticVariables = array(); - protected static $globalVariables = array(); + private static $variables = array(); + private static $staticVariables = array(); + private static $globalVariables = array(); + private static $tmpException = array(); /*public function startTime($scope) { self::$startTime[$scope] = microtime(true); @@ -365,89 +366,38 @@ $name = $value[PHPTAGS_STACK_PARAM]; if ( isset($value[PHPTAGS_STACK_PARAM_2]) ) { // This is function or object if ( is_array($value[PHPTAGS_STACK_PARAM_2]) ) { // This is function - if ( isset(self::$functionsHook[$name]) ) { - $function = &self::$functionsHook[$name]; - $param = array(); - foreach ( $value[PHPTAGS_STACK_PARAM_2] as $val ) { - if ( $val[PHPTAGS_STACK_COMMAND] == T_VARIABLE ) { // Example $foo - $ref = &$thisVariables[ $val[PHPTAGS_STACK_PARAM] ]; - if ( isset($val[PHPTAGS_STACK_ARRAY_INDEX]) ) { // Example: $foo[1] - foreach ( $val[PHPTAGS_STACK_ARRAY_INDEX] as $v ) { - if ( !isset($ref[ $v[PHPTAGS_STACK_RESULT] ]) ) { - $ref[ $v[PHPTAGS_STACK_RESULT] ] = null; - // @todo PHP Fatal error: Only variables can be passed by reference - if( is_string($ref) ) { - //PHPTAGS_EXCEPTION_NOTICE_UNINIT_STRING_OFFSET $return[] = (string) new ExceptionPhpTags( (int)$v[PHPTAGS_STACK_RESULT] , PHPTAGS_NOTICE_UNINIT_STRING_OFFSET, $value[PHPTAGS_STACK_TOKEN_LINE], $place ); - } - } - $ref = &$ref[ $v[PHPTAGS_STACK_RESULT] ]; - } - } - $param[] = &$ref; - } else { - // @todo PHP Fatal error: Only variables can be passed by reference - $param[] = $val[PHPTAGS_STACK_RESULT]; - } - } - $count = count( $param ); - do { - if( isset($function[$count]) ) { - $function = &$function[$count]; - break; - } else { - if ( isset($function[PHPTAGS_DEFAULT_VALUES]) ) { // Has default values - $param += $function[PHPTAGS_DEFAULT_VALUES]; - $count = count( $param ); - if( isset($function[$count]) ) { - $function = &$function[$count]; - break; - } - } - if ( isset($function[PHPTAGS_MIN_VALUES]) ) { - if( $count >= $function[PHPTAGS_MIN_VALUES] && isset($function['']) ) { - $function = &$function['']; - $count = "''"; // it for error message - break; - } - } - } - $return[] = (string) new ExceptionPhpTags( $name, PHPTAGS_WARNING_WRONG_PARAMETER_COUNT, $value[PHPTAGS_STACK_TOKEN_LINE], $place ); - $value[PHPTAGS_STACK_RESULT] = null; - break 2; /**** EXIT ****/ - } while( false ); + if ( !isset(self::$functionsHook[$name]) ) { + $return[] = new ExceptionPhpTags( PHPTAGS_EXCEPTION_FATAL_CALL_TO_UNDEFINED_FUNCTION, $name, $value[PHPTAGS_STACK_TOKEN_LINE], $place ); + return $return; + } + $hookClassName = self::$functionsHook[$name]; + if( !class_exists($hookClassName) ) { + $return[] = new ExceptionPhpTags( PHPTAGS_EXCEPTION_FATAL_NONEXISTENT_HOOK_CLASS, array($name, $hookClassName), $value[PHPTAGS_STACK_TOKEN_LINE], $place ); + return $return; + } + $classParens = class_parents( $hookClassName ); + if ( !isset($classParens['PhpTags\\BaseHooks']) ) { + $return[] = new ExceptionPhpTags( PHPTAGS_EXCEPTION_FATAL_INVALID_HOOK_CLASS, array($name, $hookClassName), $value[PHPTAGS_STACK_TOKEN_LINE], $place ); + return $return; + } - if ( is_callable($function) ) { - try { - wfSuppressWarnings(); - $result = $function( $param, $transit ); - if ( $result instanceof outPrint ) { - $value[PHPTAGS_STACK_RESULT] = $result->returnValue; - $return[] = $result; - } else { - $value[PHPTAGS_STACK_RESULT] = $result; - } - wfRestoreWarnings(); - } catch ( ExceptionPhpTags $e ) { - $e->tokenLine = $value[PHPTAGS_STACK_TOKEN_LINE]; - $e->place = $place; - if ( is_array($e->params) ) { - array_unshift( $e->params, $name ); - } - $return[] = $e; - $value[PHPTAGS_STACK_RESULT] = null; - break; /**** EXIT ****/ - } catch (Exception $e) { - $return[] = (string) new ExceptionPhpTags( $name, PHPTAGS_FATAL_ERROR_CALL_TO_FUNCTION, $value[PHPTAGS_STACK_TOKEN_LINE], $place ); - $value[PHPTAGS_STACK_RESULT] = null; - break; /**** EXIT ****/ - } - } else { - $return[] = (string) new ExceptionPhpTags( array($name, $count), PHPTAGS_FATAL_UNABLE_CALL_TO_FUNCTION, $value[PHPTAGS_STACK_TOKEN_LINE], $place ); - $value[PHPTAGS_STACK_RESULT] = null; - break; /**** EXIT ****/ + try { + wfSuppressWarnings(); + + $value[PHPTAGS_STACK_RESULT] = $hookClassName::onFunctionHook( $name, $value[PHPTAGS_STACK_PARAM_2] ); + + wfRestoreWarnings(); + } catch ( ExceptionPhpTags $e ) { + $e->tokenLine = $value[PHPTAGS_STACK_TOKEN_LINE]; + $e->place = $place; + if ( is_array($e->params) ) { + array_unshift( $e->params, $name ); } - } else { - $return[] = (string) new ExceptionPhpTags( $name, PHPTAGS_FATAL_CALL_TO_UNDEFINED_FUNCTION, $value[PHPTAGS_STACK_TOKEN_LINE], $place ); + $return[] = $e; + $value[PHPTAGS_STACK_RESULT] = null; + break; /**** EXIT ****/ + } catch (Exception $e) { + $return[] = (string) new ExceptionPhpTags( $name, PHPTAGS_FATAL_ERROR_CALL_TO_FUNCTION, $value[PHPTAGS_STACK_TOKEN_LINE], $place ); $value[PHPTAGS_STACK_RESULT] = null; break; /**** EXIT ****/ } @@ -736,5 +686,8 @@ public static function setObjectsHook( $className, array $objectsName ) { self::$objectsHook += array_fill_keys( $objectsName, $className ); } + public static function addException( ExceptionPhpTags $exception ) { + self::$tmpException[] = $exception; + } } diff --git a/tests/phpunit/includes/RuntimeTest.php b/tests/phpunit/includes/RuntimeTest.php index aff1d48..00e4698 100644 --- a/tests/phpunit/includes/RuntimeTest.php +++ b/tests/phpunit/includes/RuntimeTest.php @@ -2474,5 +2474,4 @@ ); } - } -- To view, visit https://gerrit.wikimedia.org/r/110343 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I3aa6723f9377a03732dfb262aa815a5c4caa9a42 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/PhpTags Gerrit-Branch: master Gerrit-Owner: Pastakhov <pastak...@yandex.ru> Gerrit-Reviewer: Pastakhov <pastak...@yandex.ru> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits