Skip to content

Commit 4dfba7a

Browse files
[RFC] Final Property Promotion
https://wiki.php.net/rfc/final_promotion
1 parent 01c3001 commit 4dfba7a

11 files changed

+131
-8
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ PHP NEWS
5656
. Properly handle __debugInfo() returning an array reference. (nielsdos)
5757
. Properly handle reference return value from __toString(). (nielsdos)
5858
. Added the pipe (|>) operator. (crell)
59+
. Added support for `final` with constructor property promotion.
60+
(DanielEScherzer)
5961

6062
- Curl:
6163
. Added curl_multi_get_handles(). (timwolla)

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ PHP 8.5 UPGRADE NOTES
146146
RFC: https://wiki.php.net/rfc/attributes-on-constants
147147
. Added the pipe (|>) operator.
148148
RFC: https://wiki.php.net/rfc/pipe-operator-v3
149+
. Constructor property promotion can now be used for final properties.
150+
RFC: https://wiki.php.net/rfc/final_promotion
149151

150152
- Curl:
151153
. Added support for share handles that are persisted across multiple PHP
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Promoted property may be marked final (hook)
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function __construct(
8+
public final $prop { get {} set {} }
9+
) {}
10+
}
11+
12+
class B extends A {
13+
public $prop { get {} set {} }
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Cannot override final property A::$prop in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Promoted property may be marked final (normal)
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function __construct(
8+
public final $prop
9+
) {}
10+
}
11+
12+
class B extends A {
13+
public $prop { get {} set {} }
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Cannot override final property A::$prop in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Promoted property may be marked final (no visibility needed)
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function __construct(
8+
final $prop
9+
) {}
10+
}
11+
12+
class B extends A {
13+
public $prop { get {} set {} }
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Cannot override final property A::$prop in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Final promoted property conflicts with non-promoted non-hooked property
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function __construct(
8+
final $prop
9+
) {}
10+
}
11+
12+
class B extends A {
13+
public $prop;
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Cannot override final property A::$prop in %s on line %d
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Non-promoted constructor parameter does not conflict with final promoted property
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function __construct(
8+
final $prop
9+
) {
10+
echo __METHOD__ . "(): $prop\n";
11+
}
12+
}
13+
14+
class B extends A {
15+
public function __construct(
16+
$prop
17+
) {
18+
echo __METHOD__ . "(): $prop\n";
19+
parent::__construct($prop);
20+
}
21+
}
22+
23+
$b = new B("test");
24+
25+
?>
26+
--EXPECT--
27+
B::__construct(): test
28+
A::__construct(): test
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Confirm that the AST indicates final promoted properties
3+
--FILE--
4+
<?php
5+
try {
6+
assert(false && new class {
7+
public function __construct(public final $prop) {}
8+
});
9+
} catch (Error $e) {
10+
echo $e->getMessage(), "\n";
11+
}
12+
?>
13+
--EXPECT--
14+
assert(false && new class {
15+
public function __construct(public final $prop) {
16+
}
17+
18+
})

Zend/zend_ast.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2795,6 +2795,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
27952795
zend_ast_export_attributes(str, ast->child[3], indent, 0);
27962796
}
27972797
zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_CPP);
2798+
if (ast->attr & ZEND_ACC_FINAL) {
2799+
smart_str_appends(str, "final ");
2800+
}
27982801
if (ast->child[0]) {
27992802
zend_ast_export_type(str, ast->child[0], indent);
28002803
smart_str_appendc(str, ' ');

Zend/zend_compile.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -903,13 +903,7 @@ uint32_t zend_modifier_token_to_flag(zend_modifier_target target, uint32_t token
903903
}
904904
break;
905905
case T_FINAL:
906-
if (target == ZEND_MODIFIER_TARGET_METHOD
907-
|| target == ZEND_MODIFIER_TARGET_CONSTANT
908-
|| target == ZEND_MODIFIER_TARGET_PROPERTY
909-
|| target == ZEND_MODIFIER_TARGET_PROPERTY_HOOK) {
910-
return ZEND_ACC_FINAL;
911-
}
912-
break;
906+
return ZEND_ACC_FINAL;
913907
case T_STATIC:
914908
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_METHOD) {
915909
return ZEND_ACC_STATIC;
@@ -7681,7 +7675,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
76817675
zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast));
76827676
bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
76837677
bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0;
7684-
uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_READONLY);
7678+
uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_READONLY | ZEND_ACC_FINAL);
76857679
bool is_promoted = property_flags || hooks_ast;
76867680

76877681
znode var_node, default_node;

ext/reflection/tests/ReflectionProperty_isFinal.phpt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class C {
1212
public protected(set) final mixed $p6;
1313
public private(set) mixed $p7;
1414
public private(set) final mixed $p8;
15+
16+
public function __construct(final $p9, public $p10) {}
1517
}
1618

1719
$rc = new ReflectionClass(C::class);
@@ -30,3 +32,5 @@ p5: bool(false)
3032
p6: bool(true)
3133
p7: bool(true)
3234
p8: bool(true)
35+
p9: bool(true)
36+
p10: bool(false)

0 commit comments

Comments
 (0)