Skip to content

Commit b4c3b85

Browse files
committed
Disallow data classes in ReflectionProperty::setValue() and ReflectionMethod::invoke()
1 parent 59f1896 commit b4c3b85

File tree

4 files changed

+28
-100
lines changed

4 files changed

+28
-100
lines changed

ext/reflection/php_reflection.c

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3433,6 +3433,13 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
34333433
_DO_THROW("Given object is not an instance of the class this method was declared in");
34343434
RETURN_THROWS();
34353435
}
3436+
3437+
if (Z_OBJCE_P(object)->ce_flags & ZEND_ACC_DATA_CLASS) {
3438+
zend_throw_exception_ex(reflection_exception_ptr, 0,
3439+
"May not invoke mutating method \"%s::%s()\" through reflection",
3440+
ZSTR_VAL(Z_OBJCE_P(object)->name), ZSTR_VAL(mptr->common.function_name));
3441+
RETURN_THROWS();
3442+
}
34363443
}
34373444
/* Copy the zend_function when calling via handler (e.g. Closure::__invoke()) */
34383445
callback = _copy_function(mptr);
@@ -5744,7 +5751,6 @@ ZEND_METHOD(ReflectionProperty, setValue)
57445751
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &tmp, &value) == FAILURE) {
57455752
RETURN_THROWS();
57465753
}
5747-
ZVAL_DEREF(tmp);
57485754

57495755
if (Z_TYPE_P(tmp) != IS_NULL && Z_TYPE_P(tmp) != IS_OBJECT) {
57505756
zend_string *method_name = get_active_function_or_method_name();
@@ -5755,7 +5761,6 @@ ZEND_METHOD(ReflectionProperty, setValue)
57555761
}
57565762
}
57575763
} else {
5758-
ZVAL_DEREF(value);
57595764
zend_string *method_name = get_active_function_or_method_name();
57605765
zend_error(E_DEPRECATED, "Calling %s() with a single argument is deprecated", ZSTR_VAL(method_name));
57615766
zend_string_release(method_name);
@@ -5766,23 +5771,15 @@ ZEND_METHOD(ReflectionProperty, setValue)
57665771

57675772
zend_update_static_property_ex(intern->ce, ref->unmangled_name, value);
57685773
} else {
5769-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &object, &value) == FAILURE) {
5770-
RETURN_THROWS();
5771-
}
5772-
5773-
zval *orig_object = object;
5774-
ZVAL_DEREF(object);
5775-
if (Z_TYPE_P(object) != IS_OBJECT) {
5776-
zend_argument_type_error(1, "must be of type object, %s given", zend_zval_value_name(object));
5774+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oz", &object, &value) == FAILURE) {
57775775
RETURN_THROWS();
57785776
}
57795777

57805778
if (Z_OBJCE_P(object)->ce_flags & ZEND_ACC_DATA_CLASS) {
5781-
if (Z_TYPE_P(orig_object) != IS_REFERENCE) {
5782-
zend_throw_error(NULL, "Instance of data class %s must be passed by reference", ZSTR_VAL(Z_OBJCE_P(object)->name));
5783-
RETURN_THROWS();
5784-
}
5785-
SEPARATE_DATA_OBJ(object);
5779+
zend_throw_exception_ex(reflection_exception_ptr, 0,
5780+
"May not set property value of data class \"%s\" through reflection",
5781+
ZSTR_VAL(Z_OBJCE_P(object)->name));
5782+
RETURN_THROWS();
57865783
}
57875784

57885785
zend_update_property_ex(intern->ce, Z_OBJ_P(object), ref->unmangled_name, value);

ext/reflection/php_reflection.stub.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,10 +451,7 @@ public function getName(): string {}
451451
/** @tentative-return-type */
452452
public function getValue(?object $object = null): mixed {}
453453

454-
/**
455-
* @prefer-ref $objectOrValue
456-
* @tentative-return-type
457-
*/
454+
/** @tentative-return-type */
458455
public function setValue(mixed $objectOrValue, mixed $value = UNKNOWN): void {}
459456

460457
/** @tentative-return-type */

ext/reflection/php_reflection_arginfo.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/reflection/tests/ReflectionProperty_setValue_data_class.phpt

Lines changed: 13 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,95 +7,29 @@ data class Box {
77
public function __construct(
88
public $value,
99
) {}
10-
}
11-
12-
function getBoxByValue() {
13-
return new Box(1);
14-
}
1510

16-
$returnByReference = new Box(1);
17-
function &getBoxByReference() {
18-
global $returnByReference;
19-
return $returnByReference;
11+
public mutating function setNull() {
12+
$this->value = null;
13+
}
2014
}
2115

22-
const BOX = new Box(1);
23-
24-
class Prop {
25-
public function __construct(
26-
public $box,
27-
) {}
28-
}
29-
30-
class ReadonlyProp {
31-
public function __construct(
32-
public readonly Box $box,
33-
) {}
34-
}
35-
36-
$reflection = new ReflectionProperty(Box::class, 'value');
37-
38-
echo "CV\n";
3916
$box = new Box(1);
40-
$copy = $box;
41-
$reflection->setValue($box, 2);
42-
var_dump($box->value);
43-
var_dump($copy->value);
44-
45-
echo "\nReturn by value\n";
46-
try {
47-
$reflection->setValue(getBoxByValue(), 2);
48-
} catch (Error $e) {
49-
echo $e->getMessage(), "\n";
50-
}
51-
52-
echo "\nReturn by reference\n";
53-
$copy = getBoxByReference();
54-
$reflection->setValue(getBoxByReference(), 2);
55-
var_dump(getBoxByReference()->value);
56-
var_dump($copy->value);
5717

58-
echo "\nConst\n";
18+
$reflection = new ReflectionProperty(Box::class, 'value');
5919
try {
60-
$reflection->setValue(BOX, 2);
61-
} catch (Error $e) {
62-
echo $e->getMessage(), "\n";
20+
$reflection->setValue($box, 2);
21+
} catch (Exception $ex) {
22+
echo get_class($ex) . ': ' . $ex->getMessage(), "\n";
6323
}
6424

65-
echo "\nProp\n";
66-
$prop = new Prop(new Box(1));
67-
$copy = $prop->box;
68-
$reflection->setValue($prop->box, 2);
69-
var_dump($prop->box->value);
70-
var_dump($copy->value);
71-
72-
echo "\nReadonly prop\n";
73-
$readonlyProp = new ReadonlyProp(new Box(1));
25+
$reflection = new ReflectionMethod(Box::class, 'setNull');
7426
try {
75-
$reflection->setValue($readonlyProp->box, 2);
76-
} catch (Error $e) {
77-
echo $e->getMessage(), "\n";
27+
$reflection->invoke($box);
28+
} catch (Exception $ex) {
29+
echo get_class($ex) . ': ' . $ex->getMessage(), "\n";
7830
}
7931

8032
?>
8133
--EXPECT--
82-
CV
83-
int(2)
84-
int(1)
85-
86-
Return by value
87-
Instance of data class Box must be passed by reference
88-
89-
Return by reference
90-
int(2)
91-
int(1)
92-
93-
Const
94-
Instance of data class Box must be passed by reference
95-
96-
Prop
97-
int(2)
98-
int(1)
99-
100-
Readonly prop
101-
Cannot modify readonly property ReadonlyProp::$box
34+
ReflectionException: May not set property value of data class "Box" through reflection
35+
ReflectionException: May not invoke mutating method "Box::setNull()" through reflection

0 commit comments

Comments
 (0)