Chances are, if you’ve looked at PHP code, you’ve encountered magic methods — you can tell it is a magic method because the method is preceded by two underscores. If you’re not using these tools, you should be.
Magic methods are functions that allow you to show an object how to react when something happens to it. You get to define how your object should react in an event. This can be a huge tool in preventing errors and reducing redundant code.
In this article, I am going to introduce you to some of the magic methods that PHP provides. I have chosen these methods because they are most useful to you and help you see the patterns of how they work.
Construct
What It Does
The most used magic method is __construct(). This function is called as soon as a class is instantiated. It has several uses.
The first use is to inject data into the class. For example, when creating an instance of the WP_Query class, we normally pass an array of arguments directly to it:
$args = [ ‘post_type’ => ‘hiroy’ ];
$query = new WP_Query( $args );
When we pass arguments into the class at instantiation, those arguments are passed to its constructor. This allows us to have one classes that produce different objects.
The second use is to set off the sequence of events that must happen before the object is usable. For example, let’s say we have a class that is designed to create specific HTML markup, by doing some of the work based on a collection of posts.
We could use the constructor to set off that process that creates that HTML so that it is ready when we need it, or we can trigger an error later. This is useful when using “getters” and “setters” as I will discuss later on in this article.
In PHP4 and PHP5, a method with the same name as the class functions as a constructor. In PHP7, this is not the case. It is never a good idea to have a method name the same as a class.
It often feels like a good idea, but it is not for future-compatibility sake. Also, this “pseudo-constructor” can not be declared static or an error will be thrown.
Setting Hooks In The Constor
It is a common practice to add hooks in the class constructor. This may be a bad idea because hooks called using the $this reference to current object are hard to unhook. Also, it makes the class less reusable.
One solution is to move hooks outside of object context. The other is to keep all hooks inside of a specific “set up” class or classes that have no functional reason to be instantiated twice, or implement the singleton pattern to ensure they are only instantiated once.
Another option is to use a “setup action” and the did_action() function to prevent running again. That looks like this:
<?php class stuff { public function __construct(){ if( ! did_action( 'init_stuff' ) ){ $this->add_hooks(); } } protected function add_hooks(){ //add your hooks do_action( 'init_stuff' ); } }
This pattern works, but I think when it works, it’s a sign that a class is in violation of the single-responsibility principle. In general, I think that a class that all public methods of a class that adds hooks should be only used by those hooks.
Magic Setters and Getters
In general, when we talk about a “setter” or “getter” method, we refer to a method of a class that is used to set or get the value of a class property. By convention, we use the prefix “set_” and “get_” for these types of method. This pattern is useful for properties of a class that we want to be accessible outside of the class, but do not want to be modifiable outside of the class.
Here is an example class that takes two dates and queries for the most commented on posts during that period:
<?php class popular_posts { /** * WP_Query object * * @var WP_Query */ protected $query; /** * Create object with WP_Query of most commented on posts in a given period * * @param string $before * @param string $after */ public function __construct( $before, $after ){ $this->set_query( $before, $after ); } /** * Get WP_Query * * @return \WP_Query */ public function get_query(){ return $this->query; } /** * Set query property * * @param string $before * @param string $after */ protected function set_query( $before, $after ){ $args = array( 'date_query' => array( 'after' => $before, 'before' => $after, 'inclusive' => true, ), 'orderby' => 'comment_count', 'order' => 'DESC', 'posts_per_page' => '5', 'paged' => '1', ); $this->query = new WP_Query( $args ); } }
This class has a protected property called query. It is set by the method set_query(). That method is protected since it only makes sense to call it once. It is called by the constructor and then it is done.
It’s protected to prevent it being changed after the an object of this class is instantiated. That’s good, but the object would be worthless if you couldn’t do something with that object. That’s why we have the “getter” method get_query(), which just returns the property $query.
Using get_ and set_ for getters and setters is a convention, but it is not required. PHP does provide a magic getter that if present is called when a property is accessed from outside of the scope of the object. A __get() magic method can be used to make some or all protected and private methods of a class public.
The magic __get() is passed the name of the property. This allows you to allow specific properties to be returned, or return something else. Also, you may wish to use __get() to call a setter.
The __set() magic method is passed the name of the property being set and its value. It can be used to create an arbitrary setter for all properties of a class, and making some or all protected and private properties writeable outside from outside of the class.
Magic getters and setters have some usages, for example, validating types before allowing properties to be changed. For example, a class where it makes sense for one property to be mutable outside of the object, but you want to ensure the type stays the same.
Here is a class for using any WP_Query through a provided partial. It gets around the issue that a public property can change its type. This class would throw errors if the property $query was changed to an array, or an object of any class other than WP_Query.
<?php class query_loop { /** * @var WP_Query */ protected $query; protected $partial_partial; public function __construct( $partial_partial ){ $this->partial_partial = $partial_partial; } public function __set( $property, $value ){ if( 'query' == $property && ( is_a( $value, 'WP_Query' ) ) ) { $this->query = $value; } } public function __get( $property ){ if( 'query' == $property ){ return $this->query; } } public function output_loop(){ $query = $this->query; ob_start(); foreach( $query->post as $post ){ $post = setup_postdata( $post ); include $this->partial_partial; } return ob_get_clean(); } } Raw
Note that this class’ use of the __get() and set() doesn’t really make it any more compact than using explicit getters and setters. Also, it makes the inline docs less useful. This shows the weakness of using magic get and set methods.
You could theoretically make a class that allows a property to be set or retrieved. Here is a class that acts almost the same as the stdClass, but checks if a property is set before returning it in the __get() magic method:
<?php class anything { /** * Get any property that is set * * @param string $property Name of property * * @return mixed */ public function __get( $property ){ if( isset( $this->$property ) ){ return $this->$property; } } /** * Add anything to this class * * @param string $property Name of property * @param mixed $value Value to set */ public function __set( $property, $value ){ $this->$property = $value; } } Raw
Magic getters and setters are useful when a class has a lot of properties, not all of which may be used each time. With lots of properties that need to be made “semi-public” and potentially call a private explicitly defined “getter” this can be useful with a __get() like this:
abstract class check_set { /** * Get a property of this class -- if it exists, will call this_{$property_name} if not set an such a method exists * * @param string $property Property name * * @return mixed Property value */ public function __get( $property ){ if( property_exists( $this, $property ) ) { if( ! isset( $this->$property ) && method_exists( $this, 'set_' . $property ) ){ call_user_func( [ $this, 'set_' . $property ] ); } return $this->$property; } } }
Magic get and set are useful, but are often more trouble than they are worth. They can save a lot of redundant code, but they make the code less explicit. They do solve many problems. Just don’t use them because it’s clever or you can. Use them because they solve a real problem.
For example, the magic __get() method in the WP_Post class is used to allow post objects to access meta values of that post, which there are an arbitrary number of.
Converting Objects To Strings
There are many situations where we may need a string representation of an object. PHP provides a __toString() magic method that allows you to use an object as a string. This allows you to echo an object and have a meaningful string returned instead of throwing an error.
Another, more useful, example is when we wish to store an object in a MySQL database. This requires serializing the object into a string. When an object is passed to the function serialize() the magic method __sleep is called first. When an object is passed to unserialize() the magic method __wakeup() is called.
You should be very careful with these methods as they can be used to perform remote data execution attacks. In fact, it is very common to use them to prevent serialization of a class.
A good use for these methods is to reduce the size of objects. For example, if a class has properties that are calculated, they can be removed before serialization and re-calcuated after serialization. This saves space in the database and might speed up queries.
For example, consider a class that creates objects for conversion rate of a page. Conversion rate is the number of conversions divided by the number of page views. Here is an example class that take conversions and page views and calculates the conversion rate, which makes use of both sleep and wakeup and provides for another useful example of the __get() magic method:
<?php class conversion_rate { /** * @var int */ protected $conversions; /** * @var int */ protected $total; /** * @var float|int */ protected $conversion_rate; /** * @param int $conversions * @param int $total */ public function __construct( $conversions, $total ){ $this->conversions = $conversions; $this->total = $total; } /** * @return float|int */ public function get_conversion_rate(){ return $this->conversion_rate; } public function __sleep() { return [ 'total', 'conversions' ]; } public function __wakeup() { $this->set_conversion_rate(); } /** * @param string $property * @param int $value */ public function __set( $property, $value ){ if( 0 <= $property && in_array( $property, [ 'conversions', 'total' ] ) ){ $this->$property = (int) $value; $this->set_conversion_rate(); } } protected function set_conversion_rate(){ if ( 0 != $this->total ) { $this->conversion_rate = $this->conversions / $this->total; }else{ $this->conversion_rate = 0; } } }
This class uses the __sleep() magic method to tell PHP to only serialize the total and conversions properties. That’s not going to shorten the serialized string by much, but if there was a lot of these objects being saved, or you wanted additional math, it could save a lot of space in the database.
While it doesn’t use a magic method, there is a way to get similar functionality as __sleep() provides for serializing when converting an object to JSON. If a class implements the JsonSerializable interface, it can have a method called JsonSerializable() which is used to define how the object is converted to JSON when passed to the function json_encode().
Here is a new class, which extends the earlier class, that can be used to create a JSON string that has all three properties in it.
<?php class json_conversion_rate extends conversion_rate implements \JsonSerializable { public function jsonSerialize() { $this->set_conversion_rate(); return [ 'total' => $this->total, 'conversions' => $this->conversions, 'conversion_rate' => $this->conversion_rate ]; } }
Use Magic, Learn More Magic
I hope this article has shown you how to use the magic methods I have described or how to use them better. Also, I hope it will help you understand other methods. For example, now that you understand __get() and __set() which allows access to a non-existent, private or protected variables, there is also __call() and __callStatic() that allow access to non-existent, private, or protected methods.
Take a look at all of the magic methods listed in the PHP manual and see which other ones may be useful to you. But also remember that magic methods let you do clever stuff, but that doesn’t mean that you should use them. Any advanced wizard will tell you, too much magic, no matter how well-intentioned, is dangerous and is bound to have unintended consequences.
1 Comment