mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
[RFC] Extend #[\Override] to target properties (#19061)
RFC: https://wiki.php.net/rfc/override_properties Co-authored-by: Tim Düsterhus <tim@bastelstu.be>
This commit is contained in:
parent
c3bee21256
commit
38beb44176
27 changed files with 449 additions and 19 deletions
1
NEWS
1
NEWS
|
@ -12,6 +12,7 @@ PHP NEWS
|
|||
. The backtick operator as an alias for shell_exec() has been deprecated.
|
||||
(timwolla)
|
||||
. Returning null from __debugInfo() has been deprecated. (DanielEScherzer)
|
||||
. Support #[\Override] on properties. (Jiří Pudil)
|
||||
|
||||
- Curl:
|
||||
. The curl_close() function has been deprecated. (DanielEScherzer)
|
||||
|
|
|
@ -178,6 +178,8 @@ PHP 8.5 UPGRADE NOTES
|
|||
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
|
||||
. #[\Override] can now be applied to properties.
|
||||
RFC: https://wiki.php.net/rfc/override_properties
|
||||
|
||||
- Curl:
|
||||
. Added support for share handles that are persisted across multiple PHP
|
||||
|
|
39
Zend/tests/attributes/override/properties_01.phpt
Normal file
39
Zend/tests/attributes/override/properties_01.phpt
Normal file
|
@ -0,0 +1,39 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface I {
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
interface II extends I {
|
||||
#[\Override]
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
class P {
|
||||
public mixed $p1;
|
||||
public mixed $p2;
|
||||
}
|
||||
|
||||
class PP extends P {
|
||||
#[\Override]
|
||||
public mixed $p1;
|
||||
public mixed $p2;
|
||||
}
|
||||
|
||||
class C extends PP implements I {
|
||||
#[\Override]
|
||||
public mixed $i;
|
||||
#[\Override]
|
||||
public mixed $p1;
|
||||
public mixed $p2;
|
||||
public mixed $c;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
15
Zend/tests/attributes/override/properties_02.phpt
Normal file
15
Zend/tests/attributes/override/properties_02.phpt
Normal file
|
@ -0,0 +1,15 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: No parent class.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
#[\Override]
|
||||
public mixed $c;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: C::$c has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
21
Zend/tests/attributes/override/properties_03.phpt
Normal file
21
Zend/tests/attributes/override/properties_03.phpt
Normal file
|
@ -0,0 +1,21 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: No parent class, but child implements matching interface.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface I {
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
class P {
|
||||
#[\Override]
|
||||
public mixed $i;
|
||||
}
|
||||
|
||||
class C extends P implements I {}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: P::$i has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
21
Zend/tests/attributes/override/properties_04.phpt
Normal file
21
Zend/tests/attributes/override/properties_04.phpt
Normal file
|
@ -0,0 +1,21 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: No parent class, but child implements matching interface (2).
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface I {
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
class C extends P implements I {}
|
||||
|
||||
class P {
|
||||
#[\Override]
|
||||
public mixed $i;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: P::$i has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
26
Zend/tests/attributes/override/properties_05.phpt
Normal file
26
Zend/tests/attributes/override/properties_05.phpt
Normal file
|
@ -0,0 +1,26 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: No parent interface.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface I {
|
||||
#[\Override]
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
interface II extends I {}
|
||||
|
||||
|
||||
class C implements II {
|
||||
public mixed $i;
|
||||
}
|
||||
|
||||
class C2 implements I {
|
||||
public mixed $i;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: I::$i has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
15
Zend/tests/attributes/override/properties_06.phpt
Normal file
15
Zend/tests/attributes/override/properties_06.phpt
Normal file
|
@ -0,0 +1,15 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: On trait.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
trait T {
|
||||
#[\Override]
|
||||
public mixed $t;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
19
Zend/tests/attributes/override/properties_07.phpt
Normal file
19
Zend/tests/attributes/override/properties_07.phpt
Normal file
|
@ -0,0 +1,19 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: On used trait without parent property.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
trait T {
|
||||
#[\Override]
|
||||
public mixed $t;
|
||||
}
|
||||
|
||||
class Foo {
|
||||
use T;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Foo::$t has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
23
Zend/tests/attributes/override/properties_08.phpt
Normal file
23
Zend/tests/attributes/override/properties_08.phpt
Normal file
|
@ -0,0 +1,23 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: On used trait with interface property.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
trait T {
|
||||
#[\Override]
|
||||
public mixed $i;
|
||||
}
|
||||
|
||||
interface I {
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
class Foo implements I {
|
||||
use T;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
19
Zend/tests/attributes/override/properties_09.phpt
Normal file
19
Zend/tests/attributes/override/properties_09.phpt
Normal file
|
@ -0,0 +1,19 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Parent property is private, child property is public.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class P {
|
||||
private mixed $p;
|
||||
}
|
||||
|
||||
class C extends P {
|
||||
#[\Override]
|
||||
public mixed $p;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: C::$p has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
19
Zend/tests/attributes/override/properties_10.phpt
Normal file
19
Zend/tests/attributes/override/properties_10.phpt
Normal file
|
@ -0,0 +1,19 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Parent property is private, child property is private.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class P {
|
||||
private mixed $p;
|
||||
}
|
||||
|
||||
class C extends P {
|
||||
#[\Override]
|
||||
private mixed $p;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: C::$p has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
19
Zend/tests/attributes/override/properties_11.phpt
Normal file
19
Zend/tests/attributes/override/properties_11.phpt
Normal file
|
@ -0,0 +1,19 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Parent property is protected, child property is public.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class P {
|
||||
protected mixed $p;
|
||||
}
|
||||
|
||||
class C extends P {
|
||||
#[\Override]
|
||||
public mixed $p;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
19
Zend/tests/attributes/override/properties_12.phpt
Normal file
19
Zend/tests/attributes/override/properties_12.phpt
Normal file
|
@ -0,0 +1,19 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Parent property is protected, child property is protected.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class P {
|
||||
protected mixed $p;
|
||||
}
|
||||
|
||||
class C extends P {
|
||||
#[\Override]
|
||||
protected mixed $p;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
21
Zend/tests/attributes/override/properties_13.phpt
Normal file
21
Zend/tests/attributes/override/properties_13.phpt
Normal file
|
@ -0,0 +1,21 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Redeclared trait property.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
trait T {
|
||||
public mixed $t;
|
||||
}
|
||||
|
||||
class C {
|
||||
use T;
|
||||
|
||||
#[\Override]
|
||||
public mixed $t;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: C::$t has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
25
Zend/tests/attributes/override/properties_14.phpt
Normal file
25
Zend/tests/attributes/override/properties_14.phpt
Normal file
|
@ -0,0 +1,25 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Redeclared trait property with interface.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface I {
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
trait T {
|
||||
public mixed $i;
|
||||
}
|
||||
|
||||
class C implements I {
|
||||
use T;
|
||||
|
||||
#[\Override]
|
||||
public mixed $i;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
19
Zend/tests/attributes/override/properties_15.phpt
Normal file
19
Zend/tests/attributes/override/properties_15.phpt
Normal file
|
@ -0,0 +1,19 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Valid anonymous class.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface I {
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
new class () implements I {
|
||||
#[\Override]
|
||||
public mixed $i;
|
||||
};
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
21
Zend/tests/attributes/override/properties_16.phpt
Normal file
21
Zend/tests/attributes/override/properties_16.phpt
Normal file
|
@ -0,0 +1,21 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Invalid anonymous class.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface I {
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
new class () implements I {
|
||||
public mixed $i;
|
||||
|
||||
#[\Override]
|
||||
public mixed $c;
|
||||
};
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: I@anonymous::$c has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
16
Zend/tests/attributes/override/properties_17.phpt
Normal file
16
Zend/tests/attributes/override/properties_17.phpt
Normal file
|
@ -0,0 +1,16 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Static property no parent class.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C
|
||||
{
|
||||
#[\Override]
|
||||
public static mixed $c;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: C::$c has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
19
Zend/tests/attributes/override/properties_18.phpt
Normal file
19
Zend/tests/attributes/override/properties_18.phpt
Normal file
|
@ -0,0 +1,19 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: Static property.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class P {
|
||||
public static mixed $p;
|
||||
}
|
||||
|
||||
class C extends P {
|
||||
#[\Override]
|
||||
public static mixed $p;
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
21
Zend/tests/attributes/override/properties_19.phpt
Normal file
21
Zend/tests/attributes/override/properties_19.phpt
Normal file
|
@ -0,0 +1,21 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: valid promoted property
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface I {
|
||||
public mixed $i { get; }
|
||||
}
|
||||
|
||||
class C implements I {
|
||||
public function __construct(
|
||||
#[\Override]
|
||||
public mixed $i,
|
||||
) {}
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
17
Zend/tests/attributes/override/properties_20.phpt
Normal file
17
Zend/tests/attributes/override/properties_20.phpt
Normal file
|
@ -0,0 +1,17 @@
|
|||
--TEST--
|
||||
#[\Override]: Properties: invalid promoted property
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class C {
|
||||
public function __construct(
|
||||
#[\Override]
|
||||
public mixed $c,
|
||||
) {}
|
||||
}
|
||||
|
||||
echo "Done";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: C::$c has #[\Override] attribute, but no matching parent property exists in %s on line %d
|
|
@ -68,7 +68,7 @@ final class SensitiveParameterValue
|
|||
/**
|
||||
* @strict-properties
|
||||
*/
|
||||
#[Attribute(Attribute::TARGET_METHOD)]
|
||||
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_PROPERTY)]
|
||||
final class Override
|
||||
{
|
||||
public function __construct() {}
|
||||
|
|
4
Zend/zend_attributes_arginfo.h
generated
4
Zend/zend_attributes_arginfo.h
generated
|
@ -1,5 +1,5 @@
|
|||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 9aee3d8f2ced376f5929048444eaa2529ff90311 */
|
||||
* Stub hash: 715016d1ff1b0a6abb325a71083eff397a080c44 */
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
|
||||
|
@ -230,7 +230,7 @@ static zend_class_entry *register_class_Override(void)
|
|||
zend_string *attribute_name_Attribute_class_Override_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
|
||||
zend_attribute *attribute_Attribute_class_Override_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Override_0, 1);
|
||||
zend_string_release(attribute_name_Attribute_class_Override_0);
|
||||
ZVAL_LONG(&attribute_Attribute_class_Override_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD);
|
||||
ZVAL_LONG(&attribute_Attribute_class_Override_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_PROPERTY);
|
||||
|
||||
return class_entry;
|
||||
}
|
||||
|
|
|
@ -7918,6 +7918,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
|
|||
if (attributes_ast) {
|
||||
zend_compile_attributes(
|
||||
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, ZEND_ATTRIBUTE_TARGET_PARAMETER);
|
||||
|
||||
zend_attribute *override_attribute = zend_get_attribute_str(prop->attributes, "override", sizeof("override")-1);
|
||||
if (override_attribute) {
|
||||
prop->flags |= ZEND_ACC_OVERRIDE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8897,6 +8902,11 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
|
|||
|
||||
if (attr_ast) {
|
||||
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0);
|
||||
|
||||
zend_attribute *override_attribute = zend_get_attribute_str(info->attributes, "override", sizeof("override")-1);
|
||||
if (override_attribute) {
|
||||
info->flags |= ZEND_ACC_OVERRIDE;
|
||||
}
|
||||
}
|
||||
|
||||
CG(context).active_property_info_name = old_active_property_info_name;
|
||||
|
|
|
@ -254,7 +254,10 @@ typedef struct _zend_oparray_context {
|
|||
/* or IS_CONSTANT_VISITED_MARK | | | */
|
||||
#define ZEND_CLASS_CONST_IS_CASE (1 << 6) /* | | | X */
|
||||
/* | | | */
|
||||
/* Property Flags (unused: 13...) | | | */
|
||||
/* has #[\Override] attribute | | | */
|
||||
#define ZEND_ACC_OVERRIDE (1 << 28) /* | X | X | */
|
||||
/* | | | */
|
||||
/* Property Flags (unused: 13-27,29...) | | | */
|
||||
/* =========== | | | */
|
||||
/* | | | */
|
||||
/* Promoted property / parameter | | | */
|
||||
|
@ -393,9 +396,6 @@ typedef struct _zend_oparray_context {
|
|||
/* supports opcache compile-time evaluation (funcs) | | | */
|
||||
#define ZEND_ACC_COMPILE_TIME_EVAL (1 << 27) /* | X | | */
|
||||
/* | | | */
|
||||
/* has #[\Override] attribute | | | */
|
||||
#define ZEND_ACC_OVERRIDE (1 << 28) /* | X | | */
|
||||
/* | | | */
|
||||
/* Has IS_PTR operands that needs special cleaning; same | | | */
|
||||
/* value as ZEND_ACC_OVERRIDE but override is for class | | | */
|
||||
/* methods and this is for the top level op array | | | */
|
||||
|
|
|
@ -1563,6 +1563,8 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
|
|||
ZSTR_VAL(key),
|
||||
ZSTR_VAL(parent_info->ce->name));
|
||||
}
|
||||
|
||||
child_info->flags &= ~ZEND_ACC_OVERRIDE;
|
||||
}
|
||||
} else {
|
||||
zend_function **hooks = parent_info->hooks;
|
||||
|
@ -2315,13 +2317,11 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry
|
|||
|
||||
void zend_inheritance_check_override(const zend_class_entry *ce)
|
||||
{
|
||||
zend_function *f;
|
||||
|
||||
if (ce->ce_flags & ZEND_ACC_TRAIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, f) {
|
||||
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, zend_function *f) {
|
||||
if (f->common.fn_flags & ZEND_ACC_OVERRIDE) {
|
||||
ZEND_ASSERT(f->type != ZEND_INTERNAL_FUNCTION);
|
||||
|
||||
|
@ -2332,14 +2332,17 @@ void zend_inheritance_check_override(const zend_class_entry *ce)
|
|||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
if (ce->num_hooked_props) {
|
||||
zend_property_info *prop;
|
||||
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) {
|
||||
if (!prop->hooks) {
|
||||
continue;
|
||||
}
|
||||
ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, zend_property_info *prop) {
|
||||
if (prop->flags & ZEND_ACC_OVERRIDE) {
|
||||
zend_error_noreturn(
|
||||
E_COMPILE_ERROR,
|
||||
"%s::$%s has #[\\Override] attribute, but no matching parent property exists",
|
||||
ZSTR_VAL(ce->name), zend_get_unmangled_property_name(prop->name));
|
||||
}
|
||||
|
||||
if (prop->hooks) {
|
||||
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
|
||||
f = prop->hooks[i];
|
||||
zend_function *f = prop->hooks[i];
|
||||
if (f && f->common.fn_flags & ZEND_ACC_OVERRIDE) {
|
||||
ZEND_ASSERT(f->type != ZEND_INTERNAL_FUNCTION);
|
||||
|
||||
|
@ -2349,8 +2352,8 @@ void zend_inheritance_check_override(const zend_class_entry *ce)
|
|||
ZEND_FN_SCOPE_NAME(f), ZSTR_VAL(f->common.function_name));
|
||||
}
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue