如何使用反射在PHP中通过引用获取属性值?

3
假设你有一个这样声明的类:
class DummyObject{
    public $attr;
    public function __construct(){
        $this->attr=array('val_1','val_2','val_3');
    }
}

做这个:

$obj=(new DummyObject1());
$attr=&$obj->attr;

您将获得对$attr的引用,因此在数组中进行的任何修改也将在DummyObject $obj实例中进行。

现在,最后一个问题。使用反射,如何获取对存储在$attr中的数组的引用而不是副本?我尝试过但没有成功:

$obj=(new DummyObject());
$reflector = new ReflectionObject($obj);
$reflectorProperty = $reflector->getProperty('attr');
$propertyValue=$reflectorProperty->getValue($ref);

事实上,$attr是原始数组的副本。
提前感谢!

1
请问您的意图是什么?我的意思是,您已经有了对象、属性名称和该属性是公共的。为什么需要使用反射来获取该属性? - Yoshi
我能猜测的唯一意图就是让代码变得复杂。 - xdazz
我的意图并不是让代码变复杂。 我有一个对象,它可以引用其他对象,这些对象从JSON的HTTP响应中使用json_decode获取,我需要递归地遍历所有属性,以对存储字符串的所有属性进行一些更改。 - jLuengas
可能有更简单的方法来完成这个,你能展示一下 JSON 代码吗? - Yoshi
3个回答

9

从PHP 5.4开始,您可以在不使用反射的情况下完成此操作:

class Kitchen
{
    private $yummy = 'cake';
}

$reader = function & ($object, $property) {
    $value = & Closure::bind(function & () use ($property) {
        return $this->$property;
    }, $object, $object)->__invoke();

    return $value;
};

$kitchen = new Kitchen();
$cake    = & $reader($kitchen, 'yummy');
$cake    = 'sorry, I ate it!';

var_dump($kitchen);

这要归功于PHP 5.4在运行时切换闭包范围的能力。你可以在http://3v4l.org/sZMt1找到一个运行示例。我实际上已经详细解释了这个技术和最终的用例,可以参考http://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/

你为什么也将外部函数作为闭包?这似乎是不必要的。https://3v4l.org/4tAqu - donquixote
我刚刚注意到这对于“内部”类不起作用。显然。 https://3v4l.org/2BZRg “警告:无法将闭包绑定到内部类ReflectionClass的范围”,“致命错误:未捕获的错误:调用null上的成员函数__invoke()” - donquixote

1
很抱歉,您不能这样做。要实现这一点,ReflectionProperty::getValue必须通过引用返回,但它并没有这样做。

我也有这个怀疑。但是我注意到,如果ReflectionProperty::getValue返回的值是一个关联数组或另一个对象,它会返回引用,所以也许可以用同样的方式检索数组引用。 - jLuengas
@jLu 我不知道你的意思。根据我的观察,getValue 函数永远不会返回引用。 - Artefacto
我尝试过使用指向对象实例和关联数组的属性,然后ReflectionProperty :: getValue返回引用,因为可以在原始对象中看到返回的对象的更改。当属性指向数组时,这种行为并不相同。 感谢您的关注! - jLuengas

0

您可以从原始的 $obj 中获取它,然后通过 "use" 语句将其传递给回调函数。

$propertyValue = $obj->{$reflectorProperty->getName()};

如果你使用公共的getter/setter函数来访问私有成员,那么你可以:
$propertyName = $reflectorProperty->getName();
$methodName = 'get' . ucfirst($propertyName);
if (method_exists($obj, $methodName)) {
    $propertyValue = call_user_func([ $object, $methodName ]);
} elseif (isset($obj->{$propertyName}) {
    $propertyValue = $this->{$propertyName};
} else {
    $propertyValue = null;
}

另一种解决方案是定义私有(或公共)方法 $obj->getProperty($name),并通过反射从您的回调函数中调用它并设置 setAccessible。一旦该方法在原始类中定义,它就可以访问所有私有成员。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接