Skip to content

Commit 85b6bf8

Browse files
authored
Support php 8.0 "Constructor Promotion" and attributes (nikic#160)
- attributes are only available in AST version 80+, and will be null before php 8.0
1 parent 66de07d commit 85b6bf8

16 files changed

+522
-43
lines changed

README.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,12 @@ ast\flags\CLASS_TRAIT
253253
ast\flags\CLASS_INTERFACE
254254
ast\flags\CLASS_ANONYMOUS
255255
256-
// Used by ast\AST_PARAM (cominable)
256+
// Used by ast\AST_PARAM (combinable)
257257
ast\flags\PARAM_REF
258258
ast\flags\PARAM_VARIADIC
259+
ast\flags\MODIFIER_PUBLIC
260+
ast\flags\MODIFIER_PROTECTED
261+
ast\flags\MODIFIER_PRIVATE
259262
260263
// Used by ast\AST_TYPE (exclusive)
261264
ast\flags\TYPE_ARRAY
@@ -361,20 +364,22 @@ This section lists the AST node kinds that are supported and the names of their
361364

362365
```
363366
AST_ARRAY_ELEM: value, key
364-
AST_ARROW_FUNC: name, docComment, params, stmts, returnType
367+
AST_ARROW_FUNC: name, docComment, params, stmts, returnType, attributes
365368
AST_ASSIGN: var, expr
366369
AST_ASSIGN_OP: var, expr
367370
AST_ASSIGN_REF: var, expr
371+
AST_ATTRIBUTE: class, args // php 8.0+ attributes (version 80+)
368372
AST_BINARY_OP: left, right
369373
AST_BREAK: depth
370374
AST_CALL: expr, args
371375
AST_CAST: expr
372376
AST_CATCH: class, var, stmts
373377
AST_CLASS: name, docComment, extends, implements, stmts
374378
AST_CLASS_CONST: class, const
379+
AST_CLASS_CONST_GROUP class, attributes // version 80+
375380
AST_CLASS_NAME: class // version 70+
376381
AST_CLONE: expr
377-
AST_CLOSURE: name, docComment, params, uses, stmts, returnType
382+
AST_CLOSURE: name, docComment, params, uses, stmts, returnType, attributes
378383
AST_CLOSURE_VAR: name
379384
AST_CONDITIONAL: cond, true, false
380385
AST_CONST: name
@@ -388,7 +393,7 @@ AST_EMPTY: expr
388393
AST_EXIT: expr
389394
AST_FOR: init, cond, loop, stmts
390395
AST_FOREACH: expr, value, key, stmts
391-
AST_FUNC_DECL: name, docComment, params, stmts, returnType
396+
AST_FUNC_DECL: name, docComment, params, stmts, returnType, attributes
392397
uses // prior to version 60
393398
AST_GLOBAL: var
394399
AST_GOTO: label
@@ -400,23 +405,23 @@ AST_INSTANCEOF: expr, class
400405
AST_ISSET: var
401406
AST_LABEL: name
402407
AST_MAGIC_CONST:
403-
AST_METHOD: name, docComment, params, stmts, returnType
408+
AST_METHOD: name, docComment, params, stmts, returnType, attributes
404409
uses // prior to version 60
405410
AST_METHOD_CALL: expr, method, args
406411
AST_METHOD_REFERENCE: class, method
407412
AST_NAME: name
408413
AST_NAMESPACE: name, stmts
409414
AST_NEW: class, args
410415
AST_NULLABLE_TYPE: type // Used only since PHP 7.1
411-
AST_PARAM: type, name, default
416+
AST_PARAM: type, name, default, attributes, docComment
412417
AST_POST_DEC: var
413418
AST_POST_INC: var
414419
AST_PRE_DEC: var
415420
AST_PRE_INC: var
416421
AST_PRINT: expr
417422
AST_PROP: expr, prop
418423
AST_PROP_ELEM: name, default, docComment
419-
AST_PROP_GROUP: type, props // version 70+
424+
AST_PROP_GROUP: type, props, attributes // version 70+
420425
AST_REF: var // only used in foreach ($a as &$v)
421426
AST_RETURN: expr
422427
AST_SHELL_EXEC: expr
@@ -443,6 +448,7 @@ AST_YIELD_FROM: expr
443448
// List nodes (numerically indexed children):
444449
AST_ARG_LIST
445450
AST_ARRAY
451+
AST_ATTRIBUTE_LIST // php 8.0+ attributes (version 80+)
446452
AST_CATCH_LIST
447453
AST_CLASS_CONST_DECL
448454
AST_CLOSURE_USES
@@ -483,6 +489,9 @@ are listed.
483489
Available since 1.0.7 (XXX).
484490

485491
* `mixed` type hints are now reported as an `AST_TYPE` with type `TYPE_MIXED` instead of an `AST_NAME`.
492+
* `AST_CLASS_CONST_GROUP` nodes are emitted for all class constant declarations.
493+
* `AST_PARAM`, `AST_CLASS_DECL`, `AST_METHOD`, `AST_PROP_DECL`, `AST_CLOSURE`, `AST_FUNC_DECL`, and `AST_ARROW_FUNC` nodes
494+
now contain an attributes child.
486495

487496
### 70 (current)
488497

ast.c

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ static const char *class_flags[] = {
107107
static const char *param_flags[] = {
108108
AST_FLAG(PARAM_REF),
109109
AST_FLAG(PARAM_VARIADIC),
110+
#if PHP_VERSION_ID >= 80000
111+
AST_FLAG(MODIFIER_PUBLIC),
112+
AST_FLAG(MODIFIER_PROTECTED),
113+
AST_FLAG(MODIFIER_PRIVATE),
114+
#endif
110115
NULL
111116
};
112117

@@ -287,6 +292,8 @@ static const ast_flag_info flag_info[] = {
287292
{ ZEND_AST_CONDITIONAL, 1, conditional_flags },
288293
};
289294

295+
// NOTE(tandre) in php 8, to get a writeable pointer to a property, OBJ_PROP_NUM(AST_CACHE_SLOT_PROPNAME) can be used.
296+
290297
static inline void ast_update_property(zval *object, zend_string *name, zval *value, void **cache_slot) {
291298
#if PHP_VERSION_ID < 80000
292299
zval name_zv;
@@ -339,6 +346,7 @@ static zend_ast *get_ast(zend_string *code, zend_arena **ast_arena, char *filena
339346
return ast;
340347
}
341348

349+
/* Returns whether node->attr (i.e. flags) is used by this node kind. Not to be confused with php 8.0's attributes. */
342350
static inline zend_bool ast_kind_uses_attr(zend_ast_kind kind) {
343351
return kind == ZEND_AST_PARAM || kind == ZEND_AST_TYPE || kind == ZEND_AST_TRAIT_ALIAS
344352
|| kind == ZEND_AST_UNARY_OP || kind == ZEND_AST_BINARY_OP || kind == ZEND_AST_ASSIGN_OP
@@ -380,6 +388,9 @@ static inline zend_bool ast_is_name(zend_ast *ast, zend_ast *parent, uint32_t i)
380388
|| parent->kind == ZEND_AST_NEW || parent->kind == ZEND_AST_STATIC_CALL
381389
|| parent->kind == ZEND_AST_CLASS_CONST || parent->kind == ZEND_AST_STATIC_PROP
382390
|| parent->kind == ZEND_AST_PROP_GROUP || parent->kind == ZEND_AST_CLASS_NAME
391+
#if PHP_VERSION_ID >= 80000
392+
|| parent->kind == ZEND_AST_ATTRIBUTE
393+
#endif
383394
;
384395
}
385396

@@ -507,7 +518,11 @@ static inline zend_ast_attr ast_assign_op_to_binary_op(zend_ast_attr attr) {
507518
static inline zend_ast **ast_get_children(zend_ast *ast, uint32_t *count) {
508519
if (ast_kind_is_decl(ast->kind)) {
509520
zend_ast_decl *decl = (zend_ast_decl *) ast;
521+
#if PHP_VERSION_ID >= 80000
522+
*count = decl->kind == ZEND_AST_CLASS ? 4 : 5;
523+
#else
510524
*count = decl->kind == ZEND_AST_CLASS ? 3 : 4;
525+
#endif
511526
return decl->child;
512527
} else if (zend_ast_is_list(ast)) {
513528
zend_ast_list *list = zend_ast_get_list(ast);
@@ -622,14 +637,44 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
622637
}
623638
}
624639

640+
#if PHP_VERSION_ID >= 80000
641+
if (state->version < 80) {
642+
switch (ast_kind) {
643+
case ZEND_AST_PARAM:
644+
if (i >= 3) {
645+
/* Skip attributes and doc comment */
646+
continue;
647+
}
648+
break;
649+
case ZEND_AST_METHOD:
650+
case ZEND_AST_FUNC_DECL:
651+
case ZEND_AST_CLOSURE:
652+
case ZEND_AST_ARROW_FUNC:
653+
if (i == 4) {
654+
continue;
655+
}
656+
break;
657+
case ZEND_AST_CLASS:
658+
if (i == 3) {
659+
continue;
660+
}
661+
break;
662+
case ZEND_AST_PROP_GROUP:
663+
if (i == 2) {
664+
continue;
665+
}
666+
break;
667+
}
668+
}
669+
#endif
625670
/* This AST_CATCH check should occur before ast_is_name() */
626671
#if PHP_VERSION_ID < 70100
627672
if (ast_kind == ZEND_AST_CATCH && i == 0) {
628673
/* Emulate PHP 7.1 format (name list) */
629674
zval tmp;
630675
ast_create_virtual_node(&tmp, AST_NAME, child->attr, child, state);
631676
ast_create_virtual_node_ex(
632-
&child_zv, ZEND_AST_NAME_LIST, 0, zend_ast_get_lineno(child), state, 1, &tmp);
677+
&child_zv, ZEND_AST_NAME_LIST, 0, zend_ast_get_lineno(child), state, 1, &tmp);
633678
} else
634679
#endif
635680
if (ast_is_name(child, ast, i)) {
@@ -693,11 +738,35 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
693738
zval tmp;
694739
ZVAL_NULL(&tmp);
695740
zend_hash_add_new(ht, AST_STR(str_docComment), &tmp);
741+
return;
742+
}
743+
#endif
744+
#if PHP_VERSION_ID < 80000
745+
if (state->version >= 80) {
746+
if (ast_kind == ZEND_AST_PARAM) {
747+
zval tmp;
748+
ZVAL_NULL(&tmp);
749+
zend_hash_add_new(ht, AST_STR(str_attributes), &tmp);
750+
zend_hash_add_new(ht, AST_STR(str_docComment), &tmp);
751+
return;
752+
} else if (ast_kind == ZEND_AST_PROP_GROUP) {
753+
zval tmp;
754+
ZVAL_NULL(&tmp);
755+
zend_hash_add_new(ht, AST_STR(str_attributes), &tmp);
756+
return;
757+
}
696758
}
697759
#endif
698760

699761
if (ast_kind_is_decl(ast_kind)) {
700762
zval id_zval;
763+
#if PHP_VERSION_ID < 80000
764+
if (state->version >= 80) {
765+
zval tmp;
766+
ZVAL_NULL(&tmp);
767+
zend_hash_add_new(ht, AST_STR(str_attributes), &tmp);
768+
}
769+
#endif
701770
ZVAL_LONG(&id_zval, state->declIdCounter);
702771
state->declIdCounter++;
703772
zend_hash_add_new(ht, AST_STR(str___declId), &id_zval);
@@ -755,6 +824,15 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
755824
ast->kind = ZEND_AST_UNARY_OP;
756825
ast->attr = AST_MINUS;
757826
break;
827+
#if PHP_VERSION_ID >= 80000
828+
case ZEND_AST_CLASS_CONST_GROUP:
829+
// ast->child is [AST_CLASS_CONST_DECL, optional attributes_list]
830+
if (state->version < 80) {
831+
// Keep class constants as a list of numerically indexed values in php 8
832+
ast_to_zval(zv, ast->child[0], state);
833+
return;
834+
}
835+
#endif
758836
#if PHP_VERSION_ID >= 70400
759837
case ZEND_AST_PROP_GROUP:
760838
if (state->version < 70) {
@@ -876,14 +954,32 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
876954
#if PHP_VERSION_ID < 70400
877955
if (ast->kind == ZEND_AST_PROP_DECL && state->version >= 70) {
878956
zval type_zval;
879-
zval prop_group_zval = *zv;
957+
zval prop_group_zval;
958+
ZVAL_COPY_VALUE(&prop_group_zval, zv);
880959
ZVAL_NULL(&type_zval);
881960
// For version 70, create an AST_PROP_GROUP wrapping the created AST_PROP_DECL.
882-
ast_create_virtual_node_ex(
883-
zv, ZEND_AST_PROP_GROUP, ast->attr, zend_ast_get_lineno(ast), state, 2, &type_zval, &prop_group_zval);
961+
if (state->version >= 80) {
962+
// For version 80, add a null attributes node.
963+
ast_create_virtual_node_ex(
964+
zv, ZEND_AST_PROP_GROUP, ast->attr, zend_ast_get_lineno(ast), state, 3, &type_zval, &prop_group_zval, &type_zval);
965+
} else {
966+
ast_create_virtual_node_ex(
967+
zv, ZEND_AST_PROP_GROUP, ast->attr, zend_ast_get_lineno(ast), state, 2, &type_zval, &prop_group_zval);
968+
}
884969
ast_update_property_long(&prop_group_zval, AST_STR(str_flags), 0, AST_CACHE_SLOT_FLAGS);
885970
}
886971
#endif
972+
#if PHP_VERSION_ID < 80000
973+
if (ast->kind == ZEND_AST_CLASS_CONST_DECL && state->version >= 80) {
974+
zval const_decl_zval;
975+
zval attributes_zval;
976+
ZVAL_COPY_VALUE(&const_decl_zval, zv);
977+
ZVAL_NULL(&attributes_zval);
978+
// For version 80, create an AST_CLASS_CONST_GROUP wrapping the created AST_CLASS_CONST_DECL
979+
ast_create_virtual_node_ex(
980+
zv, ZEND_AST_CLASS_CONST_GROUP, 0, zend_ast_get_lineno(ast), state, 2, &const_decl_zval, &attributes_zval);
981+
}
982+
#endif
887983
}
888984

889985
static const zend_long versions[] = {50, 60, 70, 80};

0 commit comments

Comments
 (0)