Skip to content

Commit 3feaa68

Browse files
committed
Handle final/abstract/readonly for structs
1 parent 911f9fb commit 3feaa68

File tree

5 files changed

+57
-6
lines changed

5 files changed

+57
-6
lines changed

Zend/tests/structs/abstract.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Structs must not be marked as abstract
3+
--FILE--
4+
<?php
5+
6+
abstract struct Box {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot use the abstract modifier on a struct, as structs are implicitly final in %s on line %d

Zend/tests/structs/final.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Structs must not be marked as final
3+
--FILE--
4+
<?php
5+
6+
final struct Box {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Cannot use the final modifier on a struct, as structs are implicitly final in %s on line %d

Zend/tests/structs/readonly.phpt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Structs may be marked as readonly
3+
--FILE--
4+
<?php
5+
6+
readonly struct Box {
7+
public function __construct(
8+
public mixed $value,
9+
) {}
10+
}
11+
12+
$a = new Box(42);
13+
$b = new Box(42);
14+
var_dump($a === $b);
15+
16+
try {
17+
$b->value = 43;
18+
} catch (Error $e) {
19+
echo $e->getMessage(), "\n";
20+
}
21+
22+
?>
23+
--EXPECT--
24+
bool(true)
25+
Cannot modify readonly property Box::$value

Zend/zend_compile.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,16 @@ uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
944944
"Cannot use the final modifier on an abstract class", 0);
945945
return 0;
946946
}
947+
if ((new_flags & ZEND_ACC_STRUCT) && (new_flags & ZEND_ACC_FINAL)) {
948+
zend_throw_exception(zend_ce_compile_error,
949+
"Cannot use the final modifier on a struct, as structs are implicitly final", 0);
950+
return 0;
951+
}
952+
if ((new_flags & ZEND_ACC_STRUCT) && (new_flags & ZEND_ACC_ABSTRACT)) {
953+
zend_throw_exception(zend_ce_compile_error,
954+
"Cannot use the abstract modifier on a struct, as structs are implicitly final", 0);
955+
return 0;
956+
}
947957
return new_flags;
948958
}
949959
/* }}} */
@@ -8257,10 +8267,6 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
82578267

82588268
zend_class_entry *original_ce = CG(active_class_entry);
82598269

8260-
if (decl->flags & ZEND_ACC_STRUCT) {
8261-
// TODO: ABSTRACT, FINAL, READONLY
8262-
}
8263-
82648270
if (EXPECTED((decl->flags & ZEND_ACC_ANON_CLASS) == 0)) {
82658271
zend_string *unqualified_name = decl->name;
82668272

Zend/zend_language_parser.y

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,9 +595,9 @@ is_variadic:
595595
;
596596

597597
class_declaration_statement:
598-
class_modifiers class_like { $<num>$ = CG(zend_lineno); }
598+
class_modifiers class_like { $<num>$ = zend_add_class_modifier($1, $2); if (!$<num>$) { YYERROR; } } { $<num>$ = CG(zend_lineno); }
599599
T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}'
600-
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1 | $2, $<num>3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); }
600+
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, $<num>3, $<num>4, $8, zend_ast_get_str($5), $6, $7, $10, NULL, NULL); }
601601
| class_like { $<num>$ = CG(zend_lineno); }
602602
T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}'
603603
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $<num>2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); }

0 commit comments

Comments
 (0)