Reflection is generally defined as a program's ability to inspect itself and modify its logic at execution time. In less technical terms, reflection is asking an object to tell you about its properties and methods, and altering those members (even private ones). In this lesson, we'll dig into how this is accomplished, and when it might prove useful.
A Little History
At the dawn of the age of programming, there was the assembly language. A program written in assembly resides on physical registers inside the computer. Its composition, methods and values could be inspected at any time by reading the registers. Even more, you could alter the program while it was running by simply modifying those registers. It required some intimate knowledge about the running program, but it was inherently reflective.
As with any cool toy, use reflection, but don't abuse it.
As higher-level programming languages (like C) came along, this reflectivity faded and disappeared. It was later re-introduced with object-oriented programming.
Today, most programming languages can use reflection. Statically typed languages, such as Java, have little to no problems with reflection. What I find interesting, however, is that any dynamically-typed language (like PHP or Ruby) is heavily based on reflection. Without the concept of reflection, duck-typing would most likely be impossible to implement. When you send one object to another (a parameter, for example), the receiving object has no way of knowing the structure and type of that object. All it can do is use reflection to identify the methods that can and cannot be called on the received object.
A Simple Example
Reflection is prevalent in PHP. In fact, there are several situations when you may use it without even knowing it. For example:
// Nettuts.php require_once 'Editor.php'; class Nettuts { function publishNextArticle() { $editor = new Editor('John Doe'); $editor->setNextArticle('135523'); $editor->publish(); } }
And:
// Editor.php class Editor { private $name; public $articleId; function __construct($name) { $this->name = $name; } public function setNextArticle($articleId) { $this->articleId = $articleId; } public function publish() { // publish logic goes here return true; } }
In this code, we have a direct call to a locally initialized variable with a known type. Creating the editor in publishNextArticle()
makes it obvious that the $editor
variable is of type Editor
. No reflection is needed here, but let's introduce a new class, called Manager
:
// Manager.php require_once './Editor.php'; require_once './Nettuts.php'; class Manager { function doJobFor(DateTime $date) { if ((new DateTime())->getTimestamp() > $date->getTimestamp()) { $editor = new Editor('John Doe'); $nettuts = new Nettuts(); $nettuts->publishNextArticle($editor); } } }
Next, modify Nettuts
, like so:
// Nettuts.php class Nettuts { function publishNextArticle($editor) { $editor->setNextArticle('135523'); $editor->publish(); } }
Now, Nettuts
has absolutely no relation to the Editor
class. It does not include its file, it does not initialize its class and it does not even know it exists. I could pass an object of any type into the publishNextArticle()
method and the code would work.
As you can see from this class diagram, Nettuts
only has a direct relationship to Manager
. Manager
creates it, and therefore, Manager
depends on Nettuts
. But Nettuts
no longer has any relation to the Editor
class, and Editor
is only related to Manager
.
At runtime, Nettuts
uses an Editor
object, thus the <<uses>> and the question mark. At runtime, PHP inspects the received object and verifies that it implements the setNextArticle()
and publish()
methods.
Object Member Information
We can make PHP display the details of an object. Let's create a PHPUnit test to help us easily exercise our code:
// ReflectionTest.php require_once '../Editor.php'; require_once '../Nettuts.php'; class ReflectionTest extends PHPUnit_Framework_TestCase { function testItCanReflect() { $editor = new Editor('John Doe'); $tuts = new Nettuts(); $tuts->publishNextArticle($editor); } }
Now, add a var_dump()
to Nettuts
:
// Nettuts.php class NetTuts { function publishNextArticle($editor) { $editor->setNextArticle('135523'); $editor->publish(); var_dump(new ReflectionClass($editor)); } }
Run the test, and watch the magic happen in the output:
PHPUnit 3.6.11 by Sebastian Bergmann. .object(ReflectionClass)#197 (1) { ["name"]=> string(6) "Editor" } Time: 0 seconds, Memory: 2.25Mb OK (1 test, 0 assertions)
Our reflection class has a name
property set to the original type of the $editor
variable: Editor
, but that's not much information. What about Editor
's methods?
// Nettuts.php class Nettuts { function publishNextArticle($editor) { $editor->setNextArticle('135523'); $editor->publish(); $reflector = new ReflectionClass($editor); var_dump($reflector->getMethods()); } }
In this code, we assign the reflection class' instance to the $reflector
variable so that we can now trigger its methods. ReflectionClass
exposes a large set of methods that you can use to obtain an object's information. One of these methods is getMethods()
, which returns an array containing each method's information.
PHPUnit 3.6.11 by Sebastian Bergmann. .array(3) { [0]=> &object(ReflectionMethod)#196 (2) { ["name"]=> string(11) "__construct" ["class"]=> string(6) "Editor" } [1]=> &object(ReflectionMethod)#195 (2) { ["name"]=> string(14) "setNextArticle" ["class"]=> string(6) "Editor" } [2]=> &object(ReflectionMethod)#194 (2) { ["name"]=> string(7) "publish" ["class"]=> string(6) "Editor" } } Time: 0 seconds, Memory: 2.25Mb OK (1 test, 0 assertions)
Another method, getProperties()
, retrieves the properties (even private properties!) of the object:
PHPUnit 3.6.11 by Sebastian Bergmann. .array(2) { [0]=> &object(ReflectionProperty)#196 (2) { ["name"]=> string(4) "name" ["class"]=> string(6) "Editor" } [1]=> &object(ReflectionProperty)#195 (2) { ["name"]=> string(9) "articleId" ["class"]=> string(6) "Editor" } } Time: 0 seconds, Memory: 2.25Mb OK (1 test, 0 assertions)
The elements in the arrays returned from getMethod()
and getProperties()
are of type ReflectionMethod
and ReflectionProperty
, respectively; these objects are quite useful:
// Nettuts.php class Nettuts { function publishNextArticle($editor) { $editor->setNextArticle('135523'); $editor->publish(); // first call to publish() $reflector = new ReflectionClass($editor); $publishMethod = $reflector->getMethod('publish'); $publishMethod->invoke($editor); // second call to publish() } }
Here, we use getMethod()
to retrieve a single method with the name of "publish"; the result of which is a ReflectionMethod
object. Then, we call the invoke()
method, passing it the $editor
object, in order to execute the editor's publish()
method a second time.
This process was simple in our case, because we already had an Editor
object to pass to invoke()
. We may have several Editor
objects in some circumstances, giving us the luxury of choosing which object to use. In other circumstances, we may have no objects to work with, in which case we would need to obtain one from ReflectionClass
.
Let's modify Editor
's publish()
method to demonstrate the double call:
// Editor.php class Editor { [ ... ] public function publish() { // publish logic goes here echo ("HERE\n"); return true; } }
And the new output:
PHPUnit 3.6.11 by Sebastian Bergmann. .HERE HERE Time: 0 seconds, Memory: 2.25Mb OK (1 test, 0 assertions)
Manipulating Instance Data
We can also modify code at execution time. What about modifying a private variable that has no public setter? Let's add a method to Editor
that retrieves the editor's name:
// Editor.php class Editor { private $name; public $articleId; function __construct($name) { $this->name = $name; } [ ... ] function getEditorName() { return $this->name; } }
This new method is called, getEditorName()
, and simply returns the value from the private $name
variable. The $name
variable is set at creation time, and we have no public methods that let us change it. But we can access this variable using reflection. You might first try the more obvious approach:
// Nettuts.php class Nettuts { function publishNextArticle($editor) { var_dump($editor->getEditorName()); $reflector = new ReflectionClass($editor); $editorName = $reflector->getProperty('name'); $editorName->getValue($editor); } }
Even though this outputs the value at the var_dump()
line, it throws an error when trying to retrieve the value with reflection:
PHPUnit 3.6.11 by Sebastian Bergmann. Estring(8) "John Doe" Time: 0 seconds, Memory: 2.50Mb There was 1 error: 1) ReflectionTest::testItCanReflect ReflectionException: Cannot access non-public member Editor::name [...]/Reflection in PHP/Source/NetTuts.php:13 [...]/Reflection in PHP/Source/Tests/ReflectionTest.php:13 /usr/bin/phpunit:46 FAILURES! Tests: 1, Assertions: 0, Errors: 1.
In order to fix this problem, we need to ask the ReflectionProperty
object to grant us access to the private variables and methods:
// Nettuts.php class Nettuts { function publishNextArticle($editor) { var_dump($editor->getEditorName()); $reflector = new ReflectionClass($editor); $editorName = $reflector->getProperty('name'); $editorName->setAccessible(true); var_dump($editorName->getValue($editor)); } }
Calling setAccessible()
and passing true
does the trick:
PHPUnit 3.6.11 by Sebastian Bergmann. .string(8) "John Doe" string(8) "John Doe" Time: 0 seconds, Memory: 2.25Mb OK (1 test, 0 assertions)
As you can see, we've managed to read private variable. The first line of output is from the object's own getEditorName()
method, and the second comes from reflection. But what about changing a private variable's value? Use the setValue()
method:
// Nettuts.php class Nettuts { function publishNextArticle($editor) { var_dump($editor->getEditorName()); $reflector = new ReflectionClass($editor); $editorName = $reflector->getProperty('name'); $editorName->setAccessible(true); $editorName->setValue($editor, 'Mark Twain'); var_dump($editorName->getValue($editor)); } }
And that's it. This code changes "John Doe" to "Mark Twain".
PHPUnit 3.6.11 by Sebastian Bergmann. .string(8) "John Doe" string(10) "Mark Twain" Time: 0 seconds, Memory: 2.25Mb OK (1 test, 0 assertions)
Indirect Reflection Use
Some of PHP's built-in functionality indirectly uses reflection—one being the call_user_func()
function.
The Callback
The call_user_func()
function accepts an array: the first element pointing to an object, and the second a method's name. You can supply an optional parameter, which is then passed to the called method. For example:
// Nettuts.php class Nettuts { function publishNextArticle($editor) { var_dump($editor->getEditorName()); $reflector = new ReflectionClass($editor); $editorName = $reflector->getProperty('name'); $editorName->setAccessible(true); $editorName->setValue($editor, 'Mark Twain'); var_dump($editorName->getValue($editor)); var_dump(call_user_func(array($editor, 'getEditorName'))); } }
The following output demonstrates that the code retrieves the proper value:
PHPUnit 3.6.11 by Sebastian Bergmann. .string(8) "John Doe" string(10) "Mark Twain" string(10) "Mark Twain" Time: 0 seconds, Memory: 2.25Mb OK (1 test, 0 assertions)
Using a Variable's Value
Another example of indirect reflection is calling a method by the value contained within a variable, as opposed to directly calling it. For example:
// Nettuts.php class Nettuts { function publishNextArticle($editor) { var_dump($editor->getEditorName()); $reflector = new ReflectionClass($editor); $editorName = $reflector->getProperty('name'); $editorName->setAccessible(true); $editorName->setValue($editor, 'Mark Twain'); var_dump($editorName->getValue($editor)); $methodName = 'getEditorName'; var_dump($editor->$methodName()); } }
This code produces the same output as the previous example. PHP simply replaces the variable with the string it represents and calls the method. It even works when you want to create objects by using variables for class names.
When Should We Use Reflection?
Now that we've put the technical details behind us, when should we leverage reflection? Here are a few scenarios:
- Dynamic typing is probably impossible without reflection.
- Aspect Oriented Programming listens from method calls and places code around methods, all accomplished with reflection.
- PHPUnit relies heavily on reflection, as do other mocking frameworks.
- Web frameworks in general use reflection for different purposes. Some use it to initialize models, constructing objects for views and more. Laravel makes heavy use of reflection to inject dependencies.
- Metaprogramming, like our last example, is hidden reflection.
- Code analysis frameworks use reflection to understand your code.
Final Thoughts
As with any cool toy, use reflection, but don't abuse it. Reflection is costly when you inspect many objects, and it has the potential to complicate your project's architecture and design. I recommend that you make use of it only when it actually gives you an advantage, or when you have no other viable option.
Personally, I've only used reflection in a few instances, most commonly when using third party modules that lack documentation. I find myself frequently using code similar to the last example. It's easy to call the proper method, when your MVC responds with a variable containing "add" or "remove" values.
Thanks for reading!
Comments