Reflection

Along with SPL, the reflection API is the other especially hard bit about PHP. However, it's hard for different reasons: SPL takes a bit of thinking to get yourself using the generic APIs, but reflection is hard simply because it invariably needs you to keep the manual close to hand to remember the proper usage!

Reflection is designed to reverse engineer various parts of PHP, including classes, functions, and extensions. By "reverse engineer" I mean that it gives you all sorts of information that otherwise you would need to try to dig out yourself. There are three primary uses for reflection:

  1. You have encoded scripts you need to interact with.

  2. The PHP manual isn't wholly up to date and you are unable to/don't want to read the source code.

  3. You're just curious how something works and would rather not read someone else's PHP source code.

As reflection is another new feature with PHP 5, it's not yet possible to judge its popularity. However, I can tell you that it was quite popular during beta testing of PHP because some parts of PHP (particularly SPL) didn't always have up-to-date documentation, which meant that using reflection made sure you always knew how things actually worked.

Anyway, let's take a look at the workings of reflection. Reflection itself is handled through various classes, the root of which is the Reflection class. There's also ReflectionClass, ReflectionExtension, ReflectionException, ReflectionFunction, ReflectionMethod, ReflectionObject, ReflectionParameter, and ReflectionProperty, for looking at various parts of your script.

To demonstrate some of these we're going to use the same sample script that gives us enough scope to try various things out:

<?php
    
class myparent {
        public function
foo($bar) {
            
// do stuff
        
}
    }

    class
mychild extends myparent {
        public
$val;

        private function
bar(myparent &$baz) {
            
// do stuff
        
}

        public function
__construct($val) {
            
$this->val = $val;
        }
    }

    
$child = new mychild('hello world');
    
$child->foo('test');
?>

What we have there are two classes, myparent and mychild, of which the second inherits from the first. Myparent has one function, foo(), that accepts one parameter. Mychild has a local variable ($val), one normal function (bar()) that takes one parameter, and also a constructor that takes one parameter too. At the end of the script we instantiate an object of type mychild, passing in a value for the constructor, then calling its foo() function.

Let's drop some reflection in there: what exactly does the mychild class offer us? Add this line to the end of the code:

$reflect = new ReflectionClass('mychild');
echo
$reflect;

Running the script now should output a lot of data:

Class [ <user> class mychild extends myparent ] {
  @@ C:\home\reflect.php 8-18

  - Constants [0] {
  }

  - Static properties [0] {
  }

  - Static methods [0] {
  }

  - Properties [1] {
    Property [ <default> public $val ]
  }

  - Methods [3] {
    Method [ <user> private method bar ] {
      @@ C:\home\reflect.php 11 - 13

      - Parameters [1] {
        Parameter #0 [ myparent &$baz ]
      }
    }

    Method [ <user> <ctor> public method __construct ] {
      @@ C:\home\reflect.php 15 - 17

      - Parameters [1] {
        Parameter #0 [ $val ]
      }
    }

    Method [ <user> public method foo ] {
      @@ C:\home\reflect.php 3 - 5

      - Parameters [1] {
        Parameter #0 [ $bar ]
      }
    }
  }
}

Author's Note: Reflection, quite correctly, refers to class functions as methods. This distinction is a little bit archaic, however: it is important in languages like C++, where a method and a function with the same signature (that is, returning the same type of value and accepting the same parameters) actually do end up being different, because the method gets a pointer to the object it's being called on as its first parameter, effectively shunting the other parameters to the right.

In PHP this doesn't happen, but the different terms are preserved anyway - what you call these these (functions, methods, "class functions", etc) is down to personal preference. The only real difference is that methods are inside objects, and therefore have the extra access modifiers (private, abstract, final, etc) and can have special actions (such as being a constructor). I've used "function" throughout - "method" is just too pernickety for me!

Although there are quite a few symbols in there - enough to make you need to concentrate to read it - the output is actually quite understandable. For example, it says there are three methods, then lists each of them. Note that it tells us that all three are user functions, all three take one parameter, etc. The @@ lines tell us where individual items were actually defined in the source code, which is great for jumping straight to something that interests you.

The actual two lines of code we used to export that information are simple: we create a new instance of the ReflectionClass class, passing in the class we're interested in as its only parameter. We then simply echo it out; internally this calls the __toString() method of ReflectionClass, which formats the output neatly. There is another popular way to export data, and it looks like this:

Reflection::export(new ReflectionClass('mychild'));

That gives the same result, except it's arguably harder to read! Also, our version has the advantage of having a ReflectionClass object held in a variable, which means we can do other things with it beyond printing it out. For example, change the current two reflection lines to this:

