Skip to content

Commit 7fe259e

Browse files
TysonAndrenikic
authored andcommitted
Add AST_PROP_GROUP in version 70.
The type information is deliberately left out of versions < 70. `children['type']` is a Node describing the type of the property group, or null. `children['props']` is the AST_PROP_DECL. The property visibility modifier flags were moved to the AST_PROP_GROUP. Update package.xml
1 parent 24a626e commit 7fe259e

13 files changed

+372
-49
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ ast\flags\NAME_FQ (= 0) // example: \Foo\Bar
227227
ast\flags\NAME_NOT_FQ // example: Foo\Bar
228228
ast\flags\NAME_RELATIVE // example: namespace\Foo\Bar
229229
230-
// Used by ast\AST_METHOD, ast\AST_PROP_DECL, ast\AST_CLASS_CONST_DECL
231-
// and ast\AST_TRAIT_ALIAS (combinable)
230+
// Used by ast\AST_METHOD, ast\AST_PROP_DECL, ast\AST_CLASS_CONST_DECL,
231+
// ast\AST_PROP_GROUP and ast\AST_TRAIT_ALIAS (combinable)
232232
ast\flags\MODIFIER_PUBLIC
233233
ast\flags\MODIFIER_PROTECTED
234234
ast\flags\MODIFIER_PRIVATE
@@ -404,6 +404,7 @@ AST_PRE_INC: var
404404
AST_PRINT: expr
405405
AST_PROP: expr, prop
406406
AST_PROP_ELEM: name, default, docComment
407+
AST_PROP_GROUP: type, props // version 70+
407408
AST_REF: var // only used in foreach ($a as &$v)
408409
AST_RETURN: expr
409410
AST_SHELL_EXEC: expr
@@ -465,6 +466,13 @@ function accepts a boolean argument that determines whether deprecated versions
465466
In the following the changes in the respective AST versions, as well as their current support state,
466467
are listed.
467468

469+
### 70 (experimental)
470+
471+
* `AST_PROP_GROUP` was added to support PHP 7.4's typed properties.
472+
The property visibility modifiers are now part of `AST_PROP_GROUP` instead of `AST_PROP_DECL`.
473+
474+
Note that property group type information is only available with AST versions 70+.
475+
468476
### 60 (current)
469477

470478
Supported since 0.1.7 (2018-10-06).

ast.c

Lines changed: 78 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ static const ast_flag_info flag_info[] = {
253253
{ ZEND_AST_FUNC_DECL, 1, func_flags },
254254
{ ZEND_AST_CLOSURE, 1, func_flags },
255255
{ ZEND_AST_PROP_DECL, 1, modifier_flags },
256+
{ ZEND_AST_PROP_GROUP, 1, modifier_flags },
256257
{ ZEND_AST_CLASS_CONST_DECL, 1, visibility_flags },
257258
{ ZEND_AST_TRAIT_ALIAS, 1, modifier_flags },
258259
};
@@ -311,6 +312,9 @@ static inline zend_bool ast_kind_uses_attr(zend_ast_kind kind) {
311312
|| kind == ZEND_AST_UNARY_OP || kind == ZEND_AST_BINARY_OP || kind == ZEND_AST_ASSIGN_OP
312313
|| kind == ZEND_AST_CAST || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_ARRAY_ELEM
313314
|| kind == ZEND_AST_INCLUDE_OR_EVAL || kind == ZEND_AST_USE || kind == ZEND_AST_PROP_DECL
315+
#if PHP_VERSION_ID >= 70400
316+
|| kind == ZEND_AST_PROP_GROUP
317+
#endif
314318
|| kind == ZEND_AST_GROUP_USE || kind == ZEND_AST_USE_ELEM
315319
|| kind == AST_NAME || kind == AST_CLOSURE_VAR || kind == ZEND_AST_CLASS_CONST_DECL
316320
|| kind == ZEND_AST_ARRAY;
@@ -336,6 +340,9 @@ static inline zend_bool ast_is_name(zend_ast *ast, zend_ast *parent, uint32_t i)
336340
|| parent->kind == ZEND_AST_CALL || parent->kind == ZEND_AST_CONST
337341
|| parent->kind == ZEND_AST_NEW || parent->kind == ZEND_AST_STATIC_CALL
338342
|| parent->kind == ZEND_AST_CLASS_CONST || parent->kind == ZEND_AST_STATIC_PROP
343+
#if PHP_VERSION_ID >= 70400
344+
|| parent->kind == ZEND_AST_PROP_GROUP
345+
#endif
339346
;
340347
}
341348

