Reflection in PHP

Have you ever heard about introspecting or reverse-engineering classes? The PHP Reflection classes allow you to do that.

Disclaimer

The Reflection class names are quite self-explanatory, for example:

  • ReflectionTest
  • ReflectionClass
  • ReflectionObject
  • ReflectionFunction

I don’t want to give you an exhausting list of all Reflection classes and methods. IMHO, there’s no use knowing all that stuff as you will probably use 20% of them at most.

Instead, I want to discuss some concepts.

What is Reflection?

What does the “introspecting/reverse-engineering” feature mean?

It might look complicated, but it’s not. It means some code can inspect other code in the same system.

In other words, you can see what’s inside any class at runtime. To achieve that, you have a built-in set of classes, also called “Reflection classes” or “The Reflection API”, with useful methods to get all the information you need, including private and protected properties and methods.

Simple example

In the following PHP code, there seems no way I can get the level, even with a new instantiation, because it is private:

class DBZ {
    private $level = "saiyajin2";
}

It’s true until I do something like that:

class DBZ {
    private $level = "saiyajin2";
}

$dbz                = new DBZ();
$ReflectionProperty = new \ReflectionProperty(DBZ::class, "level");
$ReflectionProperty->setAccessible(true);
$level              = $ReflectionProperty->getValue($dbz);
print_r($level);

With the Reflection classes, you get access to whatever you want. The Reflection API provides various tools to check, get, and even execute things from other classes.

Source

When to use

To put this as simple as possible, I would say the Reflection API can be useful in the following cases:

  • code analysis
  • automating tasks
  • testing and mocking
  • using private fields from third-party libraries

There’s a pretty convenient package in Symfony for debugging: Var Dumper. If you look at the code base, you’ll see it uses the Reflection classes in several areas. For example, here with the Argstub class that represents a list of function arguments:

    private static function getParameters(string $function, ?string $class): array
    {
        if (isset(self::$parameters[$k = $class.'::'.$function])) {
            return self::$parameters[$k];
        }

        try {
            $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function);
        } catch (\ReflectionException $e) {
            return [null, null];
        }

By the way, PHP 7.4 introduced a new Reflection class called ReflectionReference, especially for tools such as the var-dumper that heavily rely on the Reflection API.

Hum, what do you mean by Reflection?

Reflection in PHP (and other languages that support it) means the code can inspect and modify itself while running.

PHP uses the same internal mechanism in the builtin functions get_class() or call_user_func(). The Reflection API is not the only way to get information. However, it does it with an object-oriented approach, and it handles errors and exceptions (ReflectionException).

I was explicitly told not to use Reflection

As we saw earlier, the Reflection API allows for getting private properties. It has some cost, though.

Replacing all method_exists() with its equivalent in the Reflection API is a bad idea.

If you need speed, for example, for a lot of iterations, then any reflection technique might be slower, whether it’s the Reflection API or some builtin PHP functions.

For that purpose, getters and setters might be faster. However, Reflection might be cleaner. You cannot use setters and getters for everything and anything. It does not make sense all the time.

Wrap up

The Reflection API is especially useful when automating tasks, running tests, or using third-party libraries.

The concept of Reflection goes way beyond PHP, and many languages support it.

It’s also another way to browse code for developers.