If you use object-oriented programming, there’s a good chance that you’re familiar with the constructor. It’s this special method that an object-oriented programming language uses to initialize a new object. In PHP (and a lot of other object-oriented programming languages), the __construct
method is a special method reserved for the role of constructor.
The role of the constructor isn’t always well understood. This is especially true when you’re new to object-oriented programming. You know the method exists, but you’re not too sure how to use it.
This can lead to some less than ideal practices around constructors. This is especially true in the WordPress world where two such practices are popular. First, there’s the habit of putting hooks in the constructor. And, because of that habit, WordPress developers tend to overuse the singleton pattern.
These two practices show a misunderstanding of the role that a constructor plays inside a class. This isn’t how object-oriented programming wants you to use constructors. There are other ways to solve those problems without sacrificing the role of a constructor.
Now, this isn’t to say that constructors don’t have any problems either. They do! In fact, that’s why we’re looking at the static factory method pattern. It solves one common problem that constructors have in PHP.
The problem with constructors
Alright, so what’s this problem that constructors have in PHP? Well, they have a few, but they all originate from a single larger problem. It’s that you can only have one constructor per class in PHP.
What goes on outside the PHP world
In other programming languages, you can overload a class method to solve this issue. If you’re not familiar with overloading, it’s the ability to create more than one method with the same name. You can do that by defining a method multiple times, but with different parameters like this:
class OverloadingExample { public void display(char c) { System.out.println(c); } public void display(char c, int num) { System.out.println(c + " " + num); } }
This is a small example in Java. You can see that there are two display
methods. But each display
method uses different parameters. One is just a char
and the other is a char
and an int
.
You can also overload constructors in the same way. We call this constructor overloading. It looks pretty much the same overloading normal method:
public class TimeOfDay { public int hour; public int minute; public TimeOfDay(int h, int m) { hour = h; minute = m; } public TimeOfDay(int h) { this(h, 0); } }
Constructors in Java don’t use the __construct
method name. Instead, you use the name of the class which is TimeOfDay
here. (PHP used to allow you to do that as well.) Like our overloaded methods, each constructor has its own unique set of parameters.
Meanwhile in the PHP world
It wouldn’t be fair to say that you can’t overload functions or methods in PHP. The issue is that it’s not built into the language like we saw with Java. You have to be more creative about it.
class MyPlugin_SomeClass { // ... } $some_class = new MyPlugin_SomeClass(); $some_class->do_something($argument1); $some_class->do_something($argument2, $argument3);
As you can see in the example above, we have a small class called MyPlugin_SomeClass
. (Great name, I know!) We left it empty for now. But below the class, we added three more lines of code.
The first one initializes the MyPlugin_SomeClass
class. The other two lines of code call the do_something
method. The first one calls it with a single argument while the second calls it with two.
Now, we need to create the do_something
method. And we need to overload it to support the two different calls that we have. (We could also use a default value, but let’s imagine that we can’t here!) We have two options available to us to do that.
Magic method
The first option is to use the __call
magic method. This magic method handles all calls to object methods that don’t exist. This might sound a bit confusing so let’s look at our small example modified to use the __call
magic method.
class MyPlugin_SomeClass { function __call($name, $arguments) { if ('do_something' == $name && 1 == count($arguments)) { // ... } elseif ('do_something' == $name && 2 == count($arguments)) { // ... } } } $some_class = new MyPlugin_SomeClass(); $some_class->do_something($argument1); $some_class->do_something($argument1, $argument2);
As you might have noticed, the do_something
method doesn’t exist inside our MyPlugin_SomeClass
class. Instead, both method calls will go to the __call
magic method.
The __call
magic method has two arguments: name
and arguments
. name
is the name of the called method that doesn’t exist. Meanwhile, arguments
is an array containing all the arguments passed to that non-existent method.
In the __call
magic method, we can check how many arguments the method received and adapt ourselves to it. We do this by using a conditional statement to check the name of the method. First, we always check if the name
is do_something
. Then, once we know the call is to the do_something
method, we check the number of arguments.
We do that by using the count
on the arguments
parameter. If count
returns 1
, we do one thing. Meanwhile, if count
returns 2
, we do something else.
Variadic method
The other option is to turn our do_something
method into a variadic method. A variadic method (or function) is a function or method that accepts an unknown number of arguments. PHP has two different methods for creating variadic functions and/or methods.
The first method is to use special PHP functions created to support variadic functions or methods. These are the func_get_arg
, func_get_args
and func_num_args
functions. That said, it’s only worth remembering the func_get_args
function. It’s the function that you’ll use most of the time.
class MyPlugin_SomeClass { public function do_something() { $arguments = func_get_args(); if (1 == count($arguments)) { // ... } elseif (2 == count($arguments)) { // ... } } } $some_class = new MyPlugin_SomeClass(); $some_class->do_something($argument1); $some_class->do_something($argument2, $argument3);
Here’s another look at our MyPlugin_SomeClass
class. We removed the __call
method from earlier. And, instead of it, we have the do_something
method that we coded as a variadic method.
This method looks a lot like our __call
method from earlier with a few noteworthy changes. The first one is that the do_something
method doesn’t have any parameters. Instead, it relies on the func_get_args
function to get the arguments passed to it
We store the array of arguments returned by the func_get_args
function in the arguments
variable. We then use it in a similar conditional as earlier example with the __call
method. We just don’t need name
check anymore since we’re not using the __call
method.
class MyPlugin_SomeClass { public function do_something(...$arguments) { if (1 == count($arguments)) { // ... } elseif (2 == count($arguments)) { // ... } } } $some_class = new MyPlugin_SomeClass(); $some_class->do_something($argument1); $some_class->do_something($argument2, $argument3);
If you’re using PHP 5.6 or later, there’s also the new ...
token that you can see in the example above. You use it in function or method definition to say that it accepts an unknown number of arguments. It replaces the use of the func_get_args
function that we used before.
Neither options work well with constructors
This brings us to the problems with both these options. The first problem is that the first option doesn’t even work with constructors! How come? Well, let’s imagine that you had a MyPlugin_SomeClass
class that looked like this:
class MyPlugin_SomeClass { function __call($name, $arguments) { if ('__construct' == $name && 1 == count($arguments)) { // ... } } } $some_class = new MyPlugin_SomeClass($argument1);
The MyPlugin_SomeClass
above uses the __call
magic method that we saw earlier. You’d think that since the MyPlugin_SomeClass
class doesn’t have a __construct
method that any call to it should go to the __call
method. But that’s not what happens. The __call
method won’t get called when you create a new object.
The issue with variadic functions or methods
So that rules out our first option, but what about the second? Well, the issue with a variadic constructor isn’t that it doesn’t work! (That would’ve been one crazy plot twist!) No, the issue with a variadic constructor is different from the one with the __call
method
The issue with overloading a constructor using a variadic method is readability. (This is a problem with variadic functions/methods in general.) If a method accepts an unknown number of arguments, how can you know what the method accepts? You can do it with documentation, but that can also get confusing quite fast.
class MyPlugin_SomeClass { public function __construct() { $arguments = func_get_args(); if (1 == count($arguments)) { // ... } elseif (2 == count($arguments)) { // ... } } }
Here’s our MyPlugin_SomeClass
with a variadic constructor. It doesn’t look any different from the variadic do_something
method from earlier. That said, it’s still possible to see the issue with readability.
Here are some examples questions that are hard to answer with our __construct
method:
- How many arguments can you pass?
- What are the types of arguments accepted?
- Does the order of the arguments matter?
In normal circumstances, a developer can answer all these questions in an instant. They can just go look at the definition of the function or method to see what it expects. It’s a painless exercise.
But when you make a function or method variadic, you obscure this information. You make others work harder to figure things out. They have to go in your function or method and read the code to see how it works and what it expects.
Of course, a good use of PHPDoc can answer a lot of these questions. But it can also make the PHPDoc heavy and complicated. It’s far from an ideal solution to our readability problem.
Why do we even care about constructor overloading?
Ok, so we’ve talked a lot about constructor overloading and how PHP doesn’t support it that well. But why should you care? Why is constructor overloading useful?
Constructor overloading is useful because it lets us have more than one way of creating an object. If you only have one constructor, you have to make it fit every possible scenario. This isn’t always ideal depending on what our class does.
Static factory method pattern
This is where the static factory method pattern can come in and help us out. Now, there’s a good chance that you’ve never heard of the static factory method pattern before. It’s not part of the popular design patterns that you see everywhere. It also gets confused with the factory method pattern a lot.
But don’t let this lack of popularity fool you! It’s a useful pattern to know about. So what does the static factory method pattern do and how does it work?
Well, the idea behind the static factory method pattern is quite simple. It’s a static method that returns an instance of the class. The static factory method behaves the exact same way as a constructor except that you don’t use the new
keyword.
With the static factory method pattern, we can create as many “faux” constructors as we want. This lets us circumvent the one constructor limit that PHP gives us. And, without this limit, we’re free to rethink how to approach instantiating a new object.
A static factory method example
The best way to wrap our heads around all this is with an example! So let’s look at one! It’ll help you see how you can rethink how you use constructors when you use the static factory method pattern.
Managing products
Let’s imagine that we have a plugin that manages products. To represent these products, we created the MyPlugin_Product
class. Here’s a partial look at it:
class MyPlugin_Product { /** * The Product's ID. Not the WordPress post ID. * * @var string */ private $id; /** * The name of the product. * * @var string */ private $name; /** * The type of the product. * * @var string */ private $type; /** * Constructor. * * @param string $id * @param string $name * @param string $type */ public function __construct($id, $name, $type) { $this->id = $id; $this->name = $name; $this->type = $type; } // ... }
The code above shows part of the MyPlugin_Product
class. We have the three internal variables: id
, name
and type
. Their names are pretty self-explanatory so we won’t go over them.
There’s also a constructor which has three mandatory parameters. These mandatory three parameters mirror the three internal variables of our class. And all that the constructor does is assign these arguments to those internal variables.
Creating an object from different data sources
Now, our plugin stores these products inside the WordPress database using a custom post type. That said, it’s not the only location where you can find the details about these products. They also reside on another service that we can access with an API.
This means that we have two data sources for these products: the WordPress database and the API. These two data sources also have different ways to represent these products. The WordPress database returns a WP_Post
object by default.
Meanwhile, the API information returns a JSON object which we can convert using the json_decode
function. By default, the json_decode
function will convert our JSON object into an stdClass
object. But you can also tell it to convert the JSON object into an array instead.
Different ways to store data
It doesn’t really matter which of the two you want to use. The important thing is that the two data sources don’t store and format the data the same way. Let’s imagine that you request information on a product from the API. The API returns this JSON object to you:
{ "id": "42", "name": "My awesome book!", "type": "book", // ... }
As you can see, our JSON object is pretty simple and straightforward. Each JSON object variable has a name that represents the data it stores. id
for the product ID, name
for the product name and type
for the product type.
But things won’t be the same once we store this product in the WordPress database. We have to store this information as post metadata. We do this by using custom fields which is a key-value pair.
The problem with custom fields is that any plugins can add them to a post. This can lead to a situation where plugins could overwrite our metadata if we’re not careful. This is especially problematic if our keys are too generic which is the case with id
and name
.
To prevent this issue, it’s always better to prefix all the keys of our custom fields. This means that we can’t use the variable names from the JSON object as our keys. So, for example, instead of using id
as our custom field key, we should use myplugin_product_id
. These are keys that are unlikely to conflict with ones used by other plugins.
Creating our static factory methods
Now, that we’ve seen why we need static factory methods with our MyPlugin_Product
class, we can go ahead and build them. In total, we’re going to need to create two static methods. One for creating a product from API data and another to create a product using data from the WordPress database.
Creating a product from API data
Let’s start with creating a product from API data! We saw earlier that the API would return a JSON object. We just need to pass that object to our static factory method.
class MyPlugin_Product { // ... /** * Creates a new product from API data. * * @param mixed $object * * @return MyPlugin_Product|null */ public static function from_api($object) { // ... } // ... }
We’re going to name our static factory method from_api
. This leads to a method call that looks like this: MyPlugin_Product::from_api()
. This makes it easy for someone reading your code to understand what your method is doing. It’s constructing our object from API data.
The other element to take in consideration is that we don’t know the format of our JSON object. You can represent a JSON object in three different ways: a string, an array or a stdClass
object. We’re going to try to deal with all three scenarios.
class MyPlugin_Product { // ... /** * Creates a new product from API data. * * @param mixed $object * * @return MyPlugin_Product|null */ public static function from_api($object) { if (is_string($object)) { $object = json_decode($object); } if (is_array($object) && isset($object['id'], $object['name'], $object['type'])) { return new self($object['id'], $object['name'], $object['type']); } elseif ($object instanceof stdClass && isset($object->id, $object->name, $object->type)) { return new self($object->id, $object->name, $object->type); } } // ... }
Our from_api
static factory method makes heavy use of conditionals to deal with these different scenarios. We start with a guard clause to check if the given object
argument is a string using the is_string
function. If we have a string, we pass it to the json_decode
function to convert it into an object.
At this point, we know that we can’t have a string anymore. We need to deal with the other two scenarios where object
is either a stdClass
object or an array. This is what the last set of guard clauses does.
Both conditions in the guard clauses follow a similar pattern. They both start by checking the type of the object
variable. We use the is_array
function to check if object
is an array. And we use the instanceof
operator to check if object
is a stdClass
object.
Once we know the type of the object
variable, we can check if it contains the necessary data. We do that using the isset
function. We pass it all the values that we want to pass to our constructor.
For our call to the constructor, we use the self
keyword. It acts as a shortcut to our class name. It’s equivalent to writing new MyPlugin_Product
.
It’s also worth noting that the from_api
static factory method can also return null
. This will happen if object
isn’t a valid JSON object, array or stdClass
object. This is a preventive measure so that we don’t get errors because some information is missing.
Note on the isset function
Now, there’s a good chance that you never saw the isset
function used the way we’re using it here. Most of us pass it one argument and nothing more. But isset
can accept as many arguments as you want!
When you use isset
with multiple arguments, it’ll only return true
if all the arguments are set. This means that isset
will only return true
if we have an id
, name
and type
in our object or array. This lets us pass those values to our constructor without having to worry whether those values exist or not.
Creating a product from a post object
Next, we want to look at our other static factory method. We need it to create a MyPlugin_Product
object from a WP_Post
object. This code will be a lot like the one from the from_api
static factory method.
class MyPlugin_Product { // ... /** * Creates a new product from a post object. * * @param WP_Post $post * * @return MyPlugin_Product|null */ public static function from_post(WP_Post $post) { if (!isset($post->myplugin_product_id, $post->myplugin_product_name, $post->myplugin_product_type)) { return; } return new self($post->myplugin_product_id, $post->myplugin_product_name, $post->myplugin_product_type); } // ... }
So here’s our static factory method for WP_Post
objects. We called it from_post
. The reasoning for the name is the same as the from_api
static factory method. It makes it clear that we use this static factory method to create a MyPlugin_Product
object from a post.
The from_post
static factory method starts with a guard clause. The guard clause uses the isset
function to check if our WP_Post
object has the required post metadata. We need values for myplugin_product_id
, myplugin_product_name
and myplugin_product_type
.
If we’re missing any of those values, the from_post
static factory method returns nothing. (This is the same as returning null
.) Otherwise, it returns a new MyPlugin_Product
object using the post metadata.
Should you make your constructor private?
This wraps up the theory and the example of static factory methods. You might be wondering what the role of the constructor is at this point. Should it even be accessible outside the class?
This is a good question! You could change the visibility of the constructor from public
to private
. This would force everyone to use your static factory methods instead of the constructor.
This might seem like we’re just implementing the singleton pattern by doing that, but we’re not. There’s a crucial difference between making a constructor private here and doing it with the singleton pattern. In this case, we’re not preventing anyone from creating new MyPlugin_Product
objects. We’re just preventing them from doing it the standard way using the new
keyword.
A powerful way to use the static keyword
The static
keyword is an easy concept to understand in object-oriented programming. But, as we’ve seen before, it’s also easy to abuse it. Its ease-of-use masks the difficulty in using it for the right type of problems.
But the static factory method pattern is a great example of how to use the static
keyword! And not only that, it’s also a great tool to have under your belt. It offers an elegant way to let you create new objects based on different contexts.
Photo Credit: Ant Rozetsky