@@ -354,7 +361,11 @@ static inline zend_bool ast_is_name(zend_ast *ast, zend_ast *parent, uint32_t i)
354361
/* Assumes that ast_is_name is already true */
355362
static inline zend_bool ast_is_type(zend_ast *ast, zend_ast *parent, uint32_t i) {
356363
if (i == 0) {
357-
return parent->kind == ZEND_AST_PARAM;
364+
return parent->kind == ZEND_AST_PARAM
365+
#if PHP_VERSION_ID >= 70400
366+
|| parent->kind == ZEND_AST_PROP_GROUP
367+
#endif
368+
;
358369
}
359370
if (i == 3) {
360371
return parent->kind == ZEND_AST_CLOSURE || parent->kind == ZEND_AST_FUNC_DECL
@@ -512,6 +523,46 @@ static void ast_create_virtual_node(
512523
zv, kind, attr, zend_ast_get_lineno(child), state, 1, &child_zv);
513524
}
514525

526+
static inline void ast_name_to_zval(zend_ast *child, zend_ast *ast, zval *child_zv, int i, ast_state_info_t *state) {
527+
zend_uchar type;
528+
zend_bool is_nullable = 0;
529+
if (child->attr & ZEND_TYPE_NULLABLE) {
530+
is_nullable = 1;
531+
child->attr &= ~ZEND_TYPE_NULLABLE;
532+
}
533+
534+
if (child->attr == ZEND_NAME_FQ) {
535+
/* Ensure there is no leading \ for fully-qualified names. This can happen if
536+
* something like ('\bar')() is used. */
537+
zval *name = zend_ast_get_zval(child);
538+
if (Z_STRVAL_P(name)[0] == '\\') {
539+
zend_string *new_name = zend_string_init(
540+
Z_STRVAL_P(name) + 1, Z_STRLEN_P(name) - 1, 0);
541+
zend_string_release(Z_STR_P(name));
542+
Z_STR_P(name) = new_name;
543+
}
544+
}
545+
546+
if (child->attr == ZEND_NAME_NOT_FQ
547+
&& ast_is_type(child, ast, i)
548+
&& (type = lookup_builtin_type(zend_ast_get_str(child)))
549+
) {
550+
/* Convert "int" etc typehints to TYPE nodes */
551+
ast_create_virtual_node_ex(
552+
child_zv, ZEND_AST_TYPE, type, zend_ast_get_lineno(child), state, 0);
553+
} else {
554+
ast_create_virtual_node(child_zv, AST_NAME, child->attr, child, state);
555+
}
556+
557+
if (is_nullable) {
558+
/* Create explicit AST_NULLABLE_TYPE node */
559+
zval tmp;
560+
ZVAL_COPY_VALUE(&tmp, child_zv);
561+
ast_create_virtual_node_ex(
562+
child_zv, AST_NULLABLE_TYPE, 0, zend_ast_get_lineno(child), state, 1, &tmp);
563+
}
564+
}
565+
515566
static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t *state) {
516567
uint32_t i, count;
517568
zend_bool is_list = zend_ast_is_list(ast);
@@ -542,43 +593,7 @@ static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, ast_state_info_t
542593
} else
543594
#endif
544595
if (ast_is_name(child, ast, i)) {
545-
zend_uchar type;
546-
zend_bool is_nullable = 0;
547-
if (child->attr & ZEND_TYPE_NULLABLE) {
548-
is_nullable = 1;
549-
child->attr &= ~ZEND_TYPE_NULLABLE;
550-
}
551-
552-
if (child->attr == ZEND_NAME_FQ) {
553-
/* Ensure there is no leading \ for fully-qualified names. This can happen if
554-
* something like ('\bar')() is used. */
555-
zval *name = zend_ast_get_zval(child);
556-
if (Z_STRVAL_P(name)[0] == '\\') {
557-
zend_string *new_name = zend_string_init(
558-
Z_STRVAL_P(name) + 1, Z_STRLEN_P(name) - 1, 0);
559-
zend_string_release(Z_STR_P(name));
560-
Z_STR_P(name) = new_name;
561-
}
562-
}
563-
564-
if (child->attr == ZEND_NAME_NOT_FQ
565-
&& ast_is_type(child, ast, i)
566-
&& (type = lookup_builtin_type(zend_ast_get_str(child)))
567-
) {
568-
/* Convert "int" etc typehints to TYPE nodes */
569-
ast_create_virtual_node_ex(
570-
&child_zv, ZEND_AST_TYPE, type, zend_ast_get_lineno(child), state, 0);
571-
} else {
572-
ast_create_virtual_node(&child_zv, AST_NAME, child->attr, child, state);
573-
}
574-
575-
if (is_nullable) {
576-
/* Create explicit AST_NULLABLE_TYPE node */
577-
zval tmp;
578-
ZVAL_COPY_VALUE(&tmp, &child_zv);
579-
ast_create_virtual_node_ex(
580-
&child_zv, AST_NULLABLE_TYPE, 0, zend_ast_get_lineno(child), state, 1, &tmp);
581-
}
596+
ast_name_to_zval(child, ast, &child_zv, i, state);
582597
} else if (child && child->kind == ZEND_AST_TYPE && (child->attr & ZEND_TYPE_NULLABLE)) {
583598
child->attr &= ~ZEND_TYPE_NULLABLE;
584599
ast_create_virtual_node(&child_zv, AST_NULLABLE_TYPE, 0, child, state);
@@ -693,6 +708,19 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
693708
ast->kind = ZEND_AST_UNARY_OP;
694709
ast->attr = AST_MINUS;
695710
break;
711+
#if PHP_VERSION_ID >= 70400
712+
case ZEND_AST_PROP_GROUP:
713+
if (state->version < 70) {
714+
// In versions less than 70, just omit property type information entirely.
715+
// ast->child is [type_ast, prop_ast]
716+
ast_to_zval(zv, ast->child[1], state);
717+
// The property visibility is on the AST_PROP_GROUP node.
718+
// Add it to the AST_PROP_DECL node for old
719+
ast_update_property_long(zv, AST_STR(str_flags), ast->attr, AST_CACHE_SLOT_FLAGS);
720+
return;
721+
}
722+
break;
723+
#endif
696724
}
697725

