Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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.0alpha3

Expand Down
8 changes: 4 additions & 4 deletions Zend/tests/lazy_objects/skipLazyInitialization.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,15 @@ getValue(): NULL
setRawValueWithoutLazyInitialization():
getValue(): string(5) "value"

## Property [ public $hooked = NULL ]
## Property [ public $hooked = NULL { get; set; } ]

skipInitializerForProperty():
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
Expand Down Expand Up @@ -324,15 +324,15 @@ getValue(): NULL
setRawValueWithoutLazyInitialization():
getValue(): string(5) "value"

## Property [ public $hooked = NULL ]
## Property [ public $hooked = NULL { get; set; } ]

skipInitializerForProperty():
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
Expand Down
23 changes: 23 additions & 0 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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");
Expand Down
165 changes: 165 additions & 0 deletions ext/reflection/tests/ReflectionClass_toString_008.phpt
Original file line number Diff line number Diff line change
@@ -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 changes: 89 additions & 0 deletions ext/reflection/tests/ReflectionProperty_toString_001.phpt
Original file line number Diff line number Diff line change
@@ -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; } ]
4 changes: 2 additions & 2 deletions ext/reflection/tests/abstract_property_indicated.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]