Magic Methods in OOP: The Definitive Guide (PHP)
Coding (Php 7.x)
This is the most complete guide on Magic Methods in PHP. The best part? You can implement them RIGHT NOW.
Wouldn't be great if you could define some scenarios or events that may happen in the future and when they happen, your code would be automatically triggered to do what you want it to do?
These are the powers of Magic Methods,
Keep reading to discover the secret beyond these Object-Oriented techniques
Follow the series ...
This blog post is the fifth part of "The complete guide to Object-Oriented Programming: Go from procedural programming to OOP expert in PHP"
If you did not read the other parts yet
You can check the other blog posts following the links below
Introduction to Object-Oriented Programming,
Inheritance and Interfaces in PHP,
More Interfaces and Polymorphism,
Visibility and Static keyword,
Constructor and Magic methods and
Abstract Classes and Extra bits not published yet, Subscribe to get Notified
Table of Content
- What are magic methods?
- The Constructor
- Dependency injection
- Optional parameters
- Type-hinting
- The __destruct() method
- The __call() and __callStatic() methods
- The getter and setter methods
- The toString method
- The __sleep() and __wakeup() couple
- The __invoke() method
- The __set_state() and var_export
- Debugging with __debuginfo()
Constructor and Magic methods
What are magic methods?
It is time to sprinkle some stardust over this article.
I am sure you have already come across some strange symbols in your web development career.
In this particular case,
I am talking about methods that have two weird underscores (__) prefixed to the original name.
Those are called “Magic Methods” and they are used in Object Oriented programming, allowing you to respond to specified circumstances when using a specific object.
In simple words, these methods specify how to react in those occasions.
The Constructor
The constructor is probably the most used magic method in PHP.
There are several design patterns that take advantage of the function of this method,
It is defined by the command:
function __construct()
If a class has this method it is going to be invoked every time a new object will be instantiated.
When using the constructor method, the behavior of the class change depending on if the class is a child or not.
If the child class does not specify a constructor the object’s behavior will be inherited from the parent like the other method you have already seen previously.
You can also overwrite the behavior specifically or load the parent behavior and edited as you prefer.
Now,
What happens when a new apartment block is being constructed?
The manager of the building needs to make some money to cover its expenses.
To do that he starts looking for tenants.
Here is how you can do that in the code
class Building { function __construct() { print "The building is complete. \n"; } } class Apartment extends Building { function __construct() { parent::__construct(); print "Now looking for tenants."; } } $mainStreet1025 = new Apartment(); // the code will output: “The building is complete. Now looking for tenants.”
On the instantiation of our apartment located in $mainStreet1025 the program will automatically output the defined lines.
If the code examples in this post seem a bit too complicated or you just want to go over the syntax used above, you can take a look at the basics of PHP
Dependency injection
Some among the more expert of you might be wondering why I haven’t explained what dependency injection is yet, some other have been surely looking weirdly to that parenthesis affixed to the methods’ names asking what they mean and why they are there.
Well,
That moment has arrived.
Jeffrey Way, the curator of Laracasts, has a theory that I want to share with you:
Do you know how are called the smallest stars in the universe?
White Dwarf!
Can you tell me what’s the name of the region of the spacetime exhibiting such strong gravitational effects that nothing, not even particles and electromagnetic radiation such as light, can escape from inside it?
Nothing more than Blackhole.
You see, he thinks and I agree that astronomers are among the smartest people in the world, he knows it, I know it, they know it.
For us developer, the situation is a bit different.
We must feel smart, we have the necessity to exhibit all our neurons all the time.
This most of the time is translate in something like dependency injection.
Dependency injection is nothing else than a very easy technique that was named in a really fancy way, to make it look like very difficult to understand and make whoever use it look the smartest person in the room.
So, What is it?
Dependency injection is a method of supplying an object that a given piece of code requires.
The required object is called a dependency.
Basically, instead of having your objects creating a dependency you pass the needed dependencies into the object from outside,
This permit the developer to create objects using variables parameters rather than using fixed ones.
Example coming...
$windowsCount = 10; $wallsColor = “red”; $roomsCount = 3; $myBuilding = new Building($windowsCount, $wallsColor, $roomsCount); $windowsCount = 14; $wallsColor = “green”; $roomsCount = 6; $yourBuilding = new Building($windowsCount, $wallsColor, $roomsCount);
Here you have it,
You just discovered what’s goes inside that parenthesis.
As you can see leveraging this technique will be very useful, it allows your code to create different objects and any of them will have different characteristics.
$myBuilding, for instance, has red walls and 4 rooms, whereas $yourBuilding has 6 rooms and green walls.
That’s it!
As I told you, it is a 25-dollar term for a 5-cent concept.
Even though this is a simple concept, I decided to wait until now because now that you know what is a construct you can easily understand the following examples in this article.
The __construct method is automatically invoked when a new object is instantiated.
So now you are able to do this:
class Building { function __construct($windowsCount, $wallsColor, $roomsCount) { $this->windowsCount = $windowsCount; $this->wallsColor = $wallsColor; $this->roomsCount = $roomsCount; } } $windowsCount = 10; $wallsColor = “red”; $roomsCount = 3; $myBuilding = new Building($windowsCount, $wallsColor, $roomsCount);
You have just seen dependency injection at work!
If this looks a bit overwhelming let me explain what’s happening.
The constructor requires 3 parameters: $windowsCount, $wallsColor and $roomsCount.
Once this method is invoked those 3 paramethers are included into the class using:
$this->nameOfTheVariable = $nameOfTheParamether;
When a new object is created (in our case $myBuilding) the three parameters are specified within the parentheses:
$myBuilding = new Building($windowsCount, $wallsColor, $roomsCount);
A very important note that you need to keep in mind is that the order of the parameters must be the same in both the places otherwise you might end up with $roomCount equal to red and 50 windows in a building with 1 room.
Good job!
but we didn’t finish yet.
There are several improvements we can implement this technique to enhance the quality of our code.
Here are brief explanations of them.
Optional parameters
Once you set classes or methods up and require dependency, you must give a parameter in order to do not bump into errors but, what if you do not have the required parameter?
Well, you can establish it in advance by giving a default value to it.
How?
... function __construct($wallsColor = red) { ... } $myRedBuilding = new Building(); $myBlueBuilding = new Building(“blue”);
There is the main difference between the two objects created here.
At the moment we create the first object we did not provide any parameter but we want to be sure that people do not see only bricks and concrete so I have added a default value to the constructor to do that via $wallsColor = red.
What this bit of code is saying literary is:
If there is no parameter that specifies the color of the walls in this building uses a default color of red.
In the second object, you can see the blue string inside the parenthesis and that “blue” will overwrite the standard color of red.
Type-hinting
What is type hinting?
Type hinting is the practice to clearly point out the required data type (objects, interface, etc.).
From the early version of PHP5 until the new versions it is possible to type hint parameters inside methods.
Like this:
function __construct(array $address) { ... } $address = [ ‘main street’, ‘248’, ‘London’ ]; $myBuilding = new Building($address);
Now the constructor is requiring an address variable of type array.
If you put everything that is not an array it will result in an error.
This procedure is very useful because it adds a new level of protection, leading to an improvement in security and better code.
A new feature of PHP7 is that now is even possible to do scalar type hinting, which means that if you use the last version of PHP you can hint boolean. string, integer and float variables.
The __destruct() method
You have seen that the constructor is the first method that is invoked when a new object has been instantiated,
As logical, the name of the magic method that is invoked the last is the deconstructor.
This method is automatically called when the object has no more references or when you compel the deletion.
You will rarely see this method but from my point of view, it is very useful because PHP will clean the object and remove from the memory, resulting in an enhancement in performance.
function __destruct() { echo "The building has been terminated"; }
The __call() and __callStatic() methods
Those call methods are very easy to understand.
If a class has a __call() method (do not forget the double underscore) and you call a method that does not exist on an object then the __call() method is invoked instead.
class Building { function __call($method, $arguments) { echo _CLASS_ . “has not method ” . $method; } } $myHome = new Building(); $myHome->buyAWindow(); // This code will echo the sentence in __call() method // "Building has not method buyAWindow"
The class in the example above does not have a buyAWindow() method, so, in this case, the __call() method is invoked and it returns the message.
The only difference between the __call() and the __callStatic() methods is that the first responds to non-static calls the second to static ones.
Notice that,
even though I did not specify any parameters the call methods have the name of the method as first and an array that contains the list of arguments available as the second parameter.
The getter and setter methods
The __get() and __set() methods are called when the code tries to read or write properties that do not exist or are not available in the class;
class Building { function __get($propertyName) { echo $propertyName . “does not exists”; } function __set($propertyName, $propertyValue) { echo “Impossible to set ” . $propertyValue . “to ” . $propertyName; } } $myHome = new Building(); echo $myHome->securityCameraCount; // It will echo the string “securityCameraCount does not exists” $myHome->televisionCount = 10; // It will echo the string “Impossible to set televisionCount to 10”
In the example above $securityCameraCount() is a method that does not exist in the Building class,
The __get() method has passed the name of the property that it is looked for so we can manage this error by returning a string that warns the user about it.
We have the same problem with the televisionCount,
This parameter is not available in the class, so when we try to assign the value of 10 to it PHP understand that it needs to invoke the __set() method,
Within the setter method, you can have several choices depending on what you want to do, the most common way to manage it is to show the error to the user via a message.
the toString() method
Sometimes it could happen that, either by mistake or on purpose, you will treat a class as a string.
For example when doing this:
$myHome = new Building(); echo $myHome;
The __toString() method allows you to manage the behavior of the class when that happens.
This is considered to be a very delicate method because it can easily throw fatal errors in case you code the wrong thing.
For instance,
this magic method must return a string and you must not throw exceptions from inside, if one of those conditions is not respected PHP will return a fatal error.
You can see a full example of the __toString() method below.
class Building { public $sentence; public function __toString() { return $this->sentence; } } $myHome = new Building(); echo $myHome;
The __sleep() and __wakeup() couple
These two methods are called when an object is serialized or is being unserialized.
To be precise the __sleep() is called just a moment before the serialization.
whereas the __wakeup() is called a bit after the unserialization.
How do they work?
The serialize() method check if within the call is present the magic method __sleep(), if so, serialize() stops for a second and let __sleep() do its things.
Since this method must return an array usually its goal is to return an array with the names of all the object’s variables that need to be serialized.
In case the method does not return anything then what’s returned is NULL that is serialized and an E_NOTICE will be shown.
Some helpful case in which the use of this method is justified may be to get large object data that do not need to be saved or execute some cleaning chores.
On the other side, you have the __wakeup() method.
It simply mirrors his close friend __sleep(),
This time is the unserialize() method that looks for the __wakeup(), if it exists within the class then the method restores all the connections of data lost during the serialization.
class Building { public $address, $city, $postcode; public function __sleep() { return array(address', 'city, 'postcode); } public function __wakeup() { return $address; } }
The __invoke() method
The __invoke() method is incredibly simple,
It is automatically called when the code does a call an object as it was a method or a function.
class Building { public function __invoke($variable) { var_dump($variable); } } $myBuilding = new Building ; $myBuilding(“23, Main Street”);
In the example above the outcome of the invoke method will show string(15) “23, Main Street”.
There are plenty of examples online on how to use this method but you can choose the best way to implement this method according to your needs.
Think of this method as a shortcut that let you do a specific task and is accessible by calling the object as a method.
This magic method is quite loose, which means you can do almost anything you want with it but be careful at one thing, arguments and parameters need to match,
If you call the object and you set 4 arguments, then the connected method needs to have 4 parameters.
Forgot to do this will result in an error thrown by the script.
The __set_state() and var_export
We just went from one of the easier to understand magic methods to one of the most complex The __set_state() method occurs when an instance of your object is passed to the var_export function.
What’s var_export() do?
var_export() needs two parameter,
the first one is mandatory and needs to represent the variable you need to export whereas the second is an optional boolean that indicates if it must return the variable representation as a string or outputting it.
We need a sample here:
class Building { public $windowsCount; } $myBuilding = new Building; $myBuilding->windowsCount = 5; var_export($myBuilding); // This code will output: Building::__set_state(array( windowsCount => 5, ))
Now __set_state(),
As said __set_state() is called to respond to an instance of an object passed to the var_export function.
It has only one parameter that must be an array that contains exported properties as a key-value pair.
class Building { public $windowsCount; public static function __set_state($windows) { $myBuilding = new Building; $myBuilding>windowsCount = $windows[‘windowsCount’]; return $myBuilding; } } $myBuilding = new Building; $myBuilding->windowsCount = 5; eval('$state = ' . var_export($a, true) . ';'); /* $state = Building::__set_state(array( 'windowsCount' => 5, )); */ var_dump($state);
It will result in:
object(Building)#2 (1) { ["windowsCount"]=> int(5) }
Debugging with __debuginfo()
Arrived with PHP 5.6 this characteristic is invoked by var_dump() when you dump an object in order to the determine which properties need to be output.
If the method is not defined on the object all public, protected and private properties will be shown.
class Building { private $windows; private $doors; public function __debugInfo() { return [ 'windows' => $this->windows, ]; } } var_dump(new Building(10)); // Here is the outcome object(Building)#1 (1) { ["'windows'"]=> int(10) }
In the above example, we are saying that __debugInfo() need to return only $windows and hide $doors
The conclusion of the fifth part
I hope you now are more aware of all the methods and trickery that the PHP language has to offer to Web Developers.
You probably won't use these methods described above on daily basis,
but,
once you understand the concepts and maybe go back to this page from time to time in order to brush up their purpose will help you in better managing your code.
Also please, notice that you need to consider these only as helpers.
Even if, nobody can deny their power, start to use call method, getter, setter etc eventually will give you less control on the project you are writing.
Your main focus has to be writing clean code that is scalable and let you do what you need it to do not vice-versa.
Said that,
Feel free and do not be scared of taking advantage of dependency injection, constructors and other methods after you have deeply understood how their works behind the scene.
In the next episode,
You will see what abstract methods are and extra features related to the Object Oriented paradigm
So,
Stay tuned and subscribe using the form down here to get notified when it will be published.
Now it is your turn:
Are you going to update your methods and properties applying protected and private keywords or do you prefer spread static keywords everywhere?
let me know in the comment box below!
In the next episode,
we will focus on magic methods
I have prepared a complete list with a detailed description and code examples.
For this reason,
Stay tuned and subscribe using the form down here to get notified when it will be published.
Now it is your turn:
What Magic Method are you going to implement in your code?
let me know in the comment box below!