Add support for variadic parameters (PHP 5.6)

This commit is contained in:
nikic 2014-03-26 18:23:30 +01:00
parent f5be0d30f7
commit 96f1151ab2
14 changed files with 1550 additions and 1343 deletions

View File

@ -106,6 +106,7 @@
%token T_NS_C
%token T_DIR
%token T_NS_SEPARATOR
%token T_ELLIPSIS
%%
@ -235,6 +236,11 @@ optional_ref:
| '&' { $$ = true; }
;
optional_ellipsis:
/* empty */ { $$ = false; }
| T_ELLIPSIS { $$ = true; }
;
function_declaration_statement:
T_FUNCTION optional_ref T_STRING '(' parameter_list ')' '{' inner_statement_list '}'
{ $$ = Stmt\Function_[$3, [byRef: $2, params: $5, stmts: $8]]; }
@ -371,10 +377,10 @@ non_empty_parameter_list:
;
parameter:
optional_class_type optional_ref T_VARIABLE
{ $$ = Param[parseVar($3), null, $1, $2]; }
| optional_class_type optional_ref T_VARIABLE '=' static_scalar
{ $$ = Param[parseVar($3), $5, $1, $2]; }
optional_class_type optional_ref optional_ellipsis T_VARIABLE
{ $$ = Param[parseVar($4), null, $1, $2, $3]; }
| optional_class_type optional_ref optional_ellipsis T_VARIABLE '=' static_scalar
{ $$ = Param[parseVar($4), $6, $1, $2, $3]; }
;
optional_class_type:

View File

