Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 12 additions & 12 deletions ext/reflection/php_reflection.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,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 {}
Expand Down Expand Up @@ -182,7 +182,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) {}

Expand Down Expand Up @@ -256,7 +256,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 {}

Expand Down Expand Up @@ -475,8 +475,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 {}
Expand Down Expand Up @@ -587,8 +587,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 {}
Expand Down Expand Up @@ -637,7 +637,7 @@ public function getType(): ?ReflectionType {}
/** @not-serializable */
class ReflectionParameter implements Reflector
{
public string $name;
public readonly string $name;

/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
Expand Down Expand Up @@ -749,7 +749,7 @@ public function getTypes(): array {}
/** @not-serializable */
class ReflectionExtension implements Reflector
{
public string $name;
public readonly string $name;

/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
Expand Down Expand Up @@ -795,7 +795,7 @@ public function isTemporary(): bool {}
/** @not-serializable */
class ReflectionZendExtension implements Reflector
{
public string $name;
public readonly string $name;

/** @implementation-alias ReflectionClass::__clone */
private function __clone(): void {}
Expand Down Expand Up @@ -839,7 +839,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 getTarget(): int {}
Expand Down Expand Up @@ -908,7 +908,7 @@ public function getTrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT): array {
*/
final class ReflectionConstant implements Reflector
{
public string $name;
public readonly string $name;

public function __construct(string $name) {}

Expand Down
26 changes: 13 additions & 13 deletions ext/reflection/php_reflection_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 8 additions & 9 deletions ext/reflection/tests/ReflectionClassConstant_unset_name.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
2 changes: 1 addition & 1 deletion ext/reflection/tests/ReflectionClass_toString_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
Expand Down
97 changes: 97 additions & 0 deletions ext/reflection/tests/gh24147.phpt
Original file line number Diff line number Diff line change
@@ -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
Loading