taxicala
taxicala

Reputation: 21759

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

I know that private visibility in most of the OOP languages (if not all) define privacy in a class basis, i.e. different instances of the same class, can access private properties/methods of each other.

I want to prevent this and I want to know what is the best design/implementation in order to do this without a negative performance impact.

For example, I know that I could implement an AOP and use notations, but this would lead to a performance decrease since the languange engine would have to create the reflection of the class and check the annotation. So, basically, my question is, what is the best way to avoid instances of the same class to access each other's private methods/properties?

Example:

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

Upvotes: 6

Views: 875

Answers (4)

SOFe
SOFe

Reputation: 8214

Don't access any properties at all without getters and setters. Then in your getters and setters, check if the calling context is the same class through (debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)[0]["object"] ?? null) === $this.

For example:

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

Of course, your getBar and setBar can be replaced by __get() and __set(), but then you must not declare the field, or the magic methods will not be called.

Upvotes: 0

Daan
Daan

Reputation: 12236

You can also achieve this with the 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);

Upvotes: 1

SerCrAsH
SerCrAsH

Reputation: 450

Im not sure but :

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

Upvotes: 0

deceze
deceze

Reputation: 522024

Simply don't write code which accesses other entities' privates, period. The visibility modifiers are there to help you not shoot yourself in the foot too easily. They're not a lock and key. There are any number of ways in which you can still "circumvent" "access protection". Just be a responsible adult and not modify properties except when you write a $this-> before it.

Upvotes: 5

Related Questions