@ -12,6 +12,8 @@ class Emulative extends \PhpParser\Lexer
protected $newKeywords;
protected $inObjectAccess;
const T_ELLIPSIS = 1001;
public function __construct() {
parent::__construct();
@ -36,13 +38,17 @@ class Emulative extends \PhpParser\Lexer
$this->newKeywords += $newKeywords;
}
if (version_compare(PHP_VERSION, '5.6.0beta1', '<')) {
$this->tokenMap[self::T_ELLIPSIS] = Parser::T_ELLIPSIS;
}
}
public function startLexing($code) {
$this->inObjectAccess = false;
// on PHP 5.4 don't do anything
if (version_compare(PHP_VERSION, '5.4.0RC1', '>=')) {
// on PHP 5.6 don't do anything
if (version_compare(PHP_VERSION, '5.6.0beta1', '>=')) {
parent::startLexing($code);
} else {
$code = $this->preprocessCode($code);
@ -60,8 +66,14 @@ class Emulative extends \PhpParser\Lexer
* inside a string, i.e. a place where they don't have a special meaning).
*/
protected function preprocessCode($code) {
// binary notation (0b010101101001...)
return preg_replace('(\b0b[01]+\b)', '~__EMU__BINARY__$0__~', $code);
$code = str_replace('...', '~__EMU__ELLIPSIS__~', $code);
if (version_compare(PHP_VERSION, '5.4.0beta1', '<')) {
// binary notation (0b010101101001...)
$code = preg_replace('(\b0b[01]+\b)', '~__EMU__BINARY__$0__~', $code);
}
return $code;
}
/*
@ -86,6 +98,10 @@ class Emulative extends \PhpParser\Lexer
$replace = array(
array(is_int(bindec($matches[2])) ? T_LNUMBER : T_DNUMBER, $matches[2], $this->tokens[$i + 1][2])
);
} else if ('ELLIPSIS' === $matches[1]) {
$replace = array(
array(self::T_ELLIPSIS, '...', $this->tokens[$i + 1][2])
);
} else {
// just ignore all other __EMU__ sequences
continue;
@ -114,6 +130,8 @@ class Emulative extends \PhpParser\Lexer
public function restoreContentCallback(array $matches) {
if ('BINARY' === $matches[1]) {
return $matches[2];
} else if ('ELLIPSIS' === $matches[1]) {
return '...';
} else {
return $matches[0];
}

View File

@ -2,13 +2,15 @@
namespace PhpParser\Node;
use PhpParser\Error;
use PhpParser\NodeAbstract;
/**
* @property string $name Name
* @property null|Expr $default Default value
* @property null|string|Name $type Typehint
* @property bool $byRef Whether is passed by reference
* @property null|string|Name $type Typehint
* @property bool $byRef Whether is passed by reference
* @property bool $variadic Whether this is a variadic argument
* @property string $name Name
* @property null|Expr $default Default value
*/
class Param extends NodeAbstract
{
@ -19,17 +21,23 @@ class Param extends NodeAbstract
* @param null|Expr $default Default value
* @param null|string|Name $type Typehint
* @param bool $byRef Whether is passed by reference
* @param bool $variadic Whether this is a variadic argument
* @param array $attributes Additional attributes
*/
public function __construct($name, $default = null, $type = null, $byRef = false, array $attributes = array()) {
public function __construct($name, $default = null, $type = null, $byRef = false, $variadic = false, array $attributes = array()) {
parent::__construct(
array(
'name' => $name,
'default' => $default,
'type' => $type,
'byRef' => $byRef
'type' => $type,
'byRef' => $byRef,
'variadic' => $variadic,
'name' => $name,
'default' => $default,
),
$attributes
);
if ($variadic && null !== $default) {
throw new Error('Variadic parameter cannot have a default value');
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@ class Standard extends PrettyPrinterAbstract
public function pParam(Node\Param $node) {
return ($node->type ? (is_string($node->type) ? $node->type : $this->p($node->type)) . ' ' : '')
. ($node->byRef ? '&' : '')
. ($node->variadic ? '... ' : '')
. '$' . $node->name
. ($node->default ? ' = ' . $this->p($node->default) : '');
}

View File

@ -82,6 +82,9 @@ class EmulativeTest extends \PHPUnit_Framework_TestCase
public function provideTestLexNewFeatures() {
return array(
array('...', array(
array(Parser::T_ELLIPSIS, '...'),
)),
array('0b1010110', array(
array(Parser::T_LNUMBER, '0b1010110'),
)),

View File

@ -51,6 +51,15 @@ CODE;
<attribute:endLine>
<scalar:int>4</scalar:int>
</attribute:endLine>
<subNode:type>
<scalar:null/>
</subNode:type>
<subNode:byRef>
<scalar:true/>
</subNode:byRef>
<subNode:variadic>
<scalar:false/>
</subNode:variadic>
<subNode:name>
<scalar:string>a</scalar:string>
</subNode:name>
@ -67,12 +76,6 @@ CODE;
</subNode:value>
</node:Scalar_LNumber>
</subNode:default>
<subNode:type>
<scalar:null/>
</subNode:type>
<subNode:byRef>
<scalar:true/>
</subNode:byRef>
</node:Param>
<node:Param>
<attribute:startLine>
@ -81,6 +84,15 @@ CODE;
<attribute:endLine>
<scalar:int>4</scalar:int>
</attribute:endLine>
<subNode:type>
<scalar:null/>
</subNode:type>
<subNode:byRef>
<scalar:false/>
</subNode:byRef>
<subNode:variadic>
<scalar:false/>
</subNode:variadic>
<subNode:name>
<scalar:string>b</scalar:string>
</subNode:name>
@ -97,12 +109,6 @@ CODE;
</subNode:value>
</node:Scalar_DNumber>
</subNode:default>
<subNode:type>
<scalar:null/>
</subNode:type>
<subNode:byRef>
<scalar:false/>
</subNode:byRef>
</node:Param>
</scalar:array>
</subNode:params>

View File

@ -13,10 +13,11 @@ array(
byRef: false
params: array(
0: Param(
name: a
default: null
type: null
byRef: false
variadic: false
name: a
default: null
)
)
uses: array(
@ -32,10 +33,11 @@ array(
byRef: false
params: array(
0: Param(
name: a
default: null
type: null
byRef: false
variadic: false
name: a
default: null
)
)
uses: array(
@ -70,10 +72,11 @@ array(
byRef: true
params: array(
0: Param(
name: a
default: null
type: null
byRef: false
variadic: false
name: a
default: null
)
)
uses: array(

View File

@ -11,10 +11,11 @@ array(
name: a
params: array(
0: Param(
name: b
default: null
type: null
byRef: true
variadic: false
name: b
default: null
)
)
stmts: array(
@ -25,10 +26,11 @@ array(
name: a
params: array(
0: Param(
name: b
default: null
type: null
byRef: false
variadic: false
name: b
default: null
)
)
stmts: array(

View File

@ -20,6 +20,9 @@ array(
name: a
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: b
default: Expr_ConstFetch(
name: Name(
@ -28,18 +31,20 @@ array(
)
)
)
type: null
byRef: false
)
1: Param(
type: null
byRef: false
variadic: false
name: c
default: Scalar_String(
value: foo
)
type: null
byRef: false
)
2: Param(
type: null
byRef: false
variadic: false
name: d
default: Expr_ClassConstFetch(
class: Name(
@ -49,48 +54,53 @@ array(
)
name: B
)
type: null
byRef: false
)
3: Param(
type: null
byRef: false
variadic: false
name: f
default: Expr_UnaryPlus(
expr: Scalar_LNumber(
value: 1
)
)
type: null
byRef: false
)
4: Param(
type: null
byRef: false
variadic: false
name: g
default: Expr_UnaryMinus(
expr: Scalar_DNumber(
value: 1
)
)
type: null
byRef: false
)
5: Param(
type: null
byRef: false
variadic: false
name: h
default: Expr_Array(
items: array(
)
)
type: null
byRef: false
)
6: Param(
type: null
byRef: false
variadic: false
name: i
default: Expr_Array(
items: array(
)
)
type: null
byRef: false
)
7: Param(
type: null
byRef: false
variadic: false
name: j
default: Expr_Array(
items: array(
@ -103,10 +113,11 @@ array(
)
)
)
type: null
byRef: false
)
8: Param(
type: null
byRef: false
variadic: false
name: k
default: Expr_Array(
items: array(
@ -128,8 +139,6 @@ array(
)
)
)
type: null
byRef: false
)
)
stmts: array(

View File

@ -10,32 +10,36 @@ array(
name: a
params: array(
0: Param(
name: b
default: null
type: null
byRef: false
variadic: false
name: b
default: null
)
1: Param(
name: c
default: null
type: array
byRef: false
variadic: false
name: c
default: null
)
2: Param(
name: d
default: null
type: callable
byRef: false
variadic: false
name: d
default: null
)
3: Param(
name: f
default: null
type: Name(
parts: array(
0: E
)
)
byRef: false
variadic: false
name: f
default: null
)
)
stmts: array(

View File

@ -0,0 +1,106 @@
Variadic functions
-----
<?php
function test($a, ... $b) {}
function test($a, &... $b) {}
function test($a, Type ... $b) {}
function test($a, Type &... $b) {}
-----
array(
0: Stmt_Function(
byRef: false
name: test
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
1: Param(
type: null
byRef: false
variadic: true
name: b
default: null
)
)
stmts: array(
)
)
1: Stmt_Function(
byRef: false
name: test
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
1: Param(
type: null
byRef: true
variadic: true
name: b
default: null
)
)
stmts: array(
)
)
2: Stmt_Function(
byRef: false
name: test
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
1: Param(
type: Name(
parts: array(
0: Type
)
)
byRef: false
variadic: true
name: b
default: null
)
)
stmts: array(
)
)
3: Stmt_Function(
byRef: false
name: test
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
1: Param(
type: Name(
parts: array(
0: Type
)
)
byRef: true
variadic: true
name: b
default: null
)
)
stmts: array(
)
)
)

View File

@ -0,0 +1,6 @@
Invalid variadic function
-----
<?php
function foo(...$foo = []) {}
-----
Variadic parameter cannot have a default value on line 2

View File

@ -0,0 +1,25 @@
Function parameters
-----
<?php
function test($a, &$b, Type $c, Type &$c, Type &... $d) {}
function test(... $foo) {}
function test(Type ... $foo) {}
function test(&... $foo) {}
-----
function test($a, &$b, Type $c, Type &$c, Type &... $d)
{
}
function test(... $foo)
{
}
function test(Type ... $foo)
{
}
function test(&... $foo)
{
}