Lesson 4 - Encapsulation in PHP
In the previous exercise, Solved tasks for OOP in PHP lessons 1-3, we've practiced our knowledge from previous lessons.
In previous lesson, Solved tasks for OOP in PHP lessons 1-3, we learned all about attributes and the three main magic methods. Today, we will focus mainly on encapsulation.
Encapsulation
OOP is built on 3 pillars: Encapsulation, Inheritance, and Polymorphism. Today we will talk about first of them all.
Encapsulation allows us to hide methods and attributes in a way that they are only accessible from inside the class. You could imagine objects as black boxse with specific interfaces, through which you pass instructions/data that is then processed by the box.
You may have no idea how it works from the inside, but you will always know how it behaves externally and how to use it. This means that there is no way to break it. Mainly because we only see and work with the parts that were specifically chosen to be accessible by class' author.
Iur Human class would run into problems if its $dateOfBirth property and other properties derived from it - fullAged and age were changed from the outside. Think about it, if $dateOfBirth changed, then the fullAged and age properties would no longer be valid. When problems like these arise, the object state is deemed inconsistent. Problems like these are very likely to happen in structured programming. What we do to avoid these sort of scenarios in OOP is encapsulate the object by marking the $dateOfBirth property as private. This way, there is no way anyone, or anything, could access it from the outside. However, we will add a public changeDateOfBirth() method, which assigns a new date of birth to the dateOfBirth variable, re-computes the age, and re-checks whether the user is of age. Our class is now relatively safe and the application is stable.
Encapsulation forces programmers to use an object in the way it was intended to be used. Interfaces split classes into public members and private structures.
Generally, we try to hide as much as possible and only expose the most important parts. We won't implement the age computing yet, because it's not all that simple.
Sleep
Private attributes are essentially an object's inner logic. As you now know, marking it as private denies all access to the aforementioned attributes from the outside. Now, let's add a method that makes our human sleep, which is part of human functionality and a perfect example to show off the benefits of encapsulation.
Humans will have fatigue, which will be stored in the private $fatigue property.
There will also be a sleep() method, which will take the number of hours that are to be slept as a parameter. The sleep() method decreases tiredness based on how many hours the Human slept. Make sure that tiredness doesn't go into the negatives when a Human oversleeps.
To tire our Humans out, we will add a run() method, which will take a number of miles as a parameter. We'll keep it simple and make it add one tiredness unit per mile. If a Human is too tired to run, the method will print out a message saying that he/she cannot run anymore.
The Human class with its new functionality now looks like this:
class Human { public $firstName; public $lastName; public $age; private $fatigue = 0; public function __construct($firstName, $lastName, $age) { $this->firstName = $firstName; $this->lastName = $lastName; $this->age = $age; } public function sleep($time) { $this->fatigue -= $time * 10; if ($this->fatigue < 0) $this->fatigue = 0; } public function run($distance) { if ($this->fatigue + $distance <= 20) $this->fatigue += $distance; else echo("I'm too tired."); } public function greet() { echo('Hi, my name is ' . $this->firstName); } public function __toString() { return $this->firstName; } }
Notice that we initialized the $fatigue property with a value of 0 right in its declaration. This value will be set to every time and instance is created. In other words, it's another way to initialize properties without having to do it right in the constructor.
Other than the private modifier on the $fatigue attribute, the class is pretty self-explanatory. The run() method is not very elegant since it prints the message about being too tired directly (when fatigue is over 20 units). Having it print out a message direclty decreases the re-usability of the program because we may not always want to print out the same exact message. Anyway, we'll leave it as is for now, but later on in the course we will look at how to deal with error states in detail.
There is no other way to change the tiredness value from the outside other than to use the run() method or to make the Human sleep. That's the way we designed the class, and that is the only way it can be used. Objects can neither be broken nor used in the wrong way.
Let's move to index.php, and try the new functionality out. If you want, you can remove the greet() call on both of our instances. Now, make one of then run 10 miles, 3 times:
{PHP} require_once('classes/Human.php'); $carl = new Human('Carl', 'Smith', 30); $john = new Human('John', 'Newman', 24); $carl->run(10); $carl->run(10); $carl->run(10);
{PHP} class Human { public $firstName; public $lastName; public $age; private $fatigue = 0; public function __construct($firstName, $lastName, $age) { $this->firstName = $firstName; $this->lastName = $lastName; $this->age = $age; } public function sleep($time) { $this->fatigue -= $time * 10; if ($this->fatigue < 0) $this->fatigue = 0; } public function run($distance) { if ($this->fatigue + $distance <= 20) $this->fatigue += $distance; else echo("I'm too tired."); } public function greet() { echo('Hi, my name is ' . $this->firstName); } public function __toString() { return $this->firstName; } }
The program output is the following:
Carl ran 10 miles twice and then reached the fatigue limit that we set to be 20 units. The last attempt to run was not successful, so the program printed out that he was too tired to run.
Now let's try our sleeping method out, 10 miles need 1 hour of sleep. To complete the running process, we'll add an hour of sleep right before he runs his last 10 miles:
{PHP} require_once('classes/Human.php'); $carl = new Human('Carl', 'Smith', 30); $john = new Human('John', 'Newman', 24); $carl->run(10); $carl->run(10); $carl->sleep(1); $carl->run(10);
{PHP} // Contents of the Human.php file class Human { public $firstName; public $lastName; public $age; private $fatigue = 0; public function __construct($firstName, $lastName, $age) { $this->firstName = $firstName; $this->lastName = $lastName; $this->age = $age; } public function sleep($time) { $this->fatigue -= $time * 10; if ($this->fatigue < 0) $this->fatigue = 0; } public function run($distance) { if ($this->fatigue + $distance <= 20) $this->fatigue += $distance; else echo("I'm too tired."); } public function greet() { echo('Hi, my name is ' . $this->firstName); } public function __toString() { return $this->firstName; } }
The error message disappeared and everything went as expected.
If we try to access the $fatigue property from the outside (or any other file, for that matter), we will get an error:
$carl->tiredness = 0;
Private methods
Not only properties can be private, we can create private methods as well. Which can be used to provide functionality for inner class mechanisms. Private methods are also often used when public methods become too complex. In which case we would split them up into several private methods that the public method would call. Ideally, the number of lines in a method shouldn't exceed 30, and a class should be about 500 lines total. None of that is applies to you for the time being, but in bigger projects and classes it's good to follow these guidelines. This way, if the application becomes too big, we can easily split the functionality into more cooperative components. Only this way will you be able to keep your applications clear and extendable.
In next lesson, Making an object-oriented component in PHP - Image Gallery, we will put our humans off to the side for a while and create our very first component. A component that will actually be useful in practice, an image gallery. Today's code is available for download below as always.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 34x (1.87 kB)
Application includes source codes in language PHP