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

View File

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

View File

@ -2,13 +2,15 @@
namespace PhpParser\Node; namespace PhpParser\Node;
use PhpParser\Error;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
/** /**
* @property string $name Name * @property null|string|Name $type Typehint
* @property null|Expr $default Default value * @property bool $byRef Whether is passed by reference
* @property null|string|Name $type Typehint * @property bool $variadic Whether this is a variadic argument
* @property bool $byRef Whether is passed by reference * @property string $name Name
* @property null|Expr $default Default value
*/ */
class Param extends NodeAbstract class Param extends NodeAbstract
{ {
@ -19,17 +21,23 @@ class Param extends NodeAbstract
* @param null|Expr $default Default value * @param null|Expr $default Default value
* @param null|string|Name $type Typehint * @param null|string|Name $type Typehint
* @param bool $byRef Whether is passed by reference * @param bool $byRef Whether is passed by reference
* @param bool $variadic Whether this is a variadic argument
* @param array $attributes Additional attributes * @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( parent::__construct(
array( array(
'name' => $name, 'type' => $type,
'default' => $default, 'byRef' => $byRef,
'type' => $type, 'variadic' => $variadic,
'byRef' => $byRef 'name' => $name,
'default' => $default,
), ),
$attributes $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) { public function pParam(Node\Param $node) {
return ($node->type ? (is_string($node->type) ? $node->type : $this->p($node->type)) . ' ' : '') return ($node->type ? (is_string($node->type) ? $node->type : $this->p($node->type)) . ' ' : '')
. ($node->byRef ? '&' : '') . ($node->byRef ? '&' : '')
. ($node->variadic ? '... ' : '')
. '$' . $node->name . '$' . $node->name
. ($node->default ? ' = ' . $this->p($node->default) : ''); . ($node->default ? ' = ' . $this->p($node->default) : '');
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,32 +10,36 @@ array(
name: a name: a
params: array( params: array(
0: Param( 0: Param(
name: b
default: null
type: null type: null
byRef: false byRef: false
variadic: false
name: b
default: null
) )
1: Param( 1: Param(
name: c
default: null
type: array type: array
byRef: false byRef: false
variadic: false
name: c
default: null
) )
2: Param( 2: Param(
name: d
default: null
type: callable type: callable
byRef: false byRef: false
variadic: false
name: d
default: null
) )
3: Param( 3: Param(
name: f
default: null
type: Name( type: Name(
parts: array( parts: array(
0: E 0: E
) )
) )
byRef: false byRef: false
variadic: false
name: f
default: null
) )
) )
stmts: array( 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)
{
}