Click to share! ⬇️

encapsulation for hackers

Encapsulation introduces yet another fancy word to your object oriented vocabulary. You might think we’re launching rockets with all the verbiage that comes along with programming in this style. In fact though, this is not rocket science at all! It is actually fairly straight forward if you simply take the time to read through some good tutorials, watch some great screencasts, and test things out for yourself. In this tutorial, we’ll build on the idea of getters and setters and how we can use Encapsulation for information hiding and to define public APIs for consumers of our class.

Hacking The Harley

In our getters and setters tutorial, we had a class that created a motorcycle. Recall that by the end of that investigation, we had a small class that had two properties of $make and $tires. We also had created two dedicated getter and setter methods for securely setting the type of tires on our motorcycle, as well as returning the type of tires in a specific format. We did this by way of hooking into the getter and setter logic to add behaviors and data checking. We found that our crafty competitor was no longer able to hack our tires and put the poorly performing Cooper brand on our race-tuned Harley. When RacerX tries to hack us with $motorcycle->setTires('Cooper'), he fails. Did we really accomplish our goal though? Hmm…

<?php
class Motorcycle {
    public $make;
    
    public $tires;
    
    public function __construct($make) {
        $this->make = $make;
    }
    
    public function setTires($tires) {
        if($tires == 'Cooper') {
            throw new Exception('Oh hell no!');
        } else {
            $this->tires = $tires;
        }
    }
    
    public function getTires() {
        return strtoupper($this->tires);
    }
}

$motorcycle = new Motorcycle('Harley');

$motorcycle->tires = 'Cooper';

echo $motorcycle->getTires();
// COOPER

Ut oh. Do you see that right there? Our Harley is now sporting a pair of wretched rubber that will lead to bad results in our upcoming race. How did that happen? Well, here is where encapsulation comes in. RacerX was able to continue to access our property directly, because we left it’s access modifier set to public. WTF? What is an access modifier!? Let’s investigate.

  • Public Access Modifier

    Public is the least restrictive as it says that anyone or any class can access the property or method.

  • Protected Access Modifier

    Protected is a little bit more secure. It says that only the current class and any child or subclasses of the class will have access to the field or method.

  • Private Access Modifier

    Finally, we have Private which is the most secure and offers only the current class to have access to the field or method.

Now that we have a little bit in the way of definitions for the public, protected, and private access modifiers, let’s apply them to our code and see what happens. First, we’ll move to a protected instance of tires, and test things out.

In our source code we change

public $tires

to

protected $tires;

Then the malicious competitor comes along and tries to change our tires by directly accessing our property like so

$motorcycle->tires = 'Cooper';

However, we have protected ourselves! PHP now throws an exception to this!

// Fatal error: Cannot access protected property Motorcycle::$tires

Our racer enemy is not quick to give up, however. He decides that he’ll simply try to apply those Cooper tires to our trusty Harley by using the setTires setter method that we have configured for our class. Recall though that he will fail miserably as we have built-in some logic to ensure this can not happen!

$motorcycle->setTires('Cooper');
// Exception: Oh hell no!

Currently, we have Dunlop tires on our Harley. The word on the street however is that Bridgestone tires are the hot new thing and we want to be able to test them out for ourselves. Will we be able to change our tires to Bridgestone successfully?

$motorcycle->setTires('Bridgestone');

echo $motorcycle->getTires();
// BRIDGESTONE

In fact yes, yes we can. By making use of Encapsulation along with good use of getters and setters, we have successfully built a class that is able to blacklist certain types of tires from being mounted on our rims. We can still however mount the better performing tires anytime we want since we allow for that in the logic of our setter.

Let us now create a child class of our Motorcycle class, and try setting and getting the tires of the child. We will create a Minicycle class that extends the Motorcycle class.

class Minicycle extends Motorcycle {}

$minicycle = new Minicycle('Cobra');

$minicycle->setTires('Cooper');
// Exception: Oh hell no!

