Geniet van

Reflection; use what you have typed already!

Isn‘t it weird that we are type casting parameters and variables while you have already mentioned what type it should be?

As I am an old guy within the TYPO3 core team, I'm still familiar with assembly language. Using the assembly language you filled physical registers inside a computer. Things like composition, values and methods could be inspected at any time by reading the registers. This was reflective.

What does reflective mean?

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.

Whut?

Without knowing, you are already using it, partly. PHP functions like get_class() and get_class_methods() are reflection functions. Or what to think about method_exists() or __get() or __set() magic methods to see if a getter or setter method is available on the object.

But reflection goes further than that.

When being a good PHP developer, you probably use PHPDoc. You document all variables, methods, classes and interfaces. You show that you know what type a variable of parameter has thoughout the whole execution of a program. But as the name says, it is documentation, hence the "doc" part. A program or piece of code is not inspecting itself using the PHPDoc. You have to do it yourself. And this is where PHP reflection can help.

An example

Below is a very small part of a "car" object. Every car, except for a DeLorean which one of my former bosses owned, has a certain color, sprayed with paint. Car manufacturers use codes for identifying these colors, mostly a string.

/**
 * The color code of the car
 *
 * @var string
 */
public $color;

/**
 * Set the color code of the car
 *
 * @param string $color The color code
 */
public function setColor($color)
{
    $this->color = $color;
}

In an ideal world, the @var and @param tags would type cast the object variable and the method parameter automatically to a string. But that is not how it works in the PHP world, unfortunately. PHP does not require explicit type definition in a variable declaration. We can insert an array as the parameter $color in the method SetColor, and the object variable will become an array. Weird, isn't it? As a starting PHP developer I would be flabbergasted that this is not done automatically. Why use all the effort to mention the type then?

As experienced developers we are taking this for granted and do the type casting ourselves, like in the next piece of code.

/**
 * The color code of the car
 *
 * @var string
 */
public $color;

/**
 * Set the color code of the car
 *
 * @param string $color The color code
 */
public function setColor($color)
{
    $this->color = (string)$color;
}

Take a close look. There is only one very small difference, and that is the name of the desired type, written in parentheses, before the variable. This will cast this variable to the type of "string".

So let's use type casting

Type casting is a good thing to do, but it can get confusing. On which level do you use type casting? The above example is perfect Object Oriented Programming, in short OOP, where you have an object using getters and setters. But this is not always the case. Then the question starts where to use type casting.

What about type declaration, or type hinting ?

Type declarations, previously known as type hinting, allow functions to specify parameters as certain types. It exists since PHP5, but was only partly implemented. In TYPO3 7LTS and even older versions you will see that sometimes type declarations were used for arrays and class or interface names. That is almost as far as type declarations go in PHP5, but for the record here is the full list:

Type Description Minimum PHP version
Class/interface name The parameter must be an instanceof the given class or interface name. PHP 5.0.0
self The parameter must be an instanceof the same class as the one the method is defined on. This can only be used on class and instance methods. PHP 5.0.0
array The parameter must be an array. PHP 5.1.0
callable The parameter must be a valid callable. PHP 5.4.0

Using the example from above we could say a color code is not enough for a color. I suppose you all are aware how colors are constructed. Most of them exists of a combination of multiple colors, the primary colors. So actually a color should be an object where the amount of primary colors are declared to get this single color.

We could have a method like this with a type declaration

/**
 * Set the color code of the car
 *
 * @param \CarVendor\Color $color The color object
 */
public function setColor(\CarVendor\Color $color)
{
    $this->color = $color;
}

Damn, still twice the notation, once in the PHPDoc and as a type declaration in the method.

As a side note I never got it why array was added for type declarations and not any other types, like integer and string, in PHP5. Luckily PHP7 is finally changing this. Here are the type declarations you can use from PHP 7.0.0:

Type Description Minimum PHP version
bool The parameter must be a boolean value. PHP 7.0.0
float The parameter must be a floating point number. PHP 7.0.0
int The parameter must be an integer. PHP 7.0.0
string The parameter must be a string. PHP 7.0.0

A difference between PHP5 and PHP7 is that PHP5 will generate a recoverable fatal error, while PHP 7 will throw a TypeError exception when the type does not match.

Let's get back to reflection

One of the main advantages of reflection is that you can get a lot of information out of the PHPDoc. Not only the type of a variable but you probably are using the @return or @throws tag as well. Some of these tags are standardized, but you are allowed to use your own. And this is where the fun starts.

Say you have an extension which accepts query parameters from a URI. Normally in TYPO3 the query parameters are passed as an array with the name of the extension, like tx_extensionname[parameter-name] = value. At all times you should know of what type the query parameter is. A controller is a string, a uid an integer.

TYPO3 7LTS embraced PSR-7: HTTP message interfaces. Simply said, these interfaces handle HTTP request messages that are sent to a web server. This includes the query parameters I mentioned before. PSR-7 does not care about the types of the query parameters. But you can always embed it yourself. That is why I extend the HTTP request object with my own query parameters object. Here is a very small example:

/**
 * The HTTP request
 */
class QueryParameters implements SingletonInterface
{
    /**
     * The controller name
     *
     * @allowed Employee, Workplace, Professor, Publication, Department, Expert, Search
     * @queryParameter controller
     * @var string
     */
    public $controllerName;

The only standardized tag used here is @var, the other ones (@allowed and @queryParameter) I've added myself.

Using the PHP ReflectionClass I can read all the properties of the QueryParameters object, in this case only the public property $controllerName. Iterating over these properties, I can get the values of the property tags, in this case the values of @allowed, @queryParameter and @var. Using all these values I can decide if and how the property $controllerName will be filled with the query parameter coming from the HTTP request. If the controller name in the query parameter "controller" does not match the @allowed property tag, I will deny setting the property. Oh yeah, and of course I do automated type casting.

Hey, I know that from Extbase

Yes, you have seen something similar before when using Extbase. But did you never wonder how this is done in Extbase?

In my daily work I hardly use Extbase. Most of the time I don't need it and it comes with a price; It is a performance killer on big websites with high traffic. Bootstrapping Extbase is not cheap when it comes to performance. With the site volumes I'm used to (100.000+ pages) you always think twice before starting to write an extension with Extbase. In my opinion Extbase is only worth using when you use the whole featureset. But I've written another article about that ;-)

Now what to use?

Since the introduction of a better set of type declarations in methods/functions this will certainly help getting rid of most of the type casting. However, type casting might be necessary in some cases. But both methods still keep you typing things twice; Once in the PHPDoc and once with the actual casting.

Reflection gives you more opportunities than type casting or type declaration.

Reflection goes much further than I have described in this article. Reflection allows you to inspect PHP objects at runtime. By being able to inspect the characteristics and capabilities of an object, you open up a whole new world of opportunity for using objects in an object oriented world. The reason for this article was to show you how you can make advantage of extracting information from the PHPDoc instead of doing type casting or type declarations.

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.