Pastakhov has uploaded a new change for review.
https://gerrit.wikimedia.org/r/94874
Change subject: add operator T_FOREACH
......................................................................
add operator T_FOREACH
* show exception foxway as string
* fix double variables '$bar' for 'echo "$foo[$bar]";'
Time: 485 ms, Memory: 24.75Mb
OK (487 tests, 493 assertions)
Change-Id: Id917d91f56f0daef19fa0ee716feba284c35620c
---
M Foxway.body.php
M includes/Compiler.php
M includes/ExceptionFoxway.php
M includes/Runtime.php
M tests/phpunit/includes/RuntimeTest.php
5 files changed, 169 insertions(+), 24 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Foxway
refs/changes/74/94874/1
diff --git a/Foxway.body.php b/Foxway.body.php
index bfcfcb5..4ffbcff 100644
--- a/Foxway.body.php
+++ b/Foxway.body.php
@@ -51,6 +51,8 @@
array( 'Parser'=>&$parser,
'PPFrame'=>&$frame )
);
$return = implode($result);
+ } catch (\Foxway\ExceptionFoxway $exc) {
+ $return = (string) $exc;
} catch (Exception $exc) {
$return = $exc->getTraceAsString();
}
@@ -85,7 +87,10 @@
self::getScope($frame),
array( 'Parser'=>&$parser,
'PPFrame'=>&$frame )
);
- } catch (Exception $exc) {
+ } catch ( \Foxway\ExceptionFoxway $exc ) {
+ \Foxway\Runtime::$time += microtime(true) -
self::$startTime;
+ return (string) $exc;
+ } catch ( Exception $exc ) {
\Foxway\Runtime::$time += microtime(true) -
self::$startTime;
return $exc->getTraceAsString();
}
diff --git a/includes/Compiler.php b/includes/Compiler.php
index 14fa2cf..bc0cc55 100644
--- a/includes/Compiler.php
+++ b/includes/Compiler.php
@@ -28,6 +28,7 @@
define( 'FOXWAY_ALLOW_ONLY_VARIABLES', 1 << 22 );
define( 'FOXWAY_ALLOW_SKIP_PARAMS', 1 << 23 ); // used in operator T_LIST
define( 'FOXWAY_DOUBLE_ARROW_WAS_USED', 1 << 24 );
+define( 'FOXWAY_EXPECT_OPERATOR_AS', 1 << 25 ); // for operator T_FOREACH
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 );
@@ -287,6 +288,7 @@
}else{ // right operator was not used
$lastValue = &$tmp;
}
+ $values = array(); // there is
variables of array index. Example '$bar' for : echo "$foo[$bar]";
unset($tmp);
$stackEncapsed = false;
break;
@@ -462,14 +464,21 @@
case T_XOR_EQUAL: // ^=
case T_SL_EQUAL: // <<=
case T_SR_EQUAL: // >>=
+ if ( $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ // break is not necessary here
case T_DOUBLE_ARROW: // =>
- if ( !$needOperator ||
!isset($lastValue) || $rightOperators || $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if ( !$needOperator ||
!isset($lastValue) || $rightOperators ) { 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) );
if ( $id == T_DOUBLE_ARROW ) {
- if( $parentFlags &
FOXWAY_ALLOW_DOUBLE_ARROW == 0 ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+ if ( $parentFlags &
FOXWAY_ALLOW_DOUBLE_ARROW == 0 ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
$parentFlags = ( $parentFlags &
~FOXWAY_ALLOW_DOUBLE_ARROW ) | FOXWAY_DOUBLE_ARROW_WAS_USED; // Mark double
arrow was used
+ if ( $parentFlags &
FOXWAY_ALLOW_ONLY_VARIABLES ) { // T_DOUBLE_ARROW for operator T_FOREACH
+ array_shift(
$needParams );
+ unset( $lastValue );
+ break; // **** EXIT ****
+ }
} elseif ( $id == '=' ) {
if (
$lastValue[FOXWAY_STACK_COMMAND] == T_VARIABLE ) {
array_pop( $values );
// remove T_VARIABLE from $values
@@ -521,7 +530,19 @@
if( !isset($operator) ) { $needOperator
= true; } // Example: $foo[]
// break is not necessary here
case ')':
- if( !$needOperator && !isset($operator)
&& $parentFlags & FOXWAY_THIS_IS_FUNCTION ) { $needOperator = true; }
+ if ( !$needOperator &&
!isset($operator) && $parentFlags & FOXWAY_THIS_IS_FUNCTION ) { $needOperator =
true; } // @todo ALLOW_VOID_PARAMS
+ if ( $parentFlags &
FOXWAY_EXPECT_PARENTHES_CLOSE && $parentFlags & FOXWAY_THIS_IS_FUNCTION &&
$needParams[0][FOXWAY_STACK_COMMAND] == T_FOREACH ) {
+ if ( $parentFlags &
FOXWAY_DOUBLE_ARROW_WAS_USED ) { // T_DOUBLE_ARROW Example: while ( $foo as
$key=>$value )
+
$needParams[0][FOXWAY_STACK_DO_TRUE][0][FOXWAY_STACK_PARAM_2] = array(
$values[0][FOXWAY_STACK_PARAM], $lastValue[FOXWAY_STACK_PARAM] );
+ } else { // T_VARIABLE.
Example: while ( $foo as $value )
+
$needParams[0][FOXWAY_STACK_DO_TRUE][0][FOXWAY_STACK_PARAM_2] =
$lastValue[FOXWAY_STACK_PARAM];
+ }
+ $values = array();
+ unset( $lastValue );
+ $parentFlags =
array_pop($parentheses);
+ $needOperator = false;
+ break; // **** EXIT ****
+ }
// break is not necessary here
case ',':
if ( !$needOperator &&
!isset($lastValue) && $parentFlags & FOXWAY_ALLOW_SKIP_PARAMS ) {
@@ -554,13 +575,13 @@
unset(
$incompleteOperators[$parentLevel], $math[$parentLevel] );
}
}elseif( isset($lastValue) ) { //
Operator does not exists, but there is value. Operator and value not exists
for: array() or array(1,)
- if( isset($lastValue) ) {
+ //if( isset($lastValue) ) {
$operator = &$lastValue;
unset($lastValue);
- }else{ // Value does not exist.
Examples: echo () OR echo , OR echo ;
+ //}else{ // Value does not
exist. Examples: echo () OR echo , OR echo ;
// @todo ternary
without middle
- throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
- }
+ // throw new
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
+ //}
}
if( $parentFlags &
FOXWAY_EXPECT_TERNARY_END ) { // Examples: echo 1?2:3,
@@ -602,7 +623,7 @@
$lastValue =
&$needParams[0];
array_shift($needParams);
$lastValue[FOXWAY_STACK_ARRAY_INDEX][] = &$operator[FOXWAY_STACK_RESULT]; //
$lastValue must be T_VARIABLE only
- $stack[] = &$lastValue;
+ $values[] = &$lastValue;
unset($operator);
if( $parentFlags &
FOXWAY_NEED_RESTORE_OPERATOR ) {
$operator =
array_pop( $memOperators );
@@ -612,22 +633,23 @@
list( $tmp, $math ) =
array_pop($memory); // restore $stack, $math
$s =
self::mergeStackAndMath($tmp, $math);
if( $s ) {
- $stack =
array_merge( $s, $stack );
+ $stack =
array_merge( $stack, $s );
}
break 2;
case ')':
$parentLevel--;
if( $parentFlags &
FOXWAY_EXPECT_PARENTHES_CLOSE == 0 ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
- if( $parentFlags &
FOXWAY_THIS_IS_FUNCTION ) {
- if(
isset($operator) ) {
- if(
$parentFlags & FOXWAY_EXPECT_LIST_PARAMS ) {
+ if ( $parentFlags &
FOXWAY_THIS_IS_FUNCTION ) {
+ if (
isset($operator) ) {
+ //if(
$parentFlags & FOXWAY_EXPECT_LIST_PARAMS ) {
$needParams[0][FOXWAY_STACK_PARAM][] = &$operator;
-
if( $parentFlags & FOXWAY_EXPECT_VARIABLE_REFERENCE &&
$operator[FOXWAY_STACK_COMMAND] == T_VARIABLE ) {
-
array_pop( $stack ); // @todo refactoring
-
}
- }else{
-
throw new ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
- }
+
if ( $parentFlags & FOXWAY_EXPECT_VARIABLE_REFERENCE &&
$operator[FOXWAY_STACK_COMMAND] == T_VARIABLE ) { // @todo refactoring
+
$stack = array(); // @todo refactoring
+
//array_pop( $stack ); // @todo refactoring
+
} // @todo refactoring
+ //}else{
+ //
throw new ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
+ //}
unset($operator);
}
$operator =
&$needParams[0]; // restore result of function as value, this will be set as
$lastValue
@@ -635,7 +657,7 @@
$stack[] = &$operator; // add function to stack
}
array_shift($needParams);
- list( $s, $math
) = array_pop($memory); // restore $stack, $math
+ list( $s, $math
) = array_pop($memory); // restore $stack, $math @todo NEED_RESTORE_STACK
if( $s ) {
$stack
= array_merge( $s, $stack );
}
@@ -744,7 +766,7 @@
break; /********** EXIT **********/
}else{
// if(1) echo 2;
$link = &$needParams[0];
-
if( $link[FOXWAY_STACK_COMMAND] == T_WHILE ) { // T_WHILE
+
if( $link[FOXWAY_STACK_COMMAND] != T_IF ) { // T_WHILE, T_FOREACH
$stack[] = array( FOXWAY_STACK_COMMAND=>T_CONTINUE,
FOXWAY_STACK_RESULT=>1 ); // Add operator T_CONTINUE to the end of the cycle
$link[FOXWAY_STACK_DO_TRUE] = array_merge( $link[FOXWAY_STACK_DO_TRUE],
$stack );
$stack = array( &$link );
@@ -805,7 +827,7 @@
if( $parentFlags &
FOXWAY_EXPECT_START_COMMAND == 0 ) { throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
// for compatible with functions that
use flag FOXWAY_EXPECT_LIST_PARAMS
- $memory[] = array(array(), array());
+ $memory[] = array( array(), array() );
// @todo NEED_RESTORE_STACK
/*ksort($math);
array_unshift( $memory, array($stack,
$math) );
$stack = array();
@@ -963,7 +985,7 @@
array_pop($parentheses);
$parentFlags = array_pop($parentheses);
if(
!isset($link[FOXWAY_STACK_DO_FALSE]) ) { // operator 'else' not used
- if( $link[FOXWAY_STACK_COMMAND]
== T_WHILE ) {
+ if( $link[FOXWAY_STACK_COMMAND]
!= T_IF ) { // T_WHILE, T_FOREACH
$link[FOXWAY_STACK_DO_TRUE][] = array( FOXWAY_STACK_COMMAND=>T_CONTINUE,
FOXWAY_STACK_RESULT=>1 ); // Add operator T_CONTINUE to the end of the cycle
$tmp = array( &$link );
$ifOperators = array();
@@ -1110,6 +1132,33 @@
self::getNextToken( $tokens, $index,
$countTokens, $tokenLine, array('(') );
break;
+ case T_FOREACH:
+ if ( $parentFlags &
FOXWAY_EXPECT_START_COMMAND == 0 || $stack || isset($operator) || $values ) {
+ throw new ExceptionFoxway( $id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine );
+ }
+
+ array_unshift( $needParams,
array(FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_DO_TRUE=>array(),
FOXWAY_STACK_TOKEN_LINE=>$tokenLine) );
+ //$parentheses[] = $parentFlags;
+ //$parentheses[] =
FOXWAY_EXPECT_START_COMMAND | FOXWAY_EXPECT_SEMICOLON |
FOXWAY_EXPECT_DO_TRUE_STACK;
+ $parentheses[] = $parentFlags;
+ $parentheses[] =
FOXWAY_EXPECT_START_COMMAND | FOXWAY_EXPECT_SEMICOLON |
FOXWAY_EXPECT_DO_TRUE_STACK;
+ $parentFlags =
FOXWAY_ALLOW_ONLY_VARIABLES|FOXWAY_EXPECT_OPERATOR_AS;
+
+ //$parentLevel++;
+ self::getNextToken( $tokens, $index,
$countTokens, $tokenLine, array('(') );
+ break;
+ case T_AS:
+ if ( !$needOperator || !($parentFlags &
FOXWAY_EXPECT_OPERATOR_AS) || !isset($lastValue) ||
$lastValue[FOXWAY_STACK_COMMAND] != T_VARIABLE ) {
+ throw new ExceptionFoxway( $id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine );
+ }
+ $needOperator = false;
+ $parentFlags =
FOXWAY_ALLOW_ONLY_VARIABLES|FOXWAY_EXPECT_VARIABLE_REFERENCE|FOXWAY_EXPECT_PARENTHES_CLOSE|FOXWAY_THIS_IS_FUNCTION|FOXWAY_ALLOW_DOUBLE_ARROW;
+
+ $needParams[0][FOXWAY_STACK_PARAM] =
$lastValue[FOXWAY_STACK_PARAM]; // for reset array
+ $needParams[0][FOXWAY_STACK_DO_TRUE][0]
= array( FOXWAY_STACK_COMMAND=>$id,
FOXWAY_STACK_PARAM=>$lastValue[FOXWAY_STACK_PARAM],
FOXWAY_STACK_TOKEN_LINE=>$tokenLine);
+ unset( $lastValue );
+ $values = array();
+ break;
default :
throw new ExceptionFoxway($id,
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
break;
diff --git a/includes/ExceptionFoxway.php b/includes/ExceptionFoxway.php
index 1960613..980a733 100644
--- a/includes/ExceptionFoxway.php
+++ b/includes/ExceptionFoxway.php
@@ -17,6 +17,11 @@
define( 'FOXWAY_PHP_NOTICE_UNINIT_STRING_OFFSET', 113 ); // PHP Notice:
Uninitialized string offset: $1
define( 'FOXWAY_PHP_NOTICE_UNDEFINED_CONSTANT', 114 ); // PHP Notice: Use of
undefined constant $1 - assumed '$1'
define( 'FOXWAY_PHP_FATAL_CANNOT_UNSET_STRING_OFFSETS', 115 ); // PHP Fatal
error: Cannot unset string offsets
+define( 'FOXWAY_PHP_WARNING_INVALID_ARGUMENT_FOR_FOREACH', 116 ); // PHP
Warning: Invalid argument supplied for foreach()
+// PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried
to allocate 73 bytes)
+// PHP Fatal error: Maximum execution time of 30 seconds exceeded
+// PHP Warning: Variable passed to each() is not an array or object
+
/**
* Error Exception class of Foxway extension.
@@ -44,6 +49,9 @@
$place = $this->place;
switch ( $this->code ) {
+ case FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED:
+ $message = 'HP Parse error: syntax error,
unexpected ' . ( is_string($params) ? $params : token_name($params) );
+ break;
case FOXWAY_PHP_WARNING_DIVISION_BY_ZERO:
$message = "PHP Warning: Division by zero";
break;
@@ -69,9 +77,13 @@
$message = "PHP Notice: Use of undefined
constant $params - assumed '$params'";
break;
case FOXWAY_PHP_FATAL_CANNOT_UNSET_STRING_OFFSETS:
- $message = "PHP Fatal error: Cannot unset
string offsets";
+ $message = 'PHP Fatal error: Cannot unset
string offsets';
+ break;
+ case FOXWAY_PHP_WARNING_INVALID_ARGUMENT_FOR_FOREACH:
+ $message = 'PHP Warning: Invalid argument
supplied for foreach()';
break;
default:
+ $message = "PHP Fatal error: Undefined error,
code {$this->code}";
break;
}
//return "$message in $place on line $line\n";
diff --git a/includes/Runtime.php b/includes/Runtime.php
index 9d8c9ae..36f99ca 100644
--- a/includes/Runtime.php
+++ b/includes/Runtime.php
@@ -255,6 +255,19 @@
}
}
break;
+ case T_FOREACH:
+ $vn =
$value[FOXWAY_STACK_PARAM]; // Variable name
+ if ( !array_key_exists($vn,
$thisVariables) ) {
+ $return[] = (string)
new ExceptionFoxway( $vn, FOXWAY_PHP_NOTICE_UNDEFINED_VARIABLE,
$value[FOXWAY_STACK_TOKEN_LINE], $place );
+ $return[] = (string)
new ExceptionFoxway( null, FOXWAY_PHP_WARNING_INVALID_ARGUMENT_FOR_FOREACH,
$value[FOXWAY_STACK_TOKEN_LINE], $place );
+ break; // **** EXIT ****
+ }
+ if ( !is_array($thisVariables[
$value[FOXWAY_STACK_PARAM] ]) ) {
+ $return[] = (string)
new ExceptionFoxway( null, FOXWAY_PHP_WARNING_INVALID_ARGUMENT_FOR_FOREACH,
$value[FOXWAY_STACK_TOKEN_LINE], $place );
+ break; // **** EXIT ****
+ }
+ reset( $thisVariables[
$value[FOXWAY_STACK_PARAM] ] );
+ // break is not necessary here
case T_WHILE: // PHP code "while($foo)
{ ... }" doing as T_WHILE { T_DO($foo) ... }. If $foo == false, T_DO doing as
T_BREAK
$memory[] = array( null, $code,
$i, $c, $loopsOwner );
$code =
$value[FOXWAY_STACK_DO_TRUE];
@@ -267,6 +280,17 @@
continue; // this is
"while(true)", just go next
}// while(false) doing as
T_BREAK;
break 2; // go to one level down
+ case T_AS:
+ if (
is_string($value[FOXWAY_STACK_PARAM_2]) ) { // T_VARIABLE. Example: while (
$foo as $value )
+ if (
!list(,$thisVariables[ $value[FOXWAY_STACK_PARAM_2] ]) = each($thisVariables[
$value[FOXWAY_STACK_PARAM] ]) ) {
+ break 2; // go
to one level down
+ }
+ } else { // T_DOUBLE_ARROW
Example: while ( $foo as $key=>$value )
+ if (
!list($thisVariables[ $value[FOXWAY_STACK_PARAM_2][0] ], $thisVariables[
$value[FOXWAY_STACK_PARAM_2][1] ]) = each($thisVariables[
$value[FOXWAY_STACK_PARAM] ]) ) {
+ break 2; // go
to one level down
+ }
+ }
+ break;
case T_BREAK:
$break =
$value[FOXWAY_STACK_RESULT];
if( $loopsOwner == T_WHILE ) {
diff --git a/tests/phpunit/includes/RuntimeTest.php
b/tests/phpunit/includes/RuntimeTest.php
index 43a1ebb..154e8c5 100644
--- a/tests/phpunit/includes/RuntimeTest.php
+++ b/tests/phpunit/includes/RuntimeTest.php
@@ -3034,4 +3034,59 @@
);
}
+ public function testRun_foreach_1() {
+ $this->assertEquals(
+ Runtime::runSource('$arr = array("one", "two",
"three"); foreach ($arr as $value) echo "* Value: $value\n";'),
+ array("* Value: one\n", "* Value: two\n", "*
Value: three\n")
+ );
+ }
+ public function testRun_foreach_2() {
+ $this->assertEquals(
+ Runtime::runSource('foreach ($arr as $value) {
echo "* Value: $value\n"; }'),
+ array("* Value: one\n", "* Value: two\n", "*
Value: three\n")
+ );
+ }
+ public function testRun_foreach_3() {
+ $this->assertEquals(
+ Runtime::runSource('$arr = array("one", "two",
"three"); foreach ($arr as $value) echo "* Value: $value\n"; echo "end";'),
+ array("* Value: one\n", "* Value: two\n", "*
Value: three\n", 'end')
+ );
+ }
+ public function testRun_foreach_4() {
+ $this->assertEquals(
+ Runtime::runSource('foreach ($arr as $value) {
echo "* Value: $value\n"; } echo "end";'),
+ array("* Value: one\n", "* Value: two\n", "*
Value: three\n", 'end')
+ );
+ }
+ public function testRun_foreach_5() {
+ $this->assertEquals(
+ Runtime::runSource('foreach ($arr as $key =>
$value) { echo $key, $value; }'),
+ array(0, 'one', 1, 'two', 2, 'three')
+ );
+ }
+ public function testRun_foreach_6() {
+ $this->assertEquals(
+ Runtime::runSource('foreach ($arr as $key =>
$value) echo "* Key: $key; Value: $value\n"; echo "end";'),
+ array("* Key: 0; Value: one\n", "* Key: 1;
Value: two\n", "* Key: 2; Value: three\n", 'end')
+ );
+ }
+ public function testRun_foreach_7() {
+ $this->assertEquals(
+ Runtime::runSource('foreach ($arr as $key =>
$value) { echo "* Key: $key; Value: $value\n"; } echo "end";'),
+ array("* Key: 0; Value: one\n", "* Key: 1;
Value: two\n", "* Key: 2; Value: three\n", 'end')
+ );
+ }
+ public function testRun_foreach_8() {
+ $this->assertEquals(
+ Runtime::runSource('$a = array("one" => 1,"two"
=> 2,"three" => 3,"seventeen" => 17); foreach ($a as $k => $v) {echo "\$a[$k]
=> $v.";}'),
+ array('$a[one] => 1.', '$a[two] => 2.',
'$a[three] => 3.', '$a[seventeen] => 17.')
+ );
+ }
+ public function testRun_foreach_9() {
+ $this->assertEquals(
+ Runtime::runSource('$a=array(); $a[0][0]="a";
$a[0][1]="b"; $a[1][0]="y"; $a[1][1]="z"; foreach ($a as $v1) { foreach ($v1 as
$v2) { echo $v2; } }'),
+ array('a', 'b', 'y', 'z')
+ );
+ }
+
}
--
To view, visit https://gerrit.wikimedia.org/r/94874
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Id917d91f56f0daef19fa0ee716feba284c35620c
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