Skip to content

[RFC] Final Property Promotion#17861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base:master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

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 UPGRADING
Original file line numberDiff line numberDiff line change
Expand Up@@ -106,6 +106,8 @@ PHP 8.5 UPGRADE NOTES
. Fatal Errors (such as an exceeded maximum execution time) now include a
backtrace.
RFC: https://wiki.php.net/rfc/error_backtraces_v2
. 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 numberDiff line numberDiff 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 {} }
}
Comment on lines +12 to +14
Copy link
Contributor

@mvorisekmvorisekApr 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class B extends A {
public$prop{ get {} set {} }
}
class B extends A {
public$prop= 5
}
Suggested change
class B extends A {
public$prop { get {} set {} }
}
class B extends A {
publicfunction __construct(
public$prop
) {}
}
Suggested change
class B extends A {
public$prop { get {} set {} }
}
class B extends A {
publicfunction __construct(
$prop
) {}
}

Is this allowed after this PR? If yes, is it tested?

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub's display is a bit confusing - are you suggesting that the code be

class B extends A { $prop } 

? That isn't valid PHP

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(edited)

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class B extends A { public function __construct( $prop ) {} } 

doesn't involve property promotion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does in A class and I would like to get this tested.


?>
--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 numberDiff line numberDiff 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 numberDiff line numberDiff 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_ast.phpt
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -2721,6 +2721,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 numberDiff line numberDiff 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 DownExpand Up@@ -7622,7 +7616,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
3 changes: 3 additions & 0 deletions ext/reflection/tests/ReflectionProperty_isFinal.phpt
Original file line numberDiff line numberDiff 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 ) {}
}

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