Php/OOP如何避免同一类的实例访问其他对象的私有属性/方法


Php/OOP How to avoid instances of the same class to access private properties/methods from other objects

我知道大多数OOP语言(如果不是全部的话)中的私有可见性在类的基础上定义了隐私,即同一类的不同实例可以访问彼此的私有属性/方法。

我想防止这种情况发生,我想知道什么是最好的设计/实现,以便在不影响性能的情况下实现

例如,我知道我可以实现AOP并使用注释,但这会导致性能下降,因为语言引擎必须创建类的反射并检查注释。所以,基本上,我的问题是,避免同一类的实例访问彼此的私有方法/属性的最佳方法是什么?

示例:

class Product
{
    private $_prize;
    public function __construct($prize)
    {
        $this->_prize = $prize;
    }
    public function calculateDiscount(Product $extraProduct)
    {
        $extraProduct->_prize = 0; //How to avoid this?
    }
}
$productA = new Product(10);
$productB = new Product(25);
$productA->calculateDiscount($productB);

简单地说,不要编写访问其他实体私有的代码。可见性修改器可以帮助你不要太容易地射中自己的脚。它们不是锁和钥匙。您仍然可以通过多种方式"规避"访问保护"。做一个负责任的成年人,不要修改属性,除非你在它之前写了一个$this->

您也可以使用ReflectionClass 实现这一点

class Product
{
    private $_prize;
    public function __construct($prize)
    {
        $this->_prize = $prize;
    }
    public function calculateDiscount(Product $extraProduct)
    {
        if(!(new ReflectionClass($extraProduct))->getProperty('_prize')->isPrivate()){
            $extraProduct->_prize = 0; //How to avoid this?
        } else {
            echo "Is private property"; 
        }
    }
}
$productA = new Product(10);
$productB = new Product(25);
$productA->calculateDiscount($productB);

我不确定,但:

class Product
{
    private $_prize;
    public function __construct($prize)
    {
        $this->_prize = $prize;
    }
    public function calculateDiscount(Product $extraProduct)
    {
        $extraProduct->setPrize(0); 
    }
    public function setPrize( $v ) {
       $this->_prize = $v;
    }
}
$productA = new Product(10);
$productB = new Product(25);
$productA->calculateDiscount($productB);

如果没有getter和setter,就不要访问任何属性。然后在getter和setter中,通过(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)[0]["object"] ?? null) === $this检查调用上下文是否是同一个类。

例如:

class Foo{
    private $bar;
    public function getBar(){
        return $this->bar;
    }
    private function setBar($bar){
        self::assertCalledByThis();
        $this->bar = $bar;
    }
    private static function assertCalledByThis(){
        $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
        $fromObject = $trace[1]["object"] ?? null; // context calling setBar()
        $toObject = $trace[0]["object"] ?? null; // context calling assertCalledByThis()
        assert($fromObject === $toObject);
    }
}

当然,您的getBarsetBar可以用__get()和__set()代替,但您不能声明字段,否则将不会调用魔术方法。