PHP中的常量不可修改对象引用


Constant non-modifying object reference in PHP

这不是一个关于实际代码的问题,只是供讨论。在我看来,能够将对象作为常量引用传递是很好的。

$c = Coordinate;
unsafe_func($c); // this should be ok
unsafe_func(... constant reference ... $c); // this should fail on write to $c

我知道有一个选项可以传递像set(clone $c)这样的对象的克隆,这将使我的原始$c不被unsafe_func()修改,但是如果我想有一个单一的对象实例呢?

我想到的是一个包装器类(代码:https://gist.github.com/904897)使用__set()__get()魔术方法,在每个__set()调用上抛出异常。它允许我这样做:

class CoordinateConst extends stdConst {};
unsafe_func(new CoordinateConst($c)); // exception: can not be modified!

我不喜欢的解决方案是,通过引入CoordinateConst,我必须删除坐标类型提示。但是,如果我扩展坐标类,__set()方法不调用可见属性。

问题:是否有更常见的方法来做到这一点,或者你宁愿避免这样的代码在任何情况下(通过传递对象克隆,包装unsafe_func调用或其他东西)?

您的解决方案有效地防止了公共属性被写入。如果坐标有如下函数,那么它就不是常量引用:

public function setX($x) {
    $this->x = $x;
}

另一种选择(但需要更多的代码,这取决于您想要执行的频率)是创建类的只读版本。

我知道在Java和c#中,它通常应用于集合。c#有一个ReadOnlyCollection<T>,它只是一个包装器/代理,就像你在那里做的一样。

所以我解决这个问题的方法是创建:

class ReadOnlyCoordinate { 
    private $coordinate;
    public function __construct(Coordinate $coordinate) {
        $this->coordinate = $coordinate;
    }
    public function getX() {
        return $this->coordinate->getX();
    }

当然,您可以扩展Coordinate,然后在设置任何属性的函数上抛出异常。


然后从整体上看这个问题,如果你不想让Coordinate成员/setter暴露给unsafe_func,那么也许你只能发送所需的数据。这当然取决于它需要多少坐标数据。

unsafe_func($coordinate->getRequiredData());

但是如果你需要大量的数据,这将成为一个麻烦