ext/reflection: Make properties readonly (#24282)

This commit is contained in:
Kamil Tekiela authored and GitHub committed 2026-10-22 15:34:57 +01:00
1 parent 461eb0e8b8
commit eef0f71aa7
7 files changed
+142 -39

No files matched your search

+7
View File
@@ -29,6 +29,13 @@ PHP 8.7 UPGRADE NOTES
not present in the result set. It previously reported the condition
through PDO::ATTR_ERRMODE and returned false.
- Reflection:
. The public $name and $class properties of ReflectionClass,
ReflectionClassConstant, ReflectionProperty, ReflectionMethod,
ReflectionFunctionAbstract, ReflectionParameter, ReflectionExtension,
ReflectionZendExtension, ReflectionAttribute and ReflectionConstant are
now readonly.
- Session:
. Setting session.cookie_path to a value containing one of ";\r\n", or
session.cookie_domain to a value containing one of ",; \t\r\n\013\014",
+12 -12
View File
@@ -22,7 +22,7 @@ interface Reflector extends Stringable
/** @not-serializable */
abstract class ReflectionFunctionAbstract implements Reflector
{
public string $name;
public readonly string $name;
/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
@@ -179,7 +179,7 @@ class ReflectionMethod extends ReflectionFunctionAbstract
/** @cvalue ZEND_ACC_FINAL */
public const int IS_FINAL = UNKNOWN;
public string $class;
public readonly string $class;
public function __construct(object|string $objectOrMethod, ?string $method = null) {}
@@ -254,7 +254,7 @@ class ReflectionClass implements Reflector
/** @cvalue ZEND_LAZY_OBJECT_SKIP_DESTRUCTOR */
public const int SKIP_DESTRUCTOR = UNKNOWN;
public string $name;
public readonly string $name;
private function __clone(): void {}
@@ -473,8 +473,8 @@ class ReflectionProperty implements Reflector
/** @cvalue ZEND_ACC_FINAL */
public const int IS_FINAL = UNKNOWN;
public string $name;
public string $class;
public readonly string $name;
public readonly string $class;
/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
@@ -592,8 +592,8 @@ class ReflectionClassConstant implements Reflector
/** @cvalue ZEND_ACC_FINAL */
public const int IS_FINAL = UNKNOWN;
public string $name;
public string $class;
public readonly string $name;
public readonly string $class;
/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
@@ -642,7 +642,7 @@ class ReflectionClassConstant implements Reflector
/** @not-serializable */
class ReflectionParameter implements Reflector
{
public string $name;
public readonly string $name;
/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
@@ -756,7 +756,7 @@ class ReflectionIntersectionType extends ReflectionType
/** @not-serializable */
class ReflectionExtension implements Reflector
{
public string $name;
public readonly string $name;
/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
@@ -802,7 +802,7 @@ class ReflectionExtension implements Reflector
/** @not-serializable */
class ReflectionZendExtension implements Reflector
{
public string $name;
public readonly string $name;
/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
@@ -846,7 +846,7 @@ class ReflectionAttribute implements Reflector
/** @cvalue REFLECTION_ATTRIBUTE_IS_INSTANCEOF */
public const int IS_INSTANCEOF = UNKNOWN;
public string $name;
public readonly string $name;
public function getName(): string {}
public function inNamespace(): bool {}
@@ -918,7 +918,7 @@ final class ReflectionFiber
*/
class ReflectionConstant implements Reflector
{
public string $name;
public readonly string $name;
public function __construct(string $name) {}
+13 -13
View File
@@ -1,5 +1,5 @@
/* This is a generated file, edit php_reflection.stub.php instead.
* Stub hash: c4dcc2653f826c2c437065faec4bf77772ef88b1
* Stub hash: 6191049a28b4c5fe134fc37cd8969a33d00c51de
* Has decl header: yes */
#include "zend_attributes.h"
@@ -1442,7 +1442,7 @@ static zend_class_entry *register_class_ReflectionFunctionAbstract(zend_class_en
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
return class_entry;
}
@@ -1526,7 +1526,7 @@ static zend_class_entry *register_class_ReflectionMethod(zend_class_entry *class
zval property_class_default_value;
ZVAL_UNDEF(&property_class_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CLASS), &property_class_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CLASS), &property_class_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_attribute *attribute_Deprecated_func_setaccessible_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setaccessible", sizeof("setaccessible") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2);
@@ -1585,7 +1585,7 @@ static zend_class_entry *register_class_ReflectionClass(zend_class_entry *class_
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
return class_entry;
}
@@ -1687,11 +1687,11 @@ static zend_class_entry *register_class_ReflectionProperty(zend_class_entry *cla
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_class_default_value;
ZVAL_UNDEF(&property_class_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CLASS), &property_class_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CLASS), &property_class_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_attribute *attribute_Deprecated_func_setaccessible_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setaccessible", sizeof("setaccessible") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2);
@@ -1738,11 +1738,11 @@ static zend_class_entry *register_class_ReflectionClassConstant(zend_class_entry
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_class_default_value;
ZVAL_UNDEF(&property_class_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CLASS), &property_class_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CLASS), &property_class_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
return class_entry;
}
@@ -1757,7 +1757,7 @@ static zend_class_entry *register_class_ReflectionParameter(zend_class_entry *cl
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_attribute *attribute_Deprecated_func_getclass_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "getclass", sizeof("getclass") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2);
@@ -1833,7 +1833,7 @@ static zend_class_entry *register_class_ReflectionExtension(zend_class_entry *cl
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
return class_entry;
}
@@ -1848,7 +1848,7 @@ static zend_class_entry *register_class_ReflectionZendExtension(zend_class_entry
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
return class_entry;
}
@@ -1879,7 +1879,7 @@ static zend_class_entry *register_class_ReflectionAttribute(zend_class_entry *cl
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
return class_entry;
}
@@ -1934,7 +1934,7 @@ static zend_class_entry *register_class_ReflectionConstant(zend_class_entry *cla
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
return class_entry;
}
+4 -4
View File
@@ -1,12 +1,12 @@
/* This is a generated file, edit php_reflection.stub.php instead.
* Stub hash: c4dcc2653f826c2c437065faec4bf77772ef88b1 */
* Stub hash: 6191049a28b4c5fe134fc37cd8969a33d00c51de */
#ifndef ZEND_PHP_REFLECTION_DECL_c4dcc2653f826c2c437065faec4bf77772ef88b1_H
#define ZEND_PHP_REFLECTION_DECL_c4dcc2653f826c2c437065faec4bf77772ef88b1_H
#ifndef ZEND_PHP_REFLECTION_DECL_6191049a28b4c5fe134fc37cd8969a33d00c51de_H
#define ZEND_PHP_REFLECTION_DECL_6191049a28b4c5fe134fc37cd8969a33d00c51de_H
typedef enum zend_enum_PropertyHookType {
ZEND_ENUM_PropertyHookType_Get = 1,
ZEND_ENUM_PropertyHookType_Set = 2,
} zend_enum_PropertyHookType;
#endif /* ZEND_PHP_REFLECTION_DECL_c4dcc2653f826c2c437065faec4bf77772ef88b1_H */
#endif /* ZEND_PHP_REFLECTION_DECL_6191049a28b4c5fe134fc37cd8969a33d00c51de_H */
@@ -10,19 +10,18 @@ class Test {
// This is unsupported and the actual behavior doesn't matter.
// Just make sure it doesn't crash.
$rc = new ReflectionClassConstant(Test::class, 'C');
unset($rc->name);
try {
var_dump($rc->getName());
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
echo $rc, "\n";
unset($rc->name);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($rc->getName());
echo $rc, "\n";
?>
--EXPECT--
Typed property ReflectionClassConstant::$name must not be accessed before initialization
Typed property ReflectionClassConstant::$name must not be accessed before initialization
Cannot unset readonly property ReflectionClassConstant::$name
string(1) "C"
Constant [ public int C ] { 1 }
@@ -27,7 +27,7 @@ Class [ <internal:Reflection> class ReflectionClass implements Stringable, Refle
}
- Properties [1] {
Property [ public string $name ]
Property [ public protected(set) readonly string $name ]
}
- Methods [64] {
+97
View File
@@ -0,0 +1,97 @@
--TEST--
GH-24147: Reflection $name and $class properties cannot be modified
--FILE--
<?php
class B {
public string $x;
}
function byRef(&$x) { $x = 'Z'; }
$tests = [
'compound assignment' => function ($r) { $r->class .= 'Z'; },
'reference' => function ($r) { $ref = &$r->name; $ref = 'Z'; },
'string offset' => function ($r) { $r->name[0] = 'Z'; },
'by-ref argument' => function ($r) { byRef($r->class); },
'dynamic by-ref argument' => function ($r) { $fn = 'byRef'; $fn($r->class); },
'foreach by reference' => function ($r) { foreach ($r as &$v) { $v = 'Z'; } },
'unset' => function ($r) { unset($r->name); },
'setValue' => function ($r) { (new ReflectionProperty($r, 'name'))->setValue($r, 'Z'); },
'setRawValue' => function ($r) { (new ReflectionProperty($r, 'name'))->setRawValue($r, 'Z'); },
];
foreach ($tests as $label => $test) {
$r = new ReflectionProperty('B', 'x');
try {
$test($r);
echo "$label: no error\n";
} catch (Throwable $e) {
echo "$label: ", $e::class, ": ", $e->getMessage(), "\n";
}
if (!isset($r->name, $r->class) || $r->name !== $r->getName() || $r->class !== $r->getDeclaringClass()->name) {
echo "$label: properties diverged from getters\n";
}
}
class WriteBeforeConstruct extends ReflectionClass {
public function __construct() {
$this->name = 'Z';
}
}
class UnsetAfterConstruct extends ReflectionClass {
public function __construct() {
parent::__construct('B');
unset($this->name);
}
}
foreach (['WriteBeforeConstruct', 'UnsetAfterConstruct'] as $class) {
try {
new $class();
echo "$class: no error\n";
} catch (Throwable $e) {
echo "$class: ", $e::class, ": ", $e->getMessage(), "\n";
}
}
// Unsetting the uninitialized property in a subclass to route reads through __get() stays possible
class UnsetBeforeConstruct extends ReflectionClass {
public function __construct() {
unset($this->name);
}
public function __get($name) {
return "magic $name";
}
}
var_dump((new UnsetBeforeConstruct())->name);
foreach (['setValue', 'setRawValue'] as $method) {
$r = (new ReflectionClass('ReflectionClass'))->newInstanceWithoutConstructor();
try {
(new ReflectionProperty($r, 'name'))->$method($r, 'Z');
echo "uninitialized $method: no error\n";
} catch (Throwable $e) {
echo "uninitialized $method: ", $e::class, ": ", $e->getMessage(), "\n";
}
}
?>
--EXPECT--
compound assignment: ReflectionException: Cannot set read-only property ReflectionProperty::$class
reference: Error: Cannot indirectly modify readonly property ReflectionProperty::$name
string offset: Error: Cannot indirectly modify readonly property ReflectionProperty::$name
by-ref argument: Error: Cannot indirectly modify readonly property ReflectionProperty::$class
dynamic by-ref argument: Error: Cannot indirectly modify readonly property ReflectionProperty::$class
foreach by reference: Error: Cannot acquire reference to readonly property ReflectionProperty::$name
unset: Error: Cannot unset readonly property ReflectionProperty::$name
setValue: ReflectionException: Cannot set read-only property ReflectionProperty::$name
setRawValue: ReflectionException: Cannot set read-only property ReflectionProperty::$name
WriteBeforeConstruct: ReflectionException: Cannot set read-only property WriteBeforeConstruct::$name
UnsetAfterConstruct: Error: Cannot unset readonly property ReflectionClass::$name
string(10) "magic name"
uninitialized setValue: ReflectionException: Cannot set read-only property ReflectionClass::$name
uninitialized setRawValue: ReflectionException: Cannot set read-only property ReflectionClass::$name