Pastakhov has uploaded a new change for review.
https://gerrit.wikimedia.org/r/93933
Change subject: add T_ISSET operator
......................................................................
add T_ISSET operator
* add constant FOXWAY_ALLOW_ONLY_VARIABLES for isset and unset operators
* fix T_EMPTY for tests echo_empty_array
Time: 422 ms, Memory: 25.25Mb
OK (448 tests, 454 assertions)
Change-Id: Ie27fe36386f92e514bc45379a9ada9efbd47f833
---
M includes/Compiler.php
M includes/Runtime.php
M tests/phpunit/includes/RuntimeTest.php
3 files changed, 209 insertions(+), 20 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Foxway
refs/changes/33/93933/1
diff --git a/includes/Compiler.php b/includes/Compiler.php
index 3568bf4..596bfea 100644
--- a/includes/Compiler.php
+++ b/includes/Compiler.php
@@ -25,6 +25,7 @@
define( 'FOXWAY_EXPECT_ARRAY_INDEX_CLOSE', 1 << 19 );
define( 'FOXWAY_EXPECT_EQUAL_END', 1 << 20 );
define( 'FOXWAY_EQUAL_HAVE_OPERATOR', 1 << 21 );
+define( 'FOXWAY_ALLOW_ONLY_VARIABLES', 1 << 22 );
define( 'FOXWAY_CLEAR_FLAG_FOR_SHIFT_BEFORE_PARENTHESES',
FOXWAY_EXPECT_PARENTHESES_WITH_LIST_PARAMS );
//define( 'FOXWAY_CLEAR_FLAG_FOR_SHIFT_AFTER_PARENTHESES',
FOXWAY_EXPECT_PARENTHESES_WITH_LIST_PARAMS );
@@ -169,7 +170,7 @@
case T_CONSTANT_ENCAPSED_STRING: // "foo" or
'bar'
case T_STRING: // true, false, null ...
case T_NUM_STRING: // echo "$foo[1]"; 1 is num
string
- if( $needOperator ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( $needOperator || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$needOperator = true;
$parentFlags &=
FOXWAY_CLEAR_FLAG_FOR_VALUE;
@@ -268,7 +269,7 @@
break;
}
// This is a closing double quote
- if( $needOperator ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( $needOperator || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$needOperator = true;
$tmp = array(
@@ -292,7 +293,7 @@
break;
case T_INC:
case T_DEC:
- if( $stackEncapsed !== false ||
$incrementOperator ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( $stackEncapsed !== false ||
$incrementOperator || $parentFlags & FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$precedence =
self::$precedencesMatrix[$id];
if( $needOperator ) { // $foo++
@@ -312,7 +313,7 @@
break;
case '+':
case '-':
- if( !$needOperator ) { // This is
negative statement of the next value: -$foo, -5, 5 + -5 ...
+ if( !$needOperator && ($parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES) == 0 ) { // This is negative statement of the next
value: -$foo, -5, 5 + -5 ...
if( $id == '-' ) { // ignore '+'
array_unshift(
$rightOperators, array(FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>null,
FOXWAY_STACK_PARAM=>0, FOXWAY_STACK_TOKEN_LINE=>$tokenLine) );
if(
isset($rightOperators[1]) ) { // this is not first right operator. Example:
echo (int)-
@@ -348,7 +349,7 @@
case T_IS_IDENTICAL: // ===
case T_IS_NOT_IDENTICAL: // !==
case '?': // Ternary
operator
- if( !$needOperator ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( !$needOperator || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
// break is not necessary here
case ':': // Ternary middle
if( $rightOperators ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
@@ -461,7 +462,7 @@
array_pop( $values ); // remove
T_VARIABLE from $values
// break is not necessary here
case T_DOUBLE_ARROW: // =>
- if( !$needOperator || !$lastValue ||
$rightOperators ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( !$needOperator || !$lastValue ||
$rightOperators || $parentFlags & FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$needOperator = false;
array_unshift( $needParams,
array(FOXWAY_STACK_COMMAND=>$id,
FOXWAY_STACK_RESULT=>&$lastValue[FOXWAY_STACK_RESULT],
FOXWAY_STACK_PARAM=>&$lastValue, FOXWAY_STACK_TOKEN_LINE=>$tokenLine) );
@@ -763,7 +764,7 @@
//$operator = false;
break;
case '(':
- if( $needOperator ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( $needOperator || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$parentheses[] = $parentFlags;
$parentFlags =
FOXWAY_EXPECT_PARENTHES_CLOSE | ($parentFlags &
FOXWAY_CLEAR_FLAG_FOR_SHIFT_BEFORE_PARENTHESES) >> 1;
@@ -839,7 +840,7 @@
self::getNextToken( $tokens, $index,
$countTokens, $tokenLine, array('(') );
break;
case T_ARRAY: // array
- if( $needOperator ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( $needOperator || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$parentheses[] = $parentFlags;
$parentFlags =
FOXWAY_EXPECT_PARENTHES_CLOSE|FOXWAY_ALLOW_COMMA_AT_END_PARENTHES|FOXWAY_EXPECT_LIST_PARAMS|FOXWAY_THIS_IS_FUNCTION|FOXWAY_ALLOW_DOUBLE_ARROW;
@@ -868,7 +869,7 @@
case T_STRING_CAST: // (string)
case T_BOOL_CAST: // (bool)
case T_UNSET_CAST: // (unset)
- if( $needOperator ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( $needOperator || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
array_unshift( $rightOperators,
array(FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>null,
FOXWAY_STACK_TOKEN_LINE=>$tokenLine) );
if( isset($rightOperators[1]) ) { //
this is not first right operator. Example: echo (int)-
@@ -1037,7 +1038,7 @@
$bytecode[][] = $tmp;
break;
case T_PRINT: // print
- if( $needOperator ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( $needOperator || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$parentheses[] = $parentFlags;
$parentFlags = FOXWAY_EXPECT_SEMICOLON
| FOXWAY_THIS_IS_FUNCTION;
@@ -1064,10 +1065,14 @@
case T_ISSET:
case T_UNSET:
case T_EMPTY:
- if( $needOperator ) { throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if( $needOperator || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$parentheses[] = $parentFlags;
- $parentFlags =
FOXWAY_EXPECT_PARENTHES_CLOSE|FOXWAY_EXPECT_LIST_PARAMS|FOXWAY_THIS_IS_FUNCTION;
+ if( $id == T_EMPTY ) {
+ $parentFlags =
FOXWAY_EXPECT_PARENTHES_CLOSE|FOXWAY_EXPECT_LIST_PARAMS|FOXWAY_THIS_IS_FUNCTION;
+ }else{
+ $parentFlags =
FOXWAY_EXPECT_PARENTHES_CLOSE|FOXWAY_EXPECT_LIST_PARAMS|FOXWAY_THIS_IS_FUNCTION|FOXWAY_ALLOW_ONLY_VARIABLES;
+ }
array_unshift( $needParams, array(
FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>null,
FOXWAY_STACK_PARAM=>array(), FOXWAY_STACK_TOKEN_LINE=>$tokenLine ) );
diff --git a/includes/Runtime.php b/includes/Runtime.php
index f035d57..5f73f50 100644
--- a/includes/Runtime.php
+++ b/includes/Runtime.php
@@ -957,6 +957,34 @@
}
}
break;
+ case T_ISSET:
+
foreach($value[FOXWAY_STACK_PARAM] as $val) {
+ if(
$val[FOXWAY_STACK_COMMAND] != T_VARIABLE ) { // Example: isset($foo);
+ throw new
Exception; // @todo
+ }
+ if(
!isset($thisVariables[ $val[FOXWAY_STACK_PARAM] ]) ) { // undefined variable
+
$value[FOXWAY_STACK_RESULT] = false;
+ break 2;
+ } // true, variable is
defined
+ if(
isset($val[FOXWAY_STACK_ARRAY_INDEX]) ) { // Example: isset($foo[1])
+ $ref =
&$thisVariables[ $val[FOXWAY_STACK_PARAM] ];
+ $vn =
array_pop( $val[FOXWAY_STACK_ARRAY_INDEX] );
+ foreach(
$val[FOXWAY_STACK_ARRAY_INDEX] as $v ) {
+ if(
!isset($ref[$v]) ) { // undefined array index
+
$value[FOXWAY_STACK_RESULT] = false;
+
break 3;
+ }
+ $ref =
&$ref[$v];
+ }
+ // @todo
->>>>>>>>>>>> | ************************************************************* |
<<<<< it only for compatible with PHP 5.4 if used PHP 5.3 @see
http://www.php.net/manual/en/function.isset.php Example #2 isset() on String
Offsets
+ if(
!isset($ref[$vn]) || (is_string($ref) && is_string($vn) && $vn !=
(string)(int)$vn) ) {
+
$value[FOXWAY_STACK_RESULT] = false;
+ break 2;
+ }
+ } // true, variable is
defined and have no array index
+ }
+ $value[FOXWAY_STACK_RESULT] =
true;
+ break;
case T_EMPTY:
foreach($value[FOXWAY_STACK_PARAM] as $val) {
if(
$val[FOXWAY_STACK_COMMAND] == T_VARIABLE ) { // Example: empty($foo);
@@ -967,7 +995,7 @@
}else{
$ref =
&$val[FOXWAY_STACK_RESULT];
}
- if(
isset($val[FOXWAY_STACK_ARRAY_INDEX]) ) { // Example: $foo[1]
+ if(
isset($val[FOXWAY_STACK_ARRAY_INDEX]) ) { // Example: empty($foo[1])
$vn =
array_pop( $val[FOXWAY_STACK_ARRAY_INDEX] );
foreach(
$val[FOXWAY_STACK_ARRAY_INDEX] as $v ) {
if(
!isset($ref[$v]) ) { // undefined array index
@@ -975,16 +1003,12 @@
}
$ref =
&$ref[$v];
}
- if(
is_string($ref) ) { // @todo it only for compatible with PHP 5.4 on PHP 5.3
@see http://www.php.net/manual/en/function.empty.php Example #2 empty() on
String Offsets
- if(
(is_string($vn) && $vn == (string)(int)$vn && !empty($ref[$vn]) ||
(!is_string($vn) && !empty($ref[$vn]))) ) {
-
$value[FOXWAY_STACK_RESULT] = false;
-
break 2;
- }//
index is string
- }elseif(
!empty($ref[$vn]) ) {
+ // @todo
->>>>>>>>>>>> | ************************************************************* |
<<<<< it only for compatible with PHP 5.4 if used PHP 5.3 @see
http://www.php.net/manual/en/function.empty.php Example #2 empty() on String
Offsets
+ if(
!empty($ref[$vn]) && (is_array($ref) || !is_string($vn) || $vn ==
(string)(int)$vn) ) {
$value[FOXWAY_STACK_RESULT] = false;
break 2;
}
- }elseif( !empty($ref) )
{ // there is no array index and empty() returns false
+ }elseif( !empty($ref) )
{ // there is no array index and empty() returns false (PHP 5.5.0 supports
expressions)
$value[FOXWAY_STACK_RESULT] = false;
break 2;
}
diff --git a/tests/phpunit/includes/RuntimeTest.php
b/tests/phpunit/includes/RuntimeTest.php
index 8e9e91c..5d02676 100644
--- a/tests/phpunit/includes/RuntimeTest.php
+++ b/tests/phpunit/includes/RuntimeTest.php
@@ -2778,6 +2778,49 @@
array('empty')
);
}
+ public function testRun_echo_empty_array_1() {
+ $this->assertEquals(
+ Runtime::runSource('$a = array ("test" => 1,
"hello" => NULL, "pie" => array("a" => "apple"));
+echo empty($a["test"]) ? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_empty_array_2() {
+ $this->assertEquals(
+ Runtime::runSource('echo empty($a["foo"]) ?
"true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_empty_array_3() {
+ $this->assertEquals(
+ Runtime::runSource('echo empty($a["hello"]) ?
"true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_empty_array_4() {
+ $this->assertEquals(
+ Runtime::runSource('echo empty($a["pie"]["a"])
? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_empty_array_5() {
+ $this->assertEquals(
+ Runtime::runSource('echo empty($a["pie"]["b"])
? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_empty_array_6() {
+ $this->assertEquals(
+ Runtime::runSource('echo
empty($a["pie"]["a"]["b"]) ? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_empty_array_7() {
+ $this->assertEquals(
+ Runtime::runSource('echo
empty($a["pie"]["b"]["a"]) ? "true" : "false";'),
+ array('true')
+ );
+ }
public function testRun_echo_empty_key_string_1() {
$this->assertEquals(
Runtime::runSource('$expected_array_got_string
= "somestring";
@@ -2815,4 +2858,121 @@
array('true')
);
}
+
+ public function testRun_echo_isset_1() {
+ $this->assertEquals(
+ Runtime::runSource('$var = ""; echo isset($var)
? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_isset_2() {
+ $this->assertEquals(
+ Runtime::runSource('echo isset($varForIsset) ?
"true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_3() {
+ $this->assertEquals(
+ Runtime::runSource('echo isset($var,
$varForIsset) ? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_4() {
+ $this->assertEquals(
+ Runtime::runSource('echo isset($varForIsset,
$var) ? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_5() {
+ $this->assertEquals(
+ Runtime::runSource('$varForIsset = "test"; echo
isset($varForIsset, $var) ? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_isset_6() {
+ $this->assertEquals(
+ Runtime::runSource('$varForIsset = NULL; echo
isset($varForIsset, $var) ? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_array_1() {
+ $this->assertEquals(
+ Runtime::runSource('$a = array ("test" => 1,
"hello" => NULL, "pie" => array("a" => "apple"));
+echo isset($a["test"]) ? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_isset_array_2() {
+ $this->assertEquals(
+ Runtime::runSource('echo isset($a["foo"]) ?
"true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_array_3() {
+ $this->assertEquals(
+ Runtime::runSource('echo isset($a["hello"]) ?
"true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_array_4() {
+ $this->assertEquals(
+ Runtime::runSource('echo isset($a["pie"]["a"])
? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_isset_array_5() {
+ $this->assertEquals(
+ Runtime::runSource('echo isset($a["pie"]["b"])
? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_array_6() {
+ $this->assertEquals(
+ Runtime::runSource('echo
isset($a["pie"]["a"]["b"]) ? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_array_7() {
+ $this->assertEquals(
+ Runtime::runSource('echo
isset($a["pie"]["b"]["a"]) ? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_key_string_1() {
+ $this->assertEquals(
+ Runtime::runSource('$expected_array_got_string
= "somestring";
+echo isset($expected_array_got_string[0]) ? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_isset_key_string_2() {
+ $this->assertEquals(
+ Runtime::runSource('echo
isset($expected_array_got_string["0"]) ? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_isset_key_string_3() {
+ $this->assertEquals(
+ Runtime::runSource('echo
isset($expected_array_got_string[0.5]) ? "true" : "false";'),
+ array('true')
+ );
+ }
+ public function testRun_echo_isset_key_string_4() { //PHP 5.4 changes
how isset() behaves when passed string offsets.
+ $this->assertEquals(
+ Runtime::runSource('echo
isset($expected_array_got_string["some_key"]) ? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_key_string_5() { //PHP 5.4 changes
how isset() behaves when passed string offsets.
+ $this->assertEquals(
+ Runtime::runSource('echo
isset($expected_array_got_string["0.5"]) ? "true" : "false";'),
+ array('false')
+ );
+ }
+ public function testRun_echo_isset_key_string_6() { //PHP 5.4 changes
how isset() behaves when passed string offsets.
+ $this->assertEquals(
+ Runtime::runSource('echo
isset($expected_array_got_string["0 Mostel"]) ? "true" : "false";'),
+ array('false')
+ );
+ }
}
--
To view, visit https://gerrit.wikimedia.org/r/93933
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie27fe36386f92e514bc45379a9ada9efbd47f833
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Foxway
Gerrit-Branch: develop
Gerrit-Owner: Pastakhov <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits