mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
GH-17927: Indicate virtual properties and hooks in reflection output (#19297)
This commit is contained in:
3
NEWS
3
NEWS
@@ -2,6 +2,9 @@ PHP NEWS
|
||||
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||
?? ??? ????, PHP 8.5.0beta1
|
||||
|
||||
- Reflection:
|
||||
. Fixed bug GH-17927 (Reflection: have some indication of property hooks in
|
||||
`_property_string()`). (DanielEScherzer)
|
||||
|
||||
31 Jul 2025, PHP 8.5.0alpha4
|
||||
|
||||
|
||||
@@ -394,6 +394,11 @@ PHP 8.5 UPGRADE NOTES
|
||||
. The output of ReflectionClass::toString() for enums has changed to
|
||||
better indicate that the class is an enum, and that the enum cases
|
||||
are enum cases rather than normal class constants.
|
||||
. The output of ReflectionProperty::__toString() for properties with
|
||||
hooks has changed to indicate what hooks the property has, whether those
|
||||
hooks are final, and whether the property is virtual. This also affects
|
||||
the output of ReflectionClass::__toString() when a class contains hooked
|
||||
properties.
|
||||
|
||||
- Session:
|
||||
. session_start is stricter in regard to the option argument.
|
||||
|
||||
@@ -227,7 +227,7 @@ getValue(): NULL
|
||||
setRawValueWithoutLazyInitialization():
|
||||
getValue(): string(5) "value"
|
||||
|
||||
## Property [ public $hooked = NULL ]
|
||||
## Property [ public $hooked = NULL { get; set; } ]
|
||||
|
||||
skipInitializerForProperty():
|
||||
getValue(): NULL
|
||||
@@ -235,7 +235,7 @@ getValue(): NULL
|
||||
setRawValueWithoutLazyInitialization():
|
||||
getValue(): string(5) "value"
|
||||
|
||||
## Property [ public $virtual ]
|
||||
## Property [ public virtual $virtual { get; set; } ]
|
||||
|
||||
skipInitializerForProperty():
|
||||
ReflectionException: Can not use skipLazyInitialization on virtual property A::$virtual
|
||||
@@ -324,7 +324,7 @@ getValue(): NULL
|
||||
setRawValueWithoutLazyInitialization():
|
||||
getValue(): string(5) "value"
|
||||
|
||||
## Property [ public $hooked = NULL ]
|
||||
## Property [ public $hooked = NULL { get; set; } ]
|
||||
|
||||
skipInitializerForProperty():
|
||||
getValue(): NULL
|
||||
@@ -332,7 +332,7 @@ getValue(): NULL
|
||||
setRawValueWithoutLazyInitialization():
|
||||
getValue(): string(5) "value"
|
||||
|
||||
## Property [ public $virtual ]
|
||||
## Property [ public virtual $virtual { get; set; } ]
|
||||
|
||||
skipInitializerForProperty():
|
||||
ReflectionException: Can not use skipLazyInitialization on virtual property A::$virtual
|
||||
|
||||
@@ -1037,6 +1037,9 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha
|
||||
if (prop->flags & ZEND_ACC_READONLY) {
|
||||
smart_str_appends(str, "readonly ");
|
||||
}
|
||||
if (prop->flags & ZEND_ACC_VIRTUAL) {
|
||||
smart_str_appends(str, "virtual ");
|
||||
}
|
||||
if (ZEND_TYPE_IS_SET(prop->type)) {
|
||||
zend_string *type_str = zend_type_to_string(prop->type);
|
||||
smart_str_append(str, type_str);
|
||||
@@ -1054,6 +1057,26 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha
|
||||
smart_str_appends(str, " = ");
|
||||
format_default_value(str, default_value);
|
||||
}
|
||||
if (prop->hooks != NULL) {
|
||||
smart_str_appends(str, " {");
|
||||
const zend_function *get_hooked = prop->hooks[ZEND_PROPERTY_HOOK_GET];
|
||||
if (get_hooked != NULL) {
|
||||
if (get_hooked->common.fn_flags & ZEND_ACC_FINAL) {
|
||||
smart_str_appends(str, " final get;");
|
||||
} else {
|
||||
smart_str_appends(str, " get;");
|
||||
}
|
||||
}
|
||||
const zend_function *set_hooked = prop->hooks[ZEND_PROPERTY_HOOK_SET];
|
||||
if (set_hooked != NULL) {
|
||||
if (set_hooked->common.fn_flags & ZEND_ACC_FINAL) {
|
||||
smart_str_appends(str, " final set;");
|
||||
} else {
|
||||
smart_str_appends(str, " set;");
|
||||
}
|
||||
}
|
||||
smart_str_appends(str, " }");
|
||||
}
|
||||
}
|
||||
|
||||
smart_str_appends(str, " ]\n");
|
||||
|
||||
165
ext/reflection/tests/ReflectionClass_toString_008.phpt
Normal file
165
ext/reflection/tests/ReflectionClass_toString_008.phpt
Normal file
@@ -0,0 +1,165 @@
|
||||
--TEST--
|
||||
Using ReflectionClass::__toString() with hooked properties (GH-17927)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface IHookedDemo {
|
||||
public mixed $getOnly { get; }
|
||||
public mixed $setOnly { set; }
|
||||
public mixed $both { get; set; }
|
||||
}
|
||||
abstract class HookedDemo {
|
||||
abstract public mixed $getOnly { get; }
|
||||
abstract public mixed $setOnly { set; }
|
||||
abstract public mixed $both { get; set; }
|
||||
}
|
||||
class WithHooks {
|
||||
public mixed $getOnly {
|
||||
get => "always this string";
|
||||
}
|
||||
public mixed $setOnly {
|
||||
set => strtolower($value);
|
||||
}
|
||||
public mixed $both {
|
||||
get => $this->prop3;
|
||||
set => strtolower($value);
|
||||
}
|
||||
}
|
||||
class WithFinalHooks {
|
||||
public mixed $getOnly {
|
||||
final get => "always this string";
|
||||
}
|
||||
public mixed $setOnly {
|
||||
final set => strtolower($value);
|
||||
}
|
||||
public mixed $both {
|
||||
final get => $this->prop3;
|
||||
final set => strtolower($value);
|
||||
}
|
||||
}
|
||||
class WithMixedHooks {
|
||||
public mixed $getIsFinal {
|
||||
final get => "always this string";
|
||||
set => strtolower($value);
|
||||
}
|
||||
public mixed $setIsFinal {
|
||||
get => $this->setIsFinal;
|
||||
final set => strtolower($value);
|
||||
}
|
||||
}
|
||||
$classes = [
|
||||
IHookedDemo::class,
|
||||
HookedDemo::class,
|
||||
WithHooks::class,
|
||||
WithFinalHooks::class,
|
||||
WithMixedHooks::class,
|
||||
];
|
||||
foreach ( $classes as $clazz ) {
|
||||
echo new ReflectionClass( $clazz );
|
||||
}
|
||||
?>
|
||||
--EXPECTF--
|
||||
Interface [ <user> <iterateable> interface IHookedDemo ] {
|
||||
@@ %s %d-%d
|
||||
|
||||
- Constants [0] {
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
}
|
||||
|
||||
- Static methods [0] {
|
||||
}
|
||||
|
||||
- Properties [3] {
|
||||
Property [ abstract public virtual mixed $getOnly { get; } ]
|
||||
Property [ abstract public virtual mixed $setOnly { set; } ]
|
||||
Property [ abstract public virtual mixed $both { get; set; } ]
|
||||
}
|
||||
|
||||
- Methods [0] {
|
||||
}
|
||||
}
|
||||
Class [ <user> <iterateable> abstract class HookedDemo ] {
|
||||
@@ %s %d-%d
|
||||
|
||||
- Constants [0] {
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
}
|
||||
|
||||
- Static methods [0] {
|
||||
}
|
||||
|
||||
- Properties [3] {
|
||||
Property [ abstract public virtual mixed $getOnly { get; } ]
|
||||
Property [ abstract public virtual mixed $setOnly { set; } ]
|
||||
Property [ abstract public virtual mixed $both { get; set; } ]
|
||||
}
|
||||
|
||||
- Methods [0] {
|
||||
}
|
||||
}
|
||||
Class [ <user> <iterateable> class WithHooks ] {
|
||||
@@ %s %d-%d
|
||||
|
||||
- Constants [0] {
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
}
|
||||
|
||||
- Static methods [0] {
|
||||
}
|
||||
|
||||
- Properties [3] {
|
||||
Property [ public virtual mixed $getOnly { get; } ]
|
||||
Property [ public mixed $setOnly { set; } ]
|
||||
Property [ public mixed $both { get; set; } ]
|
||||
}
|
||||
|
||||
- Methods [0] {
|
||||
}
|
||||
}
|
||||
Class [ <user> <iterateable> class WithFinalHooks ] {
|
||||
@@ %s %d-%d
|
||||
|
||||
- Constants [0] {
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
}
|
||||
|
||||
- Static methods [0] {
|
||||
}
|
||||
|
||||
- Properties [3] {
|
||||
Property [ public virtual mixed $getOnly { final get; } ]
|
||||
Property [ public mixed $setOnly { final set; } ]
|
||||
Property [ public mixed $both { final get; final set; } ]
|
||||
}
|
||||
|
||||
- Methods [0] {
|
||||
}
|
||||
}
|
||||
Class [ <user> <iterateable> class WithMixedHooks ] {
|
||||
@@ %s %d-%d
|
||||
|
||||
- Constants [0] {
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
}
|
||||
|
||||
- Static methods [0] {
|
||||
}
|
||||
|
||||
- Properties [2] {
|
||||
Property [ public mixed $getIsFinal { final get; set; } ]
|
||||
Property [ public mixed $setIsFinal { get; final set; } ]
|
||||
}
|
||||
|
||||
- Methods [0] {
|
||||
}
|
||||
}
|
||||
89
ext/reflection/tests/ReflectionProperty_toString_001.phpt
Normal file
89
ext/reflection/tests/ReflectionProperty_toString_001.phpt
Normal file
@@ -0,0 +1,89 @@
|
||||
--TEST--
|
||||
Using ReflectionProperty::__toString() with hooked properties (GH-17927)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
interface IHookedDemo {
|
||||
public mixed $getOnly { get; }
|
||||
public mixed $setOnly { set; }
|
||||
public mixed $both { get; set; }
|
||||
}
|
||||
abstract class HookedDemo {
|
||||
abstract public mixed $getOnly { get; }
|
||||
abstract public mixed $setOnly { set; }
|
||||
abstract public mixed $both { get; set; }
|
||||
}
|
||||
class WithHooks {
|
||||
public mixed $getOnly {
|
||||
get => "always this string";
|
||||
}
|
||||
public mixed $setOnly {
|
||||
set => strtolower($value);
|
||||
}
|
||||
public mixed $both {
|
||||
get => $this->prop3;
|
||||
set => strtolower($value);
|
||||
}
|
||||
}
|
||||
class WithFinalHooks {
|
||||
public mixed $getOnly {
|
||||
final get => "always this string";
|
||||
}
|
||||
public mixed $setOnly {
|
||||
final set => strtolower($value);
|
||||
}
|
||||
public mixed $both {
|
||||
final get => $this->prop3;
|
||||
final set => strtolower($value);
|
||||
}
|
||||
}
|
||||
class WithMixedHooks {
|
||||
public mixed $getIsFinal {
|
||||
final get => "always this string";
|
||||
set => strtolower($value);
|
||||
}
|
||||
public mixed $setIsFinal {
|
||||
get => $this->setIsFinal;
|
||||
final set => strtolower($value);
|
||||
}
|
||||
}
|
||||
$classes = [
|
||||
IHookedDemo::class,
|
||||
HookedDemo::class,
|
||||
WithHooks::class,
|
||||
WithFinalHooks::class,
|
||||
WithMixedHooks::class,
|
||||
];
|
||||
foreach ( $classes as $clazz ) {
|
||||
echo "$clazz:\n";
|
||||
$ref = new ReflectionClass( $clazz );
|
||||
foreach ( $ref->getProperties() as $prop ) {
|
||||
echo $prop;
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
IHookedDemo:
|
||||
Property [ abstract public virtual mixed $getOnly { get; } ]
|
||||
Property [ abstract public virtual mixed $setOnly { set; } ]
|
||||
Property [ abstract public virtual mixed $both { get; set; } ]
|
||||
|
||||
HookedDemo:
|
||||
Property [ abstract public virtual mixed $getOnly { get; } ]
|
||||
Property [ abstract public virtual mixed $setOnly { set; } ]
|
||||
Property [ abstract public virtual mixed $both { get; set; } ]
|
||||
|
||||
WithHooks:
|
||||
Property [ public virtual mixed $getOnly { get; } ]
|
||||
Property [ public mixed $setOnly { set; } ]
|
||||
Property [ public mixed $both { get; set; } ]
|
||||
|
||||
WithFinalHooks:
|
||||
Property [ public virtual mixed $getOnly { final get; } ]
|
||||
Property [ public mixed $setOnly { final set; } ]
|
||||
Property [ public mixed $both { final get; final set; } ]
|
||||
|
||||
WithMixedHooks:
|
||||
Property [ public mixed $getIsFinal { final get; set; } ]
|
||||
Property [ public mixed $setIsFinal { get; final set; } ]
|
||||
@@ -31,12 +31,12 @@ Class [ <user> <iterateable> abstract class Demo ] {
|
||||
}
|
||||
|
||||
- Properties [2] {
|
||||
Property [ abstract public $a ]
|
||||
Property [ abstract public virtual $a { get; } ]
|
||||
Property [ public $b = NULL ]
|
||||
}
|
||||
|
||||
- Methods [0] {
|
||||
}
|
||||
}
|
||||
Property [ abstract public $a ]
|
||||
Property [ abstract public virtual $a { get; } ]
|
||||
Property [ public $b = NULL ]
|
||||
|
||||
Reference in New Issue
Block a user