Skip to content

Commit 629a29b

Browse files
committed
Add mutating keyword
1 parent 273270e commit 629a29b

13 files changed

+262
-6
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Mutating method calls on data classes
3+
--FILE--
4+
<?php
5+
6+
data class D {
7+
public function nonMutating() {
8+
echo __FUNCTION__, "\n";
9+
}
10+
11+
public mutating function mutating() {
12+
echo __FUNCTION__, "\n";
13+
}
14+
}
15+
16+
$d = new D();
17+
18+
$d->nonMutating();
19+
$d->mutating!();
20+
21+
try {
22+
$d->nonMutating!();
23+
} catch (Error $e) {
24+
echo $e->getMessage(), "\n";
25+
}
26+
27+
try {
28+
$d->mutating();
29+
} catch (Error $e) {
30+
echo $e->getMessage(), "\n";
31+
}
32+
33+
?>
34+
--EXPECT--
35+
nonMutating
36+
mutating
37+
Non-mutating method must not be called with $object->func!() syntax
38+
Mutating method must be called with $object->func!() syntax

Zend/tests/data_classes/mutating_methods.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ data class Point {
99
public int $y,
1010
) {}
1111

12-
public /* mutating */ function zero() {
12+
public mutating function zero() {
1313
$this->x = 0;
1414
$this->y = 0;
1515
}

Zend/tests/data_classes/nested_references.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ data class Point {
99
public int $y,
1010
) {}
1111

12-
public /* mutating */ function zero() {
12+
public mutating function zero() {
1313
$this->x = 0;
1414
$this->y = 0;
1515
}

Zend/tests/data_classes/references.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ data class Point {
99
public int $y,
1010
) {}
1111

12-
public /* mutating */ function zero() {
12+
public mutating function zero() {
1313
$this->x = 0;
1414
$this->y = 0;
1515
}

Zend/zend_compile.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,8 @@ static char *zend_modifier_token_to_string(uint32_t token)
844844
return "readonly";
845845
case T_ABSTRACT:
846846
return "abstract";
847+
case T_MUTATING:
848+
return "mutating";
847849
EMPTY_SWITCH_DEFAULT_CASE()
848850
}
849851
}
@@ -877,6 +879,11 @@ uint32_t zend_modifier_token_to_flag(zend_modifier_target target, uint32_t token
877879
return ZEND_ACC_STATIC;
878880
}
879881
break;
882+
case T_MUTATING:
883+
if (target == ZEND_MODIFIER_TARGET_METHOD) {
884+
return ZEND_ACC_MUTATING;
885+
}
886+
break;
880887
}
881888

882889
char *member;

Zend/zend_compile.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ typedef struct _zend_oparray_context {
318318
/* Data classes are value types | | | */
319319
#define ZEND_ACC_DATA_CLASS (1 << 30) /* X | | | */
320320
/* | | | */
321-
/* Function Flags (unused: 29-30) | | | */
321+
/* Function Flags (unused: 30) | | | */
322322
/* ============== | | | */
323323
/* | | | */
324324
/* deprecation flag | | | */
@@ -380,6 +380,9 @@ typedef struct _zend_oparray_context {
380380
/* has #[\Override] attribute | | | */
381381
#define ZEND_ACC_OVERRIDE (1 << 28) /* | X | | */
382382
/* | | | */
383+
/* Mutating function of a data class | | | */
384+
#define ZEND_ACC_MUTATING (1 << 29) /* | X | | */
385+
/* | | | */
383386
/* op_array uses strict mode types | | | */
384387
#define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */
385388

Zend/zend_language_parser.y

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
151151
%token <ident> T_STATIC "'static'"
152152
%token <ident> T_ABSTRACT "'abstract'"
153153
%token <ident> T_FINAL "'final'"
154+
%token <ident> T_MUTATING "'mutating'"
154155
%token <ident> T_PRIVATE "'private'"
155156
%token <ident> T_PROTECTED "'protected'"
156157
%token <ident> T_PUBLIC "'public'"
@@ -308,7 +309,7 @@ reserved_non_modifiers:
308309

309310
semi_reserved:
310311
reserved_non_modifiers
311-
| T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY
312+
| T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY | T_MUTATING
312313
;
313314

314315
ampersand:
@@ -1067,6 +1068,7 @@ member_modifier:
10671068
| T_ABSTRACT { $$ = T_ABSTRACT; }
10681069
| T_FINAL { $$ = T_FINAL; }
10691070
| T_READONLY { $$ = T_READONLY; }
1071+
| T_MUTATING { $$ = T_MUTATING; }
10701072
;
10711073

10721074
property_list:

Zend/zend_language_scanner.l

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_
17291729
RETURN_TOKEN_WITH_IDENT(T_FINAL);
17301730
}
17311731

1732+
<ST_IN_SCRIPTING>"mutating" {
1733+
RETURN_TOKEN_WITH_IDENT(T_MUTATING);
1734+
}
1735+
17321736
<ST_IN_SCRIPTING>"private" {
17331737
RETURN_TOKEN_WITH_IDENT(T_PRIVATE);
17341738
}

Zend/zend_vm_def.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3607,6 +3607,21 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV,
36073607
}
36083608
HANDLE_EXCEPTION();
36093609
}
3610+
if (UNEXPECTED(
3611+
((fbc->common.fn_flags & ZEND_ACC_MUTATING) != 0)
3612+
!= ((opline->extended_value & ZEND_INIT_METHOD_CALL_MUTATING) != 0)
3613+
)) {
3614+
if (fbc->common.fn_flags & ZEND_ACC_MUTATING) {
3615+
zend_throw_error(NULL, "Mutating method must be called with $object->func!() syntax");
3616+
} else {
3617+
zend_throw_error(NULL, "Non-mutating method must not be called with $object->func!() syntax");
3618+
}
3619+
FREE_OP2();
3620+
if ((OP1_TYPE & (IS_VAR|IS_TMP_VAR)) && !needs_addref && GC_DELREF(orig_obj) == 0) {
3621+
zend_objects_store_del(orig_obj);
3622+
}
3623+
HANDLE_EXCEPTION();
3624+
}
36103625
if (OP2_TYPE == IS_CONST &&
36113626
EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) &&
36123627
EXPECTED(obj == orig_obj)) {

0 commit comments

Comments
 (0)