698726
#if PHP_VERSION_ID < 70100
@@ -747,9 +775,20 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
747775
}
748776

749777
ast_fill_children_ht(Z_ARRVAL(children_zv), ast, state);
778+
#if PHP_VERSION_ID < 70400
779+
if (ast->kind == ZEND_AST_PROP_DECL && state->version >= 70) {
780+
zval type_zval;
781+
zval prop_group_zval = *zv;
782+
ZVAL_NULL(&type_zval);
783+
// For version 70, create an AST_PROP_GROUP wrapping the created AST_PROP_DECL.
784+
ast_create_virtual_node_ex(
785+
zv, ZEND_AST_PROP_GROUP, ast->attr, zend_ast_get_lineno(ast), state, 2, &type_zval, &prop_group_zval);
786+
ast_update_property_long(&prop_group_zval, AST_STR(str_flags), 0, AST_CACHE_SLOT_FLAGS);
787+
}
788+
#endif
750789
}
751790

752-
static const zend_long versions[] = {50, 60};
791+
static const zend_long versions[] = {50, 60, 70};
753792
static const size_t versions_count = sizeof(versions)/sizeof(versions[0]);
754793

755794
static inline zend_bool ast_version_deprecated(zend_long version) {

ast_data.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#include "php_ast.h"
22

3-
const size_t ast_kinds_count = 91;
4-
53
const zend_ast_kind ast_kinds[] = {
64
ZEND_AST_ARG_LIST,
75
ZEND_AST_LIST,
@@ -78,6 +76,7 @@ const zend_ast_kind ast_kinds[] = {
7876
ZEND_AST_SWITCH_CASE,
7977
ZEND_AST_DECLARE,
8078
ZEND_AST_PROP_ELEM,
79+
ZEND_AST_PROP_GROUP,
8180
ZEND_AST_CONST_ELEM,
8281
ZEND_AST_USE_TRAIT,
8382
ZEND_AST_TRAIT_PRECEDENCE,
@@ -96,6 +95,8 @@ const zend_ast_kind ast_kinds[] = {
9695
ZEND_AST_FOREACH,
9796
};
9897

98+
const size_t ast_kinds_count = sizeof(ast_kinds) / sizeof(ast_kinds[0]);
99+
99100
const char *ast_kind_to_name(zend_ast_kind kind) {
100101
switch (kind) {
101102
case ZEND_AST_ARG_LIST: return "AST_ARG_LIST";
@@ -173,6 +174,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
173174
case ZEND_AST_SWITCH_CASE: return "AST_SWITCH_CASE";
174175
case ZEND_AST_DECLARE: return "AST_DECLARE";
175176
case ZEND_AST_PROP_ELEM: return "AST_PROP_ELEM";
177+
case ZEND_AST_PROP_GROUP: return "AST_PROP_GROUP";
176178
case ZEND_AST_CONST_ELEM: return "AST_CONST_ELEM";
177179
case ZEND_AST_USE_TRAIT: return "AST_USE_TRAIT";
178180
case ZEND_AST_TRAIT_PRECEDENCE: return "AST_TRAIT_PRECEDENCE";
@@ -513,6 +515,12 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
513515
case 2: return AST_STR(str_docComment);
514516
}
515517
return NULL;
518+
case ZEND_AST_PROP_GROUP:
519+
switch (child) {
520+
case 0: return AST_STR(str_type);
521+
case 1: return AST_STR(str_props);
522+
}
523+
return NULL;
516524
case ZEND_AST_CONST_ELEM:
517525
switch (child) {
518526
case 0: return AST_STR(str_name);
@@ -701,6 +709,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
701709
REGISTER_NS_LONG_CONSTANT("ast", "AST_SWITCH_CASE", ZEND_AST_SWITCH_CASE, CONST_CS | CONST_PERSISTENT);
702710
REGISTER_NS_LONG_CONSTANT("ast", "AST_DECLARE", ZEND_AST_DECLARE, CONST_CS | CONST_PERSISTENT);
703711
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROP_ELEM", ZEND_AST_PROP_ELEM, CONST_CS | CONST_PERSISTENT);
712+
REGISTER_NS_LONG_CONSTANT("ast", "AST_PROP_GROUP", ZEND_AST_PROP_GROUP, CONST_CS | CONST_PERSISTENT);
704713
REGISTER_NS_LONG_CONSTANT("ast", "AST_CONST_ELEM", ZEND_AST_CONST_ELEM, CONST_CS | CONST_PERSISTENT);
705714
REGISTER_NS_LONG_CONSTANT("ast", "AST_USE_TRAIT", ZEND_AST_USE_TRAIT, CONST_CS | CONST_PERSISTENT);
706715
REGISTER_NS_LONG_CONSTANT("ast", "AST_TRAIT_PRECEDENCE", ZEND_AST_TRAIT_PRECEDENCE, CONST_CS | CONST_PERSISTENT);

ast_str_defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
X(default) \
3636
X(cond) \
3737
X(declares) \
38+
X(props) \
3839
X(traits) \
3940
X(adaptations) \
4041
X(method) \

ast_stub.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
const AST_PARAM_LIST = 136;
2020
const AST_CLOSURE_USES = 137;
2121
const AST_PROP_DECL = 138;
22+
const AST_PROP_GROUP = 767;
2223
const AST_CONST_DECL = 139;
2324
const AST_CLASS_CONST_DECL = 140;
2425
const AST_NAME_LIST = 141;

package.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
</stability>
2424
<license uri="https://github.com/nikic/php-ast/blob/master/LICENSE">BSD-3-Clause</license>
2525
<notes>
26-
- TBD
26+
- Fix a segfault in PHP 7.4-dev after typed properties support was added.
27+
- Add AST version 70 (experimental): This adds AST_PROP_GROUP with type information for property groups.
2728
</notes>
2829
<contents>
2930
<dir name="/">
@@ -77,6 +78,8 @@
7778
<file name="parse_file_not_existing.phpt" role="test" />
7879
<file name="parse_file_parse_error.phpt" role="test" />
7980
<file name="parse_file.phpt" role="test" />
81+
<file name="php74_ordinary_class.phpt" role="test" />
82+
<file name="php74_type_hints.phpt" role="test" />
8083
<file name="prop_doc_comments.phpt" role="test" />
8184
<file name="stmt_list.phpt" role="test" />
8285
<file name="try_catch_finally.phpt" role="test" />

php_ast.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ extern zend_module_entry ast_module_entry;
2121
#include "TSRM.h"
2222
#endif
2323

24+
// PHP 7.4 added a 3rd cache slot for property_info
25+
// and expects cache_slot[2] to be null.
2426
#define AST_NUM_CACHE_SLOTS (3 * 4)
2527

2628
ZEND_BEGIN_MODULE_GLOBALS(ast)
@@ -47,6 +49,11 @@ extern ast_str_globals str_globals;
4749
#define AST_CLOSURE_VAR 2049
4850
#define AST_NULLABLE_TYPE 2050
4951

52+
// 544 is already taken by ZEND_AST_GROUP_USE
53+
#if PHP_VERSION_ID < 70400
54+
# define ZEND_AST_PROP_GROUP 0x2ff
55+
#endif
56+
5057
/* Pretend it still exists */
5158
#if PHP_VERSION_ID >= 70100
5259
# define ZEND_AST_LIST ((1 << (ZEND_AST_IS_LIST_SHIFT + 1)) - 1)

scripts/generate_ast_data.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
$code = <<<EOC
77
#include "php_ast.h"
88
9-
const size_t ast_kinds_count = {COUNT};
10-
119
const zend_ast_kind ast_kinds[] = {
1210
{KINDS}
1311
};
1412
13+
const size_t ast_kinds_count = sizeof(ast_kinds) / sizeof(ast_kinds[0]);
14+
1515
const char *ast_kind_to_name(zend_ast_kind kind) {
1616
\tswitch (kind) {
1717
{STRS}
@@ -117,6 +117,7 @@
117117
'ZEND_AST_SWITCH_CASE' => ['cond', 'stmts'],
118118
'ZEND_AST_DECLARE' => ['declares', 'stmts'],
119119
'ZEND_AST_PROP_ELEM' => ['name', 'default', 'docComment'],
120+
'ZEND_AST_PROP_GROUP' => ['type', 'props'],
120121
'ZEND_AST_CONST_ELEM' => ['name', 'value', 'docComment'],
121122
'ZEND_AST_USE_TRAIT' => ['traits', 'adaptations'],
122123
'ZEND_AST_TRAIT_PRECEDENCE' => ['method', 'insteadof'],
@@ -210,7 +211,6 @@
210211
. " CONST_CS | CONST_PERSISTENT);";
211212
}
212213

213-
$code = str_replace('{COUNT}', count($data), $code);
214214
$code = str_replace('{KINDS}', implode("\n", $kinds), $code);
215215
$code = str_replace('{STRS}', implode("\n", $strs), $code);
216216
$code = str_replace('{CONSTS}', implode("\n", $consts), $code);

scripts/generate_ast_stub.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<?php
22

33
$stubFile = __DIR__ . '/../ast_stub.php';
4+
if (!extension_loaded('ast')) {
5+
fwrite(STDERR, __FILE__ . " requires that php-ast be enabled\n");
6+
exit(1);
7+
}
48

59
$stub = file_get_contents($stubFile);
610
$stub = preg_replace_callback(

tests/get_supported_versions.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,19 @@ var_dump(ast\get_supported_versions(true));
88

99
?>
1010
--EXPECT--
11-
array(2) {
11+
array(3) {
1212
[0]=>
1313
int(50)
1414
[1]=>
1515
int(60)
16+
[2]=>
17+
int(70)
1618
}
17-
array(2) {
19+
array(3) {
1820
[0]=>
1921
int(50)
2022
[1]=>
2123
int(60)
24+
[2]=>
25+
int(70)
2226
}

0 commit comments

Comments
 (0)