loading...
Cover image for Private parts exposed in public

Private parts exposed in public

belinde profile image Franco Traversaro ・1 min read

It's said that a good object keeps private or protected his properties, so nobody can harm the business logic. It's a wise advice, indeed, but sometimes we want to break things: maybe we need to gain access of a private connection managed by an external library, maybe we just want to see the world burn. An example of the first case happened to me a lot of years ago, when I was working on a strange Wordpress integration: I needed the underlying mysqli connection, but it was stored in a dbh property of the $wpdb singleton of class \wpdb. I couldn't edit the class to make dbh property public, but I needed the connection. What to do? We all know that a private or protected properties cannot be accessed from outside the instance through -> operator! A $object->privateProp will always fail!

That's not entirely true. Quite, but not entirely. There's a little detail that I think it's not so known: the access control to methods and properties is done checking the context from which we are working. So, if we are inside a method of the class, we can access private or protected properties of any instance of that class.

Enough talk, let's do some code. Imagine that we have a third-party library, that we cannot modify, that do this kind of things:

// begin read only code

class DeepThought {
    protected $theAnswer = 42;
    private $theQuestion = 'How many roads...';
}

$singleton = new DeepThought();

// end read only code

We disperately want to know theAnswer: we know that is protected, so we can work on it. We know that a protected property can be accessed from extended classes, so we write this piece of code:

class Towel extends DeepThought {
    public static function getTheAnswer(DeepThought $instance): int
    {
        return $instance->theAnswer;
    }
}

echo Towel::getTheAnswer($singleton);

Do you see the magic? Towel extends DeepThought, so can legitimately access his protected properties. When we call Towel::getTheAnswer() we are in the Towel context, so $instance->theAnswer is also legit.

But theAnswer is merely protected, and we also want to know theQuestion, that's private. Extending the class we still have not access to private properties... we need a bigger towel! :-)

Reflection comes to the rescue:

$refl = new ReflectionProperty(DeepThought::class, 'theQuestion');
$refl->setAccessible(true);

echo $refl->getValue($singleton);

Note that Reflection works also on protected properties, but I've presented it last for the sake of narration. We just instantiate a ReflectionProperty for theQuestion: that's like a tool that can inspect or edit some attributes without changing the code. After we use the reflection to make the property accessible, and lastly we apply our edited property description to the original object. It's like .call() in JavaScript.

That's all! With these two techniques you can tear apart any class to extract the gems inside. But stay wise! Objects with private properties often have good reasons to be designed that way. No developer take care of backward compatibility for private properties: as in real life, if you play with other's private parts without consent you can get in trouble!

Posted on Aug 8 '18 by:

Discussion

markdown guide
 

I don't know a lick of PHP, I just want to commend you for the best title ever.

 

Hey i like this trick. did not know :)