PHP Magic Methods –
Let’s say you’re developing an Object-Oriented PHP application. You’ve heard about PHP’s magic methods and have been curious about them — what are they? How do I use them? why would I want to? Let’s work together to answer those questions.
Magic methods are a feature of PHP’s object orientation. Magic methods are methods that are called, behind the scenes by PHP, when you use an object in particular ways. They are identifiable by the leading double underscores in method names. There are currently 15 magic methods supported by PHP, far too many for one article. Today I’ll be focusing on 2 of them — __get()
and __set()
.
For your reference, here is list of PHP magic methods (listed alphabetically):
- __construct()
- __destruct()
- __call()
- __callStatic()
- __get()
- __set()
- __isset()
- __unset()
- __sleep()
- __wakeup()
- __toString()
- __invoke()
- __set_state()
- __clone()
- __debugInfo()
Well Known Magic Methods
The most well known magic method in PHP is the constructor, which is called when creating a new object. The name of the method is __construct()
. It’s used to perform initialization upon object creation. Another well known PHP magic method is __toString()
, which allows you use an object in a string context. These two are well travelled territory. So let’s move on.
Magic is Bad (usually)
As a general rule, magic in code is bad. It usually makes the logic of the code harder to follow. In this sense, magic is anything where knowledge is assumed to be understood. For example, if there was an equation that multiplied a number by 86400, you might be scratching your head. However a variable like $seconds_in_a_day = 86400 it becomes clear. Systems that use convention over configuration feel like magic to me, which is why I avoid them if possible. Let’s continue!
Get and Set
You’re a good developer, so you know that we should be using encapsulation with a getter and setter for each of our properties. You might ask – If we’re using encapulation, then we shouldn’t need __get
or __set
, correct? Perhaps, but lets investigate.
The PHP manual tells us the __get() and __set() methods deal with inaccessible properties which are properties “that have not been declared or are not visible in the current scope”
__set()
is run when writing data to inaccessible properties.
__get()
is utilized for reading data from inaccessible properties.
What does this mean? It means any property that is declared public will not trigger these methods. Lets start by creating a small class called Person, and create two public properties called $firstname
and $lastname. Then create an instance of Person and try to set and get the $firstname
and $lastname
property. Your code should look like the following.
class Person { public $firstname = ''; public $lastname = ''; } $me = new Person(); $me->firstname = 'Andrew'; $me->lastname = 'Woods'; echo "firstname={$me->firstname}\n"; echo "lastname={$me->lastname}\n";
This works as you expect. There $firstname
and $lastname
properties are set, then read and displayed a couple of lines down. Now lets add a protected property called $zipcode
and a couple of lines to write and read it.
protected $zipcode = ''; // outside of the class $me->zipcode = '98109'; echo "zipcode={$me->zipcode}\n";
This new code will cause an error. “PHP Fatal error: Cannot access protected property Person::$zipcode in …”
Earlier, it was mentioned that __set() and __get() methods deal with inaccessible methods – which is exactly what zipcode is. Let’s create a __set and __get method in our Person class.
public function __set($name, $value){ echo "using __set: name={$name} value={$value}\n"; } public function __get($name){ echo "using __get: name={$name} \n"; }
Now we can see the magic methods we added are working, and the error is gone.
Why would you want to these magic methods? Under what conditions might we not know which properties are being used? Or be unable to use the defined getters and setters? I have a couple of ideas.
Possible scenarios:
- Processing Database results
- Conversion from other Data Format e.g. json
- Map one property name to another
- Graceful in error handling
Let’s review where we’re at with the code.
class Person { public $firstname = ''; public $lastname = ''; protected $zipcode = ''; public function __set($name, $value){ echo "using __set: name={$name} value={$value}\n"; } public function __get($name){ echo "using __get: name={$name} \n"; } } $me = new Person(); // Setting Values $me->firstname = 'Andrew'; $me->lastname = 'Woods'; $me->zipcode = '98109'; // Getting Values echo "firstname={$me->firstname}\n"; echo "lastname={$me->lastname}\n"; echo "zipcode={$me->zipcode}\n";
Processing Database Results
When you get results from a database, each record can be an array or an object. In either case, you don’t always control the names of the database fields. The mysqli extension does provide a parameter on the fetch_object() method to allow you to specify your own class. Adding __set and __get to your class would provide you some flexibility.
Map one property name to another
What if you wanted to make $zip work to update the $zipcode property. For your Candian colleagues, you could also have $postal_code update the $zipcode property. These kinds of decisions also apply to using __get() when retrieving values. There’s a saying “be conservative in what you output, and be liberal in what you accept”. These magic methods can help you in that effort.
Graceful in error handling
The use of the __get and __set magic methods can be used to make error handling more graceful. You could throw an Exception to let the client programmer determine what to do. Alternatively, you could let the assignment happen, and add an error_log() message. This kind of decision depends upon your project.
Conclusion
Now you know about PHP’s magic methods – what they are, and how to use them. Consider how you might make use of them in your next project.