Practical uses for reflection

Tags:

You may have heard about the reflection feature in PHP. Simply put, it’s a way to get a list of methods in a class, a methods parameters, or other “internal” things like that. But how is this actually useful for any common task in application development?

A usage scenario

Most web applications use forms. Forms often represent some model, such as a newspost. If the model is simple, writing the form in HTML is not too bad, or we might be able to use a scaffolding feature in our framework.

But what if the model has lots of data in it, and we are just writing some small application, which doesn’t necessarily require a big framework with features for form generation?

Here’s a good use for reflection! Since in models we often have a naming scheme for the data the users would be able to input, we can utilize this knowledge with reflection to generate a list of possible fields in the model, and then generate the form with less typing required from us.

Example implementation

Let’s use the newspost as an example. Typically, you would have things such as title, description, who wrote it, publication time, and maybe some additional fields like edit time, and whether or not comments are enabled.


example model

The model for this would usually contain methods like setTitle, getTitle etc. for setting and gettings each value. It might also have some additional methods, but we won’t need to think of them now.

By using reflection, we can get the list of methods in the model. Since we also know that each of available fields will have a getter, we can loop through the methods and just get every method which starts with get:

$cls = new ReflectionClass('NewsPost');
$methods = $cls->getMethods();
 
$fields = array();
foreach($methods as $method) {
  if(substr($method->getName(), 0, 3) == 'get') {
    //Just take everything after get as the field name
    $fields[] = substr($method->getName(), 3);
  }
}

Now the fields array will hold each field in our class, and we can use that to generate a form without writing lots of HTML:

<?php foreach($fields as $field): ?>
  <p>
    <label for="<?php echo $field; ?>"><?php echo $field; ?></label>
    <input type="text" name="<?php echo $field; ?>" id="<?php echo $field; ?>" />
  </p>
<?php endforeach; ?>

Depending on your model class, you may wish to perform additional formatting of the field names, so using it as the label will make more sense, such as replacing “CamelCase” with “Camel case”:

$labels = array();
foreach($fields as $field) {
  //Using temp variable for the sake of readability
  $label = preg_replace('/([a-z])([A-Z])/', '$1 $2', $field);
  $label = ucfirst(strtolower($label));
  $labels[] = $label;
}

Further improving the form generation

As mentioned earlier, the news post could have a boolean value for whether or not comments are enabled. This can’t really be represented by a text input box.

What if there was a way to know the value returned by the getter? In that case we could just show a checkbox… but oh wait, there is a way!

/**
 * Are comments enabled?
 * @return bool
 */
public function getCommentsEnabled()

Another reason to write docblocks for your methods – We can use reflection to read the docblock, and read any annotations such as the @return value.

We’ll modify our earlier example of getting the fields to include a field type:

$cls = new ReflectionClass('NewsPost');
$methods = $cls->getMethods();
 
$fields = array();
foreach($methods as $method) {
  if(substr($method->getName(), 0, 3) == 'get') {
    //the field is a textbox by default
    $field = array(
      'name' => substr($method->getName(), 3),
      'type' => 'text'
    );
 
    //we want bools to show up as checkboxes
    if(strpos($method->getDocComment(), '@return bool') !== false) {
      $field['type'] = 'checkbox';
    }
 
    $fields[] = $field;
  }
}

We can use the method getDocComment() to access the docblock of a method. We can then use strpos to check if @return bool exists in the docblock.

Conclusion

Reflection, a feature which may seem a bit useless for everyday tasks, can actually be very useful with a little ingenuity. We could extend the examples shown in the post to even include code for parsing the input from the form after its submission, but I will leave that as an excercise to you.

Have you used reflection to do something interesting? Please post your ideas/applications to comments!