Do you ever take the time to read the code of your favourite PHP framework or library? If you have, there’s a good chance that you might have seen them use PHP’s reflection API. And, if you don’t read other people’s code, you should start doing it! (It’s a great learning tool.)
Now, let’s get back to the reflection API. This is an API that gets used quite a lot by framework and library developers. But the funny thing is that it’s almost never talked about when you’re looking at resources to learn PHP. (Or even advanced PHP resources for that matter.)
This is unfortunate. That’s because PHP’s reflection API serves a unique and important purpose. That’s why developers that build our frameworks and libraries use it so much.
What’s a reflection?
At this point, there’s a good chance that you’re telling yourself, “Well, of course, I don’t know PHP’s reflection API. I don’t even know what you mean by reflection!” That’s a reasonable reaction to have. (My bad!) It’s the one I had the first time I came across code that used reflections.
Let’s start by looking at the concept of reflection. The first thing that you should know is that the concept of reflection isn’t PHP specific. It’s a concept that comes from computer science.
Ok, that’s fine. But what is this mysterious concept of reflection? Well, it’s the idea that your code should be able to examine and modify itself while it’s running.
If that sounds trippy, that’s because it is! We often think of our code as something static. Once you save and execute it, there’s no way to make changes to it. But that’s not true. And reflections is the mechanism that you use to make changes to your code as it’s running.
While it might sound crazy today that you would want to edit your code as its running, that wasn’t always the case. Back when programmers used to program using an assembly language this was quite common. In fact, the examine your code and modify as it ran was an inherent property of an assembly language.
This property of assembly languages didn’t carry over when early compiled languages appeared. But it did eventually reappear through the concept of reflection. And now a lot of programming languages support it.
What do you use them for?
So that’s some of the history behind the concept of reflection. But this doesn’t really help you understand what you would use it for. Or even why it’s even useful. After all, you’ve been getting away with not using it for a while now.
The most common use for reflection is for testing. When you’re testing your code, you often need to examine and modify it as it’s running. It’s also how unit testing frameworks tend to create mocks of your classes and functions.
It’s also great for situations where you want to analyze code and automate some task around it. For example, documentation generation libraries can use reflections to generate documentation for your code. It’s also how a lot of dependency injection containers inject dependencies into the objects they create.
That said, as we’ll see throughout the article, there are a lot of uniques uses for reflections. They don’t always fit into neat categories like those we just mentioned. But what they all have in common is that they use reflections to inspect and modify code as it’s running.
Overview of the PHP reflection API
Let’s move on to the reflection API. If you went to check the PHP documentation earlier (here‘s the link again), you might have noticed that it’s massive. There are over a dozen classes, and some of them have dozens of methods.
It’s not realistic (or even useful for us) to go over all of them. What we’ll do instead is look at the reflection API from a high level. And, from there, we’ll look the reflection classes that you’re the most likely to use.
How are reflection classes structured?
While there are a lot of reflection classes, how they related to one another is pretty easy to understand. That’s because, in general, every PHP construct has a corresponding reflection class. What do we mean by that?
Well, let’s say that you have a class that you’d like to examine. The reflection class that you’d use to do that is the ReflectionClass
class. (Quite the obvious class name!) But if you had an object that you wanted to inspect, you’d use the ReflectionObject
class instead. (You must be getting the idea!)
$classReflection = new ReflectionClass('SomeClass'); $object = new SomeClass(); $objectReflection = new ReflectionObject($object);
Above is how you’d create a reflection of the SomeClass
class and a reflection of the SomeClass
object. It’s worth noting that, while we use two different classes to generate our reflections, both reflections are almost identical. In fact, the ReflectionObject
class extends the ReflectionClass
class.
In practice, there’s no reason to use one reflection class over the other. You should just use the one that fits the information you have at the time. If you just have the class name, use the ReflectionClass
class. If you have an object, use the ReflectionObject
class instead.
Let’s get back to our reflection objects. Once we’ve instantiated one of them, we can then use its class methods to examine the SomeClass
class. This allows us to get answers to specific questions like:
- Is the class an interface? (There’s no separate reflection class for interfaces or abstract classes.)
- What’s the name of the class? (This is more useful when you create a reflection of an object.)
Diving further into reflections
But you can dig even deeper and fetch a specific constant, method or property from a class. Or you can get them all. Let’s look at a small example of this in action.
$object = new SomeClass(); $objectReflection = new ReflectionObject($object); $someProperty = $objectReflection->getProperty('someProperty');
Now, the question with this example is what did we store in the someProperty
variable? Well, it happens that what we got from the getProperty
method is another reflection. This reflection isn’t an instance of ReflectionClass
either. It’s an instance of the ReflectionProperty
class.
This brings us to an important fact about examining code with reflections. It’s that, with reflections, you’re often just dealing with other reflection classes. It’s a bit like going down a rabbit hole!
That said, it’s is also a useful way to view the structure of these reflection classes. Let’s imagine that you create a reflection using ReflectionClass
or ReflectionObject
. What you’ll get is an instance of that class where:
- Its constants are instances of the
ReflectionClassConstant
class. - Its method are instances of the
ReflectionMethod
class. - Its properties are instances of the
ReflectionProperty
class.
Getting information from reflections
Let’s say that you’ve drilled down to the reflection object that you need. How do you get concrete information out of it? For example, how would you get the value stored in the property of an object?
Well, first, you have to think about what information you want to get. In this case, we want to get the value of a property of an object. And then you have to ask yourself what is the most specific reflection that would contain this information.
$object = new SomeClass(); $objectReflection = new ReflectionObject($object); $somePropertyReflection = $objectReflection->getProperty('someProperty'); $somePropertyValue = $somePropertyReflection->getValue($object);
The code above builds on our earlier example. We initialized a SomeClass
object. We then created a reflection of it using the ReflectionObject
class.
Next, we want to access the someProperty
property from our SomeClass
object. We do that by fetching its reflection from our ReflectionObject
object. We do that using the getProperty
method which returns a ReflectionProperty
object.
We also stored that ReflectionProperty
object in the somePropertyReflection
variable. We can then use it to get the value stored in the someProperty
property. We do that using the getValue
method. We then stored that value in the somePropertyValue
variable.
Reflections are never reflections of a specific object
Now, you might have also noticed odd about our call to the getValue
method. It’s that we passed it our object
as an argument. Why did we do that? After all, didn’t we instantiate our ReflectionObject
object using our SomeClass
object?
It’s true that we did instantiate our ReflectionObject
object using our SomeClass
object. But the fact is that it doesn’t matter that we did that. Our ReflectionObject
object doesn’t contain any information about our SomeClass
object.
This is one of the more confusing parts of using reflections. Considering how we instantiate it, you’d think that our ReflectionObject
object would be smarter. That it’d contain information about the object that we reflected.
But that’s not the case. That’s because, when you create a ReflectionObject
object, you’re not creating a reflection of that object. Instead, what’s happening is that the ReflectionObject
class creates a reflection of the class of the object that you gave it. (That’s also why the ReflectionObject
class extends the ReflectionClass
class.)
So, if we go back to our example, the objectReflection
isn’t a reflection of object
. It’s a reflection of the SomeClass
class. It’s unintuitive at first, but you get the hang of it eventually.
The same goes for all the other types of reflections
What happens then when we make a call to the getProperty
method of our ReflectionObject
object? Well, in that case too, you’re not getting a reflection of the property of an object. You’re getting the reflection of a property of a class.
This means that, in our example, someProperty
isn’t the reflection of the someProperty
property of object
. It’s the reflection of the someProperty
property of the SomeClass
class. And that’s why we have to pass object
to the getValue
method.
Again, this isn’t very intuitive when you start using reflections. That said, there are benefits to things working the way they do. For example, it means that we don’t need to create multiple reflections of the someProperty
property. If we have to examine two SomeClass
objects, we can do this instead:
$object1 = new SomeClass(); $object2 = new SomeClass(); $objectReflection = new ReflectionObject($object1); $somePropertyReflection = $objectReflection->getProperty('someProperty'); $somePropertyValue1 = $somePropertyReflection->getValue($object1); $somePropertyValue2 = $somePropertyReflection->getValue($object2);
In the example above, we managed to extract two someProperty
values using the same ReflectionProperty
object. First, we started by creating a reflection of the someClass
class. We created it by using the ReflectionObject
class and passing it object1
. (We could have also used object2
.)
Using our objectReflection
, we get a reflection of the someProperty
property. We then use it to extract the value of the someProperty
property from both our objects. We then store those values in the somePropertyValue1
and somePropertyValue2
variables.
Reflections in practice
While we haven’t seen everything that reflections can do, it’s a good starting point. What we’re going to do next is look at how to use reflections in a more practical context. This will allow us to go over other aspects of reflections that we haven’t covered so far.
A class to reflect
But first, we need a class that we can reflect! We don’t need to make this class too complicated. It just needs methods and properties that aren’t public. (You don’t need for reflections to inspect public methods or properties!)
class User { /** * The user ID. * * @var int */ private $id; /** * The user's login. * * @var string */ private $login; /** * The user's password. * * @var string */ private $password; /** * Constructor. * * @param string $login * @param string $password */ public function __construct($login, $password) { $this->login = $login; $this->password = $password; } }
So above is a small class called User
that we’ll use for more practical examples. It has three internal private properties: id
, login
and password
. For now, it doesn’t have any other methods besides the constructor.
The constructor itself has two parameters: login
and password
. Those are two of the three internal properties that our User
class has. All that the constructor does is assigns the login
and password
arguments passed to it to the login
and password
internal variables.
Now, you might wonder why we didn’t put id
as a parameter of our constructor. It’s because you don’t always have or need the id
of a user when you’re creating a User
object. For example, a new user might not have its ID assigned to it right away.
Assigning a value to a private or protected property
But what happens if you want to assign a value to the id
of a user? Well, you could create a setter method. But this would mean that anyone can set a new ID to a user at any time.
This isn’t an ideal solution because the ID of a user shouldn’t change if there’s one. That said, this could also be part of the logic of the setter method. If a user already has an ID, don’t assign it a new one like this:
class User { // ... /** * Set the ID of a user. * * @param int $id */ public function setId($id) { if (!empty($this->id)) { return; } $this->id = $id; } }
Using a reflection instead of a method
However, even if the setId
method prevents someone from overwriting the id
property, it doesn’t mean that the method should exist. Instead of creating the setId
method, you could use reflections to add the id
to a User
object whenever you need it. Let’s look at how we can do that.
class UserRepository { /** * The user's login. * * @var mysqli */ private $mysql; // ... public function persist(User $user) { $result = $this->mysql->query('INSERT INTO ...'); if (!$result) { throw new \Exception($this->mysql->error); } $userReflection = new ReflectionObject($user); $idPropertyReflection = $userReflection->getProperty('id'); $idPropertyReflection->setAccessible(true); $idPropertyReflection->setValue($user, $this->mysql->insert_id); $idPropertyReflection->setAccessible(false); return $user; } }
Above we have part of the UserRepository
class. If you’re not familiar with the concept of repository, it’s class that acts as a collection of objects. If you need a persistence layer, it can also handle persisting objects inside a database. (If that’s what you use for persistence.)
This is what the persist
method does. It takes a User
object as an argument and persists it in the MySQL database. Only once we’ve persisted our User
object do we assign it an id
.
So instead of using a setId
method to assign the id
, we’re using reflections. But first, we need to save our User
object inside the database. To do that, our UserRepository
class uses the mysqli
class. This is a PHP class used to represent a connection with a MySQL database.
Our UserRepository
already has an instantiated mysqli
object stored in the mysql
property. So we can assume that our connection to the MySQL database works already. All that we have to do then is create a query to insert our new user into the database.
We do that using the query
method to run an INSERT
query. We’re not showing the query because it’s not relevant here. That said, never forget to sanitize your database inputs if you run queries like this!
We store the result of the query in the result
variable. We then check to see if it’s false
. If it is, we throw an exception with the error message stored in the error
property of the mysqli
object.
Once we’re passed this guard clause, we can start using our reflections. First, we create a reflection of the User
object using the ReflectionObject
class. Then we create a reflection of the id
property using the getProperty
method of our User
reflection.
Changing the accessibility of a property
This shouldn’t feel too new to you as it’s what we saw earlier about diving into reflections. What’s new is the last half of this section. It’s where we change the value of an internal variable of an object.
By default, reflections don’t override the scope of a variable. This means that, while we have a reflection of the id
property, we can’t do much with it. It’s still a private
property.
To change that, we need to use the setAccessible
method. This changes the scope of a property to public
. Once we’ve done that, the ReflectionProperty
object will let us modify the value of the id
property.
At that point, all that we have to do is call the setValue
method. We pass it our User
object and the ID of the user we inserted in the database. The mysqli
object always stores that ID in the insert_id
property.
Once we’ve set the value of the id
property, we need to revert the accessibility change that we did at first. Otherwise, the id
property will stay accessible as long as the object exists. (And we don’t want that!) That’s why we make a call to the setAccessible
method once more after the call to the setValue
method.
Calling a private or protected method
We can also do what we just did for private or protected properties with object methods. We can take a private or protected method and make it public so that we can use it. This is also something that can be useful in certain scenarios.
class User { // ... /** * Constructor. * * @param string $login * @param string $password */ public function __construct($login, $password) { $this->login = $login; $this->password = $this->maybeEncode($password); } /** * Encodes the password using the BCRYPT algorithm if it's not already * encoded. * * @param string $password * * @return string */ private function maybeEncode($password) { if (!password_needs_rehash($password, PASSWORD_BCRYPT)) { return $password; } return password_hash($password, PASSWORD_BCRYPT); } }
Above is an updated version of our User
class. We added a new private method called maybeEncode
. The purpose of this method is to encode our password if it wasn’t encoded already. We do that by using two built-in PHP functions: password_needs_rehash
and password_hash
.
The maybeEncode
method starts with a guard clause. We use the password_needs_rehash
function to check if we need to encode the given password
. If we don’t need to, we return it. Otherwise, we password_hash
function to encode the given password
.
It’s also worth noting that both functions have a second argument where we pass the PASSWORD_BCRYPT
constant. This is the password algorithm constant that we’ll use to encode the given password
. You can also use the PASSWORD_DEFAULT
constant if you prefer to not specify an algorithm. That said, PASSWORD_BCRYPT
is the current PASSWORD_DEFAULT
value.
Using a method reflection
Now that we have a private method in our User
class, we can look at how to create a reflection for it. This is pretty straightforward and follows the similar pattern that we’ve seen so far. You can view the code below:
$userReflection = new ReflectionObject($user); $maybeEncodeReflection = $userReflection->getMethod('maybeEncode'); $maybeEncodeReflection->setAccessible(true); $maybeEncodeReflection->invoke($user, $password); $maybeEncodeReflection->setAccessible(false);
Unlike our previous example with the UserRepository
class, this is just a code sample. That said, you’ll notice that we once again start by reflecting our User
object using the ReflectionObject
class. You could have used the ReflectionClass
class instead if you wanted as well.
After that is where things diverge a bit. First, we do a call to the getMethod
method. We use it to ask for a reflection of the maybeEncode
method.
Next, we want to make the method accessible. We use the setAccessible
method like we did with our private property. This will allow us to use our maybeEncode
method.
To make a call to a method reflection, you have to use the invoke
method. The invoke
works the same way as the other reflection methods that we’ve seen so far. You have to first pass it the object that you want the reflection to interact with.
Generating a closure
There’s also another way to access a private method using reflections. It’s by generating a closure for the method that we want to reflect. Here’s an example to show you how to do it:
$userReflection = new ReflectionObject($user); $maybeEncodeReflection = $userReflection->getMethod('maybeEncode'); $maybeEncodeClosure = $maybeEncodeReflection->getClosure($user); $maybeEncodeClosure($password);
As you might have noticed, this is a modified version of our previous example. The first part of the example is the same. We created a reflection of our User
object. We then used that reflection to create a reflection of the maybeEncode
method.
The last section of the example is where things changed. First, we don’t use setAccessible
method. We don’t need to change the accessibility of the maybeEncode
method to use it in this scenario.
Instead, we only need to call the getClosure
method. This generates a Closure
object. This object calls the maybeEncode
method in the User
object that we passed it.
We do that on the last line. We use the maybeEncodeClosure
variable as a function. We pass it our password
as an argument.
A good foundation
This is only a fraction of all that you can do using reflections. As we said at the beginning, this covers the most common uses of reflections. There are a lot more classes and methods, but their use is a lot more situational.
With what you’ve seen here, you’re in good shape to use reflections to solve interesting problems. That said, most of its use will be around testing. Reflections and testing often go hand in hand.
So, if you’re interested in testing, this is all good knowledge to have! But regardless that you do testing or not (you should though!), reflections are important computer science concept. You might encounter them in another programming language later in your career.
Photo Credit: Erik Eastman