Since the Minicycle class inherits from the Motorcycle class, it too will be able to prevent attempts of people to install Cooper tires on its rims as well.

class Minicycle extends Motorcycle {}

$minicycle = new Minicycle('Cobra');

$minicycle->setTires('Dunlop');

echo $minicycle->getTires();
// DUNLOP

Mounting a set of tires from our approved selection of brands does work fine, however.

Creating A Black Box

One of the biggest selling points of object-oriented programming is the idea of a black box. When we say black box, what we mean is that as a consumer of a given class, we don’t care how it completes its job, just as long as it does the job accurately and successfully. In this example of using getters and setters to change tires on a motorcycle, we have so far kept it very simple. In real life, however, there are probably many steps to actually change a tire. For example, we’d need to let the air out of the old tires. Then we might need to wrestle with a tire iron to pry the tire off of the rim. After that, we’d have to do the same thing to try and pry a new tire onto the rim, and finally, we would have to inflate the new tires once we were able to get them back on the rims. There are likely even more steps than that. Consider the team owner. He doesn’t care about all the steps it takes to change a tire. He just knows that Bridgestones are the hot new thing, so he says to the mechanic, “Change the tires”. The team owner wants to be able to simply give the order, and when he comes back from getting a burger at the concession stand, the hot new Bridgestones will be mounted. This is kind of how the idea of a Black Box works. Another example might be your manager at work. Maybe you deal with clients that are having technical problems and your manager wants you to “fix the client” or “make them go away”. Well in order to fix the client, or make them go away, it may take ten different procedural actions to take in a very specific order, and possibly some prayers. The boss doesn’t care how you get it done. Just get it done. Use the tools and knowledge in your head (these are the private methods and properties) and fix the client.

Let’s create this idea in code for our Motorcycle class.

<?php
class Motorcycle {
    public $make;
    
    protected $tires;
    
    public function __construct($make) {
        $this->make = $make;
    }
    
    public function setTires($tires) {
        if($tires == 'Cooper') {
            throw new Exception('Oh hell no!');
        } else {
            $this->deflateOldTires();
            $this->inflateNewTires();
            $this->tires = $tires;
        }
    }
    
    public function getTires() {
        return strtoupper($this->tires);
    }
    
    private function deflateOldTires() {
        $a = 'Press needle on rims';
        $b = 'Release all air';
        return true;
    }
    
    private function inflateNewTires() {
        $a = 'Attach tire pump';
        $b = 'Inflate to specified pressure';
        return true;
    }
} 

In this snippet above we add two new private methods. These methods are meant to be used exclusively and only by the Class itself. Objects of this class can not make use of them, nor can child or subclasses, or anyone else for that matter. These private methods contain the special and secret steps to complete a specific job. In this case, we are simply hiding the implementation of successfully changing tires on a motorcycle. When we need to change tires, we simply do so via our setTires() method and things still work perfectly. We don’t care how it gets done, just as long as it gets done.

$motorcycle = new Motorcycle('Harley');

$motorcycle->setTires('Dunlop');

echo $motorcycle->getTires();
// DUNLOP

As we can see above, our ability to mount the right tires still works like a champ. Let’s think about that RacerX frenemy we have however who might be interested in doing something malicious to our motorcycle to gain an advantage. Maybe he thinks secretly removing all the air from our tires will be his key to a victory. Let’s see what happens when someone tries to call our deflateOldTires() method in a malicious way.

$motorcycle = new Motorcycle('Harley');

$motorcycle->setTires('Dunlop');

$motorcycle->deflateOldTires();
// Fatal error: Call to private method Motorcycle::deflateOldTires()

Boo Yeah! You see, no one can peer into our black box! Because this method is private, only the class itself can make use of it to accomplish the overall goal of the public interface of that class.

Encapsulation For Hackers Summary

This tutorial covered some of the ways to begin implementing behavior and information hiding via encapsulation. This in fact is the rule of encapsulation in object-oriented programming. We should hide as much information and behavior as possible.

Click to share! ⬇️