Add support for typed constants

RFC: https://wiki.php.net/rfc/typed_class_constants
This commit is contained in:
Nikita Popov 2023-05-20 19:14:49 +02:00
parent 9a5d5c112c
commit 5c267f55c9
19 changed files with 2803 additions and 2561 deletions

View File

@ -340,7 +340,10 @@ non_empty_class_const_list:
; ;
class_const: class_const:
identifier_maybe_reserved '=' expr { $$ = Node\Const_[$1, $3]; } T_STRING '=' expr
{ $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
| semi_reserved '=' expr
{ $$ = Node\Const_[new Node\Identifier($1, stackAttributes(#1)), $3]; }
; ;
inner_statement_list_ex: inner_statement_list_ex:
@ -842,6 +845,9 @@ class_statement:
| optional_attributes method_modifiers T_CONST class_const_list semi | optional_attributes method_modifiers T_CONST class_const_list semi
{ $$ = new Stmt\ClassConst($4, $2, attributes(), $1); { $$ = new Stmt\ClassConst($4, $2, attributes(), $1);
$this->checkClassConst($$, #2); } $this->checkClassConst($$, #2); }
| optional_attributes method_modifiers T_CONST type_expr class_const_list semi
{ $$ = new Stmt\ClassConst($5, $2, attributes(), $1, $4);
$this->checkClassConst($$, #2); }
| optional_attributes method_modifiers T_FUNCTION optional_ref identifier_maybe_reserved '(' parameter_list ')' | optional_attributes method_modifiers T_FUNCTION optional_ref identifier_maybe_reserved '(' parameter_list ')'
optional_return_type method_body optional_return_type method_body
{ $$ = Stmt\ClassMethod[$5, ['type' => $2, 'byRef' => $4, 'params' => $7, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]]; { $$ = Stmt\ClassMethod[$5, ['type' => $2, 'byRef' => $4, 'params' => $7, 'returnType' => $9, 'stmts' => $10, 'attrGroups' => $1]];

View File

@ -22,6 +22,8 @@ class ClassConst implements PhpParser\Builder {
/** @var list<Node\AttributeGroup> */ /** @var list<Node\AttributeGroup> */
protected $attributeGroups = []; protected $attributeGroups = [];
/** @var Identifier|Node\Name|Node\ComplexType */
protected $type;
/** /**
* Creates a class constant builder * Creates a class constant builder
@ -119,6 +121,19 @@ class ClassConst implements PhpParser\Builder {
return $this; return $this;
} }
/**
* Sets the constant type.
*
* @param string|Node\Name|Identifier|Node\ComplexType $type
*
* @return $this
*/
public function setType($type) {
$this->type = BuilderHelpers::normalizeType($type);
return $this;
}
/** /**
* Returns the built class node. * Returns the built class node.
* *
@ -129,7 +144,8 @@ class ClassConst implements PhpParser\Builder {
$this->constants, $this->constants,
$this->flags, $this->flags,
$this->attributes, $this->attributes,
$this->attributeGroups $this->attributeGroups,
$this->type
); );
} }
} }

View File

@ -10,31 +10,36 @@ class ClassConst extends Node\Stmt {
public $flags; public $flags;
/** @var Node\Const_[] Constant declarations */ /** @var Node\Const_[] Constant declarations */
public $consts; public $consts;
/** @var Node\AttributeGroup[] */ /** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups; public $attrGroups;
/** @var Node\Identifier|Node\Name|Node\ComplexType Type declaration */
public $type;
/** /**
* Constructs a class const list node. * Constructs a class const list node.
* *
* @param Node\Const_[] $consts Constant declarations * @param Node\Const_[] $consts Constant declarations
* @param int $flags Modifiers * @param int $flags Modifiers
* @param array<string, mixed> $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
* @param list<Node\AttributeGroup> $attrGroups PHP attribute groups * @param list<Node\AttributeGroup> $attrGroups PHP attribute groups
* @param null|string|Node\Identifier|Node\Name|Node\ComplexType $type Type declaration
*/ */
public function __construct( public function __construct(
array $consts, array $consts,
int $flags = 0, int $flags = 0,
array $attributes = [], array $attributes = [],
array $attrGroups = [] array $attrGroups = [],
$type = null
) { ) {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->flags = $flags; $this->flags = $flags;
$this->consts = $consts; $this->consts = $consts;
$this->attrGroups = $attrGroups; $this->attrGroups = $attrGroups;
$this->type = $type;
} }
public function getSubNodeNames(): array { public function getSubNodeNames(): array {
return ['attrGroups', 'flags', 'consts']; return ['attrGroups', 'flags', 'type', 'consts'];
} }
/** /**

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -842,7 +842,9 @@ class Standard extends PrettyPrinterAbstract {
protected function pStmt_ClassConst(Stmt\ClassConst $node): string { protected function pStmt_ClassConst(Stmt\ClassConst $node): string {
return $this->pAttrGroups($node->attrGroups) return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags) . $this->pModifiers($node->flags)
. 'const ' . $this->pCommaSeparated($node->consts) . ';'; . 'const '
. (null !== $node->type ? $this->p($node->type) . ' ' : '')
. $this->pCommaSeparated($node->consts) . ';';
} }
protected function pStmt_Function(Stmt\Function_ $node): string { protected function pStmt_Function(Stmt\Function_ $node): string {

View File

@ -1391,6 +1391,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
'Param->default' => $stripEquals, 'Param->default' => $stripEquals,
'Stmt_Break->num' => $stripBoth, 'Stmt_Break->num' => $stripBoth,
'Stmt_Catch->var' => $stripLeft, 'Stmt_Catch->var' => $stripLeft,
'Stmt_ClassConst->type' => $stripRight,
'Stmt_ClassMethod->returnType' => $stripColon, 'Stmt_ClassMethod->returnType' => $stripColon,
'Stmt_Class->extends' => ['left' => \T_EXTENDS], 'Stmt_Class->extends' => ['left' => \T_EXTENDS],
'Stmt_Enum->scalarType' => $stripColon, 'Stmt_Enum->scalarType' => $stripColon,
@ -1434,6 +1435,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
'Stmt_Break->num' => [\T_BREAK, false, ' ', null], 'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
'Stmt_Catch->var' => [null, false, ' ', null], 'Stmt_Catch->var' => [null, false, ' ', null],
'Stmt_ClassMethod->returnType' => [')', false, ': ', null], 'Stmt_ClassMethod->returnType' => [')', false, ': ', null],
'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
'Stmt_Class->extends' => [null, false, ' extends ', null], 'Stmt_Class->extends' => [null, false, ' extends ', null],
'Stmt_Enum->scalarType' => [null, false, ' : ', null], 'Stmt_Enum->scalarType' => [null, false, ' : ', null],
'Stmt_EnumCase->expr' => [null, false, ' = ', null], 'Stmt_EnumCase->expr' => [null, false, ' = ', null],

View File

@ -142,6 +142,18 @@ class ClassConstTest extends \PHPUnit\Framework\TestCase {
); );
} }
public function testType() {
$node = $this->createClassConstBuilder('TYPE', 1)
->setType('int')
->getNode();
$this->assertEquals(
new Stmt\ClassConst(
[new Const_('TYPE', new Int_(1))],
0, [], [], new Identifier('int')),
$node
);
}
/** /**
* @dataProvider provideTestDefaultValues * @dataProvider provideTestDefaultValues
*/ */

View File

@ -49,6 +49,10 @@ X
private private
$x $x
; ;
const
X
= 1;
} }
foreach ( foreach (
@ -86,6 +90,7 @@ $stmts[9]->expr = new Expr\Variable('x');
$stmts[10]->extends = new Node\Name\FullyQualified('Bar'); $stmts[10]->extends = new Node\Name\FullyQualified('Bar');
$stmts[10]->stmts[0]->returnType = new Node\Name('Y'); $stmts[10]->stmts[0]->returnType = new Node\Name('Y');
$stmts[10]->stmts[1]->props[0]->default = new Scalar\DNumber(42.0); $stmts[10]->stmts[1]->props[0]->default = new Scalar\DNumber(42.0);
$stmts[10]->stmts[2]->type = new Node\Identifier('int');
$stmts[11]->keyVar = new Expr\Variable('z'); $stmts[11]->keyVar = new Expr\Variable('z');
$stmts[12]->vars[0]->default = new Scalar\String_('abc'); $stmts[12]->vars[0]->default = new Scalar\String_('abc');
$stmts[13]->finally = new Stmt\Finally_([]); $stmts[13]->finally = new Stmt\Finally_([]);
@ -140,6 +145,10 @@ X extends \Bar
private private
$x = 42.0 $x = 42.0
; ;
const int
X
= 1;
} }
foreach ( foreach (

View File

@ -35,6 +35,11 @@ Bar
y y
; ;
} }
const
int
X
= 1;
} }
$foo [ $bar ]; $foo [ $bar ];
@ -97,6 +102,7 @@ $stmts[2]->extends = null;
$stmts[2]->stmts[0]->returnType = null; $stmts[2]->stmts[0]->returnType = null;
$stmts[2]->stmts[1]->props[0]->default = null; $stmts[2]->stmts[1]->props[0]->default = null;
$stmts[2]->stmts[2]->adaptations[0]->newName = null; $stmts[2]->stmts[2]->adaptations[0]->newName = null;
$stmts[2]->stmts[3]->type = null;
$stmts[3]->expr->dim = null; $stmts[3]->expr->dim = null;
$stmts[4]->expr->expr = null; $stmts[4]->expr->expr = null;
$stmts[5]->expr->if = null; $stmts[5]->expr->if = null;
@ -141,6 +147,10 @@ Foo
public public
; ;
} }
const
X
= 1;
} }
$foo []; $foo [];

View File

@ -739,6 +739,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -1495,6 +1496,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(

View File

@ -152,6 +152,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -175,6 +176,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(

View File

@ -205,6 +205,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(

View File

@ -22,6 +22,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: STATIC (8) flags: STATIC (8)
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -59,6 +60,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: ABSTRACT (16) flags: ABSTRACT (16)
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -96,6 +98,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: READONLY (64) flags: READONLY (64)
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -133,6 +136,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: PUBLIC (1) flags: PUBLIC (1)
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(

View File

@ -26,6 +26,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -41,6 +42,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: PUBLIC (1) flags: PUBLIC (1)
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -56,6 +58,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: PROTECTED (2) flags: PROTECTED (2)
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -71,6 +74,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: PRIVATE (4) flags: PRIVATE (4)
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(
@ -86,6 +90,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: FINAL (32) flags: FINAL (32)
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(

View File

@ -46,6 +46,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(

View File

@ -0,0 +1,124 @@
Typed constants
-----
<?php
class Test {
const int X = 1;
private const string Y = "a", Z = "b";
const array ARRAY = [];
const Foo|Bar|null FOO = null;
}
-----
array(
0: Stmt_Class(
attrGroups: array(
)
flags: 0
name: Identifier(
name: Test
)
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassConst(
attrGroups: array(
)
flags: 0
type: Identifier(
name: int
)
consts: array(
0: Const(
name: Identifier(
name: X
)
value: Scalar_Int(
value: 1
)
)
)
)
1: Stmt_ClassConst(
attrGroups: array(
)
flags: PRIVATE (4)
type: Identifier(
name: string
)
consts: array(
0: Const(
name: Identifier(
name: Y
)
value: Scalar_String(
value: a
)
)
1: Const(
name: Identifier(
name: Z
)
value: Scalar_String(
value: b
)
)
)
)
2: Stmt_ClassConst(
attrGroups: array(
)
flags: 0
type: Identifier(
name: array
)
consts: array(
0: Const(
name: Identifier(
name: ARRAY
)
value: Expr_Array(
items: array(
)
)
)
)
)
3: Stmt_ClassConst(
attrGroups: array(
)
flags: 0
type: UnionType(
types: array(
0: Name(
parts: array(
0: Foo
)
)
1: Name(
parts: array(
0: Bar
)
)
2: Identifier(
name: null
)
)
)
consts: array(
0: Const(
name: Identifier(
name: FOO
)
value: Expr_ConstFetch(
name: Name(
parts: array(
0: null
)
)
)
)
)
)
)
)
)

View File

@ -126,6 +126,7 @@ array(
attrGroups: array( attrGroups: array(
) )
flags: 0 flags: 0
type: null
consts: array( consts: array(
0: Const( 0: Const(
name: Identifier( name: Identifier(

View File

@ -7,7 +7,7 @@ class Foo
const A = 1, B = 2; const A = 1, B = 2;
public const C = 3, D = 4; public const C = 3, D = 4;
protected const E = 5, F = 6; protected const E = 5, F = 6;
private const G = 7, H = 8; private const int G = 7, H = 8;
} }
----- -----
class Foo class Foo
@ -15,5 +15,5 @@ class Foo
const A = 1, B = 2; const A = 1, B = 2;
public const C = 3, D = 4; public const C = 3, D = 4;
protected const E = 5, F = 6; protected const E = 5, F = 6;
private const G = 7, H = 8; private const int G = 7, H = 8;
} }