$childreflect = new ReflectionClass('mychild');
echo
"This class is abstract: ", (int)$childreflect->isAbstract(), "\n";
echo
"This class is final: ", (int)$childreflect->isFinal(), "\n";
echo
"This class is actually an interface: ", (int)$childreflect->isInterface(), "\n";

echo
"\$child is an object of this class: ", (int)$childreflect->isInstance($child), "\n";

$parentreflect = new ReflectionClass('myparent');
echo
"This class inherits from myparent: ", (int)$childreflect->isSubclassOf($parentreflect), "\n";

The output of that is:

This class is abstract: 0
This class is final: 0
This class is actually an interface: 0
$child is an object of this class: 1
This class inherits from myparent: 1

There are many other functions available in the ReflectionClass class - check out the online PHP manual for more information.

From here it's only a short hop to looking at the ReflectionMethod and ReflectionParameter classes. As you saw from the original ReflectionClass output, we get a full list of the methods available - let's rewrite that so we parse the available methods in depth, examining the nature of each parameter.

Replace the current reflection code with this:

$reflect = new ReflectionClass('mychild');

echo
"\nPrinting all methods in the 'mychild' class:\n";
echo
"============================================\n";

foreach(
$reflect->getMethods() as $reflectmethod) {
    echo
"  {$reflectmethod->getName()}()\n";
    echo
"  ", str_repeat("-", strlen($reflectmethod->getName()) + 2), "\n";

    foreach(
$reflectmethod->getParameters() as $num => $param) {
        echo
"    Param $num: \$", $param->getName(), "\n";
        echo
"      Passed by reference: ", (int)$param->isPassedByReference(), "\n";
        echo
"      Can be null: ", (int)$param->allowsNull(), "\n";

        echo
"      Class type: ";
        if (
$param->getClass()) {
            echo
$param->getClass()->getName();
        } else {
            echo
"N/A";
        }
        echo
"\n\n";
    }
}

Before I explain how all that code works, it will be beneficial for you to see what it creates:

Printing all methods in the 'mychild' class:
============================================
  bar()
  -----
    Param 0: $baz
      Passed by reference: 1
      Can be null: 0
      Class type: myparent

  __construct()
  -------------
    Param 0: $val
      Passed by reference: 0
      Can be null: 1
      Class type: N/A

  foo()
  -----
    Param 0: $bar
      Passed by reference: 0
      Can be null: 1
      Class type: N/A

The output is detailed information on all our functions, neatly formatted. To get that, took quite a few lines of code, but I think you'll agree it was worth it!

First up, we call getMethods() on our ReflectionClass object. This returns an array of ReflectionMethod objects, so the call is done inside a foreach loop. Each ReflectionMethod then has getName() called on it so we know what function we're working on, and we also do a bit of pretty printing with the str_repeat() function to make the output neater.

Next we go into another foreach loop, this time as a result of calling the getParameters() function of our ReflectionMethod class. Again, this returns an array, and so we loop through it with the foreach assigning the keys (incremental numbers, starting from 0) and their values to $num and $param. The values returned here are ReflectionParameter objects.

Once we have the individual parameters, the detailed information is printed out. It should all be self-explanatory there, except perhaps for the getClass() call. If you recall, class hints can be used to force a parameter to be an object of a particular type, and this is what getClass() returns. However, in true reflection fashion, it returns the class as as ReflectionClass object, meaning that you can then go off and explore that class as well.

So, as we don't really want a huge class dump here, instead we just call getClass() to see whether it's set or not (if there is no class hint, it returns false). If it's set, we call getName() on the class to print only it's name; otherwise, we just print N/A.

And that's it! The code is mostly about formatting things neatly, but it should really demonstrate how helpful reflection can be.

The last thing I want to look at is how to use reflection to get extension information, but it's very simple in comparison:

<?php
    $reflect
= new ReflectionExtension('simplexml');
    echo
$reflect;
?>

That will output all the available information on the SimpleXML extension. I'm not going to print it out here as it would be a colossal waste of space, however you should see that it lists the version number of the extension, as well as its functions and classes. For other extensions, it will also list any php.ini settings and their current values. Thanks to the way PHP's extension model works, you could try looking up the extension "standard" - the PHP standard functions - to get a larger dump of information to examine.

That concludes our brief excursion into reflection. It's a very new technology for PHP right now, and so it's likely to evolve over the coming releases as people figure out new ways to use it. For now, one of the neatest uses out there is PHP Reflector, available from http://dev.e-taller.net/reflector/ - it lets you graphically browse your PHP installation using reflection. At the time of writing there are still some bugs, but I can only presume these will be ironed out soon.

 

Next chapter: Browser detection >>

Previous chapter: The Standard PHP Library

Jump to:

 

Home: Table of Contents

Follow us on Identi.ca or Twitter

Username:   Password:
Create Account | About TuxRadar