Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ PHP NEWS
. Properly handle __debugInfo() returning an array reference. (nielsdos)
. Properly handle reference return value from __toString(). (nielsdos)
. Added the pipe (|>) operator. (crell)
. Added support for `final` with constructor property promotion.
(DanielEScherzer)

- Curl:
. Added curl_multi_get_handles(). (timwolla)
Expand Down
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ PHP 8.5 UPGRADE NOTES
RFC: https://wiki.php.net/rfc/attributes-on-constants
. Added the pipe (|>) operator.
RFC: https://wiki.php.net/rfc/pipe-operator-v3
. Constructor property promotion can now be used for final properties.
RFC: https://wiki.php.net/rfc/final_promotion

- Curl:
. Added support for share handles that are persisted across multiple PHP
Expand Down
18 changes: 18 additions & 0 deletions Zend/tests/property_hooks/final_prop_promoted_1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Promoted property may be marked final (hook)
--FILE--
<?php

class A {
public function __construct(
public final $prop { get {} set {} }
) {}
}

class B extends A {
public $prop { get {} set {} }
}

?>
--EXPECTF--
Fatal error: Cannot override final property A::$prop in %s on line %d
18 changes: 18 additions & 0 deletions Zend/tests/property_hooks/final_prop_promoted_2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Promoted property may be marked final (normal)
--FILE--
<?php

class A {
public function __construct(
public final $prop
) {}
}

class B extends A {
public $prop { get {} set {} }
}

?>
--EXPECTF--
Fatal error: Cannot override final property A::$prop in %s on line %d
18 changes: 18 additions & 0 deletions Zend/tests/property_hooks/final_prop_promoted_3.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Promoted property may be marked final (no visibility needed)
--FILE--
<?php

class A {
public function __construct(
final $prop
) {}
}

class B extends A {
public $prop { get {} set {} }
}

?>
--EXPECTF--
Fatal error: Cannot override final property A::$prop in %s on line %d
18 changes: 18 additions & 0 deletions Zend/tests/property_hooks/final_prop_promoted_4.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Final promoted property conflicts with non-promoted non-hooked property
--FILE--
<?php

class A {
public function __construct(
final $prop
) {}
}

class B extends A {
public $prop;
}

?>
--EXPECTF--
Fatal error: Cannot override final property A::$prop in %s on line %d
28 changes: 28 additions & 0 deletions Zend/tests/property_hooks/final_prop_promoted_5.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
Non-promoted constructor parameter does not conflict with final promoted property
--FILE--
<?php

class A {
public function __construct(
final $prop
) {
echo __METHOD__ . "(): $prop\n";
}
}

class B extends A {
public function __construct(
$prop
) {
echo __METHOD__ . "(): $prop\n";
parent::__construct($prop);
}
}

$b = new B("test");

?>
--EXPECT--
B::__construct(): test
A::__construct(): test
18 changes: 18 additions & 0 deletions Zend/tests/property_hooks/final_prop_promoted_ast.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Confirm that the AST indicates final promoted properties
--FILE--
<?php
try {
assert(false && new class {
public function __construct(public final $prop) {}
});
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
assert(false && new class {
public function __construct(public final $prop) {
}

})
3 changes: 3 additions & 0 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -2795,6 +2795,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
zend_ast_export_attributes(str, ast->child[3], indent, 0);
}
zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_CPP);
if (ast->attr & ZEND_ACC_FINAL) {
smart_str_appends(str, "final ");
}
if (ast->child[0]) {
zend_ast_export_type(str, ast->child[0], indent);
smart_str_appendc(str, ' ');
Expand Down
10 changes: 2 additions & 8 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -903,13 +903,7 @@ uint32_t zend_modifier_token_to_flag(zend_modifier_target target, uint32_t token
}
break;
case T_FINAL:
if (target == ZEND_MODIFIER_TARGET_METHOD
|| target == ZEND_MODIFIER_TARGET_CONSTANT
|| target == ZEND_MODIFIER_TARGET_PROPERTY
|| target == ZEND_MODIFIER_TARGET_PROPERTY_HOOK) {
return ZEND_ACC_FINAL;
}
break;
return ZEND_ACC_FINAL;
case T_STATIC:
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_METHOD) {
return ZEND_ACC_STATIC;
Expand Down Expand Up @@ -7681,7 +7675,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast));
bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0;
uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_READONLY);
uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_READONLY | ZEND_ACC_FINAL);
bool is_promoted = property_flags || hooks_ast;

znode var_node, default_node;
Expand Down
4 changes: 4 additions & 0 deletions ext/reflection/tests/ReflectionProperty_isFinal.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class C {
public protected(set) final mixed $p6;
public private(set) mixed $p7;
public private(set) final mixed $p8;

public function __construct(final $p9, public $p10) {}
}

$rc = new ReflectionClass(C::class);
Expand All @@ -30,3 +32,5 @@ p5: bool(false)
p6: bool(true)
p7: bool(true)
p8: bool(true)
p9: bool(true)
p10: bool(false)