mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2024-11-30 04:29:15 +01:00
Add support for anonymous classes
Has not landed upstream yet, but syntax is unlikely to change.
This commit is contained in:
parent
66896dbde6
commit
e1a0ec3724
@ -148,7 +148,7 @@ function resolveMacros($code) {
|
||||
return 'foreach (' . $args[0] . ' as &$s) { if (is_string($s)) { $s = Node\Scalar\String_::parseEscapeSequences($s, null); } } $s = preg_replace(\'~(\r\n|\n|\r)$~\', \'\', $s); if (\'\' === $s) array_pop(' . $args[0] . ');';
|
||||
}
|
||||
|
||||
throw new Exception(sprintf('Unknown macro "%s"', $name));
|
||||
return $matches[0];
|
||||
},
|
||||
$code
|
||||
);
|
||||
|
@ -650,8 +650,14 @@ scalar_dereference:
|
||||
/* alternative array syntax missing intentionally */
|
||||
;
|
||||
|
||||
anonymous_class:
|
||||
T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}'
|
||||
{ $$ = array(Stmt\Class_[null, [type: 0, extends: $3, implements: $4, stmts: $6]], $2); }
|
||||
|
||||
new_expr:
|
||||
T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; }
|
||||
| T_NEW anonymous_class
|
||||
{ list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; }
|
||||
;
|
||||
|
||||
lexical_vars:
|
||||
|
@ -7,7 +7,7 @@ use PhpParser\Node\Expr;
|
||||
|
||||
class New_ extends Expr
|
||||
{
|
||||
/** @var Node\Name|Expr Class name */
|
||||
/** @var Node\Name|Expr|Node\Stmt\Class_ Class name */
|
||||
public $class;
|
||||
/** @var Node\Arg[] Arguments */
|
||||
public $args;
|
||||
@ -15,9 +15,9 @@ class New_ extends Expr
|
||||
/**
|
||||
* Constructs a function call node.
|
||||
*
|
||||
* @param Node\Name|Expr $class Class name
|
||||
* @param Node\Arg[] $args Arguments
|
||||
* @param array $attributes Additional attributes
|
||||
* @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes)
|
||||
* @param Node\Arg[] $args Arguments
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct($class, array $args = array(), array $attributes = array()) {
|
||||
parent::__construct(null, $attributes);
|
||||
|
@ -32,7 +32,7 @@ class Class_ extends ClassLike
|
||||
/**
|
||||
* Constructs a class node.
|
||||
*
|
||||
* @param string $name Name
|
||||
* @param string|null $name Name
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
* 'type' => 0 : Type
|
||||
* 'extends' => null : Name of extended class
|
||||
@ -48,7 +48,7 @@ class Class_ extends ClassLike
|
||||
$this->implements = isset($subNodes['implements']) ? $subNodes['implements'] : array();
|
||||
$this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
|
||||
|
||||
if (isset(self::$specialNames[(string) $this->name])) {
|
||||
if (null !== $this->name && isset(self::$specialNames[$this->name])) {
|
||||
throw new Error(sprintf('Cannot use \'%s\' as class name as it is reserved', $this->name));
|
||||
}
|
||||
|
||||
@ -81,6 +81,10 @@ class Class_ extends ClassLike
|
||||
return (bool) ($this->type & self::MODIFIER_FINAL);
|
||||
}
|
||||
|
||||
public function isAnonymous() {
|
||||
return null === $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -37,7 +37,9 @@ class NameResolver extends NodeVisitorAbstract
|
||||
$interface = $this->resolveClassName($interface);
|
||||
}
|
||||
|
||||
$this->addNamespacedName($node);
|
||||
if (null !== $node->name) {
|
||||
$this->addNamespacedName($node);
|
||||
}
|
||||
} elseif ($node instanceof Stmt\Interface_) {
|
||||
foreach ($node->extends as &$interface) {
|
||||
$interface = $this->resolveClassName($interface);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -466,6 +466,10 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
public function pExpr_New(Expr\New_ $node) {
|
||||
if ($node->class instanceof Stmt\Class_) {
|
||||
$args = $node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : '';
|
||||
return 'new ' . $this->pClassCommon($node->class, $args);
|
||||
}
|
||||
return 'new ' . $this->p($node->class) . '(' . $this->pCommaSeparated($node->args) . ')';
|
||||
}
|
||||
|
||||
@ -527,11 +531,7 @@ class Standard extends PrettyPrinterAbstract
|
||||
}
|
||||
|
||||
public function pStmt_Class(Stmt\Class_ $node) {
|
||||
return $this->pModifiers($node->type)
|
||||
. 'class ' . $node->name
|
||||
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
|
||||
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
|
||||
. "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
|
||||
return $this->pClassCommon($node, ' ' . $node->name);
|
||||
}
|
||||
|
||||
public function pStmt_Trait(Stmt\Trait_ $node) {
|
||||
@ -729,6 +729,14 @@ class Standard extends PrettyPrinterAbstract
|
||||
return is_string($node) ? $node : $this->p($node);
|
||||
}
|
||||
|
||||
protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) {
|
||||
return $this->pModifiers($node->type)
|
||||
. 'class' . $afterClassToken
|
||||
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
|
||||
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
|
||||
. "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}';
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public function pObjectProperty($node) {
|
||||
if ($node instanceof Expr) {
|
||||
|
@ -257,6 +257,7 @@ EOC;
|
||||
new Stmt\Const_(array(
|
||||
new Node\Const_('D', new Node\Scalar\String_('E'))
|
||||
)),
|
||||
new Expr\New_(new Stmt\Class_(null)),
|
||||
));
|
||||
|
||||
$traverser = new PhpParser\NodeTraverser;
|
||||
@ -268,10 +269,12 @@ EOC;
|
||||
$this->assertSame('NS\\B', (string) $stmts[0]->stmts[1]->namespacedName);
|
||||
$this->assertSame('NS\\C', (string) $stmts[0]->stmts[2]->namespacedName);
|
||||
$this->assertSame('NS\\D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
|
||||
$this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[4]->class);
|
||||
$this->assertSame('A', (string) $stmts[1]->stmts[0]->namespacedName);
|
||||
$this->assertSame('B', (string) $stmts[1]->stmts[1]->namespacedName);
|
||||
$this->assertSame('C', (string) $stmts[1]->stmts[2]->namespacedName);
|
||||
$this->assertSame('D', (string) $stmts[1]->stmts[3]->consts[0]->namespacedName);
|
||||
$this->assertObjectNotHasAttribute('namespacedName', $stmts[1]->stmts[4]->class);
|
||||
}
|
||||
|
||||
public function testAddTraitNamespacedName() {
|
||||
|
194
test/code/parser/stmt/class/anonymous.test
Normal file
194
test/code/parser/stmt/class/anonymous.test
Normal file
@ -0,0 +1,194 @@
|
||||
Anonymous classes
|
||||
-----
|
||||
<?php
|
||||
|
||||
new class {
|
||||
public function test() {}
|
||||
};
|
||||
new class extends A implements B, C {};
|
||||
new class() {
|
||||
public $foo;
|
||||
};
|
||||
new class($a, $b) extends A {
|
||||
use T;
|
||||
};
|
||||
|
||||
class A {
|
||||
public function test() {
|
||||
return new class($this) extends A {
|
||||
const A = 'B';
|
||||
};
|
||||
}
|
||||
}
|
||||
-----
|
||||
array(
|
||||
0: Expr_New(
|
||||
class: Stmt_Class(
|
||||
type: 0
|
||||
name: null
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
type: 1
|
||||
byRef: false
|
||||
name: test
|
||||
params: array(
|
||||
)
|
||||
returnType: null
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
1: Expr_New(
|
||||
class: Stmt_Class(
|
||||
type: 0
|
||||
name: null
|
||||
extends: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
implements: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: B
|
||||
)
|
||||
)
|
||||
1: Name(
|
||||
parts: array(
|
||||
0: C
|
||||
)
|
||||
)
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
2: Expr_New(
|
||||
class: Stmt_Class(
|
||||
type: 0
|
||||
name: null
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_Property(
|
||||
type: 1
|
||||
props: array(
|
||||
0: Stmt_PropertyProperty(
|
||||
name: foo
|
||||
default: null
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
)
|
||||
)
|
||||
3: Expr_New(
|
||||
class: Stmt_Class(
|
||||
type: 0
|
||||
name: null
|
||||
extends: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_TraitUse(
|
||||
traits: array(
|
||||
0: Name(
|
||||
parts: array(
|
||||
0: T
|
||||
)
|
||||
)
|
||||
)
|
||||
adaptations: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
0: Arg(
|
||||
value: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
1: Arg(
|
||||
value: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
)
|
||||
)
|
||||
4: Stmt_Class(
|
||||
type: 0
|
||||
name: A
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassMethod(
|
||||
type: 1
|
||||
byRef: false
|
||||
name: test
|
||||
params: array(
|
||||
)
|
||||
returnType: null
|
||||
stmts: array(
|
||||
0: Stmt_Return(
|
||||
expr: Expr_New(
|
||||
class: Stmt_Class(
|
||||
type: 0
|
||||
name: null
|
||||
extends: Name(
|
||||
parts: array(
|
||||
0: A
|
||||
)
|
||||
)
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Stmt_ClassConst(
|
||||
consts: array(
|
||||
0: Const(
|
||||
name: A
|
||||
value: Scalar_String(
|
||||
value: B
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
0: Arg(
|
||||
value: Expr_Variable(
|
||||
name: this
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
27
test/code/prettyPrinter/anonymousClass.test
Normal file
27
test/code/prettyPrinter/anonymousClass.test
Normal file
@ -0,0 +1,27 @@
|
||||
Anonymous classes
|
||||
-----
|
||||
<?php
|
||||
|
||||
new class {};
|
||||
new class extends A implements B, C {};
|
||||
new class($a) extends A {
|
||||
private $a;
|
||||
public function __construct($a) {
|
||||
$this->a = $a;
|
||||
}
|
||||
};
|
||||
-----
|
||||
new class
|
||||
{
|
||||
};
|
||||
new class extends A implements B, C
|
||||
{
|
||||
};
|
||||
new class($a) extends A
|
||||
{
|
||||
private $a;
|
||||
public function __construct($a)
|
||||
{
|
||||
$this->a = $a;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user