如何在 PHP 8.1 中使用反射更改只读属性?

发布于 2025-01-15 09:42:25 字数 570 浏览 3 评论 0原文

有没有办法使用反射或其他方式来更改已设置的只读属性?

我们有时会在测试中这样做,并且我们不想避免仅出于测试目的而使用只读属性。

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

Is there any way, using reflection or otherwise, to change a readonly property that has already been set?

We sometimes do that in tests, and we don't want to avoid using readonly props just for testing purposes.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

旧瑾黎汐 2025-01-22 09:42:25

我能想到的更改 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

https://3v4l.org/mis1l#v8.1.0< /a>

注意:我们实际上并没有“更改”属性,我们只是第一次设置它,因为没有调用构造函数。

The only way I can think of to change a readonly property is to reflect the object without calling its constructor. However, not sure if it's useful in your particular case

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

https://3v4l.org/mis1l#v8.1.0

Note: we are not actually "changing" the property we are just setting it for the first time since no constructor is called.

客…行舟 2025-01-22 09:42:25

@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);
}

@Rain is correct. But to expand on that a little, what you need to do is effectively clone the target object one property at a time, including all private properties of inherited classes, except the property you intend to alter. Then you can alter it. It would look something like this:

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文