是否有任何方法,使用反射或其他方式来更改已设置的只读属性?
我们有时会在测试中这样做,并且我们不想仅仅为了测试目的而避免使用只读属性。
class Acme {
public function __construct(
private readonly int $changeMe,
) {}
}
$object= new Acme(1);
$reflectionClass = new ReflectionClass($object);
$reflectionProperty = $reflectionClass->getProperty('changeMe');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, 2);
Fatal error: Uncaught Error: Cannot modify readonly property Acme::$changeMe
我能想到的更改
readonly
属性的唯一方法是反映对象而不调用其构造函数。但是,不确定它在您的特定情况下是否有用
class Acme {
public function __construct(
public readonly int $changeMe,
) {}
}
$object = new Acme(1);
$reflection = new ReflectionClass($object);
$instance = $reflection->newInstanceWithoutConstructor();
$reflectionProperty = $reflection->getProperty('changeMe');
$reflectionProperty->setValue($instance, 33);
var_dump($reflectionProperty->getValue($instance)); // 33
注意:我们实际上并没有“更改”属性,我们只是第一次设置它,因为没有调用构造函数。
@Rain 是正确的。但要对此进行扩展,您需要做的是一次有效地克隆目标对象的一个属性,包括继承类的所有私有属性,除了您打算更改的属性。然后你就可以改变它。它看起来像这样:
function setValue(object &$object, string $propertyName, mixed $value): void
{
$objReflection = new ReflectionClass($object);
if ($objReflection->getProperty($propertyName)->isReadOnly()) {
$mutable = $objReflection->newInstanceWithoutConstructor();
do {
foreach ($objReflection->getProperties() as $property) {
if ($property->isInitialized($object) && $property->name != $propertyName) {
$objReflection->getProperty($property->name)->setValue($mutable, $property->getValue($object));
}
}
} while ($objReflection = $objReflection->getParentClass());
$object = $mutable;
}
$objReflection->getProperty($propertyName)->setValue($object, $value);
}