Using the PHP Reflection API for fun and profit

When PHP got a real object oriented system in 5.0, it also got a neat feature taken from Java land. Reflection allows you to introspect & reverse engineer functions, classes, and extensions. In addition you can use reflection to extraction of documentation from classes and functions. In PHP Reflection is done using a number of Reflection classes.

Using reflection

Reflection starts by creating a new Reflector, you do this like so

Show Plain Text
  1.  
  2. $modelReflector = new ReflectionClass('Model');
  3. $debugReflector = new ReflectionFunction('debug');
  4.  

Reflection constructors take the name of the class or function you wish to reflect. Once you have a reflection of the class you want to look at there are quite a few things you can do with it. You can find out what properties it has with $reflector->getProperties(), see what methods it has with $reflector->getMethods() and so on. In addition to getting methods, you can find out a great deal about the methods. You can find out what arguments they take, and which arguments are required, and which are optional.

Show Plain Text
  1. $modelReflector = new ReflectionClass('Model');
  2. $method = $modelReflector->getMethod('find');
  3. $parameters = $method->getParameters();

Will give you a list of ReflectionParameter objects, that you could iterate over and find which are required and get any type hints that are provided. If we were to dump the $method object we created we would see the following.

Show Plain Text
  1. /**
  2.  * Returns a result set array.
  3.  *
  4.  * Also used to perform new-notation finds, where the first argument is type of find operation to perform
  5.  * (all / first / count / neighbors / list / threaded ),
  6.  * second parameter options for finding ( indexed array, including: 'conditions', 'limit',
  7.  * 'recursive', 'page', 'fields', 'offset', 'order')
  8.  *
  9.  * Eg: find('all', array(
  10.  *                  'conditions' => array('name' => 'Thomas Anderson'),
  11.  *                  'fields' => array('name', 'email'),
  12.  *                  'order' => 'field3 DESC',
  13.  *                  'recursive' => 2,
  14.  *                  'group' => 'type'));
  15.  *
  16.  * Specifying 'fields' for new-notation 'list':
  17.  *  - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value.
  18.  *  - If a single field is specified, 'id' is used for key and specified field is used for value.
  19.  *  - If three fields are specified, they are used (in order) for key, value and group.
  20.  *  - Otherwise, first and second fields are used for key and value.
  21.  *
  22.  * @param array $conditions SQL conditions array, or type of find operation (all / first / count / neighbors / list / threaded)
  23.  * @param mixed $fields Either a single string of a field name, or an array of field names, or options for matching
  24.  * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
  25.  * @param integer $recursive The number of levels deep to fetch associated records
  26.  * @return array Array of records
  27.  * @access public
  28.  * @link http://book.cakephp.org/view/449/find
  29.  */
  30. Method [ <user> public method find ] {
  31.   @@ /usr/LOCAL/lib/php5/cakephp1.2/cake/libs/model/model.php 1908 - 1978
  32.  
  33.   - Parameters [4] {
  34.     Parameter #0 [ <optional> $conditions = NULL ]
  35.     Parameter #1 [ <optional> $fields = Array ]
  36.     Parameter #2 [ <optional> $order = NULL ]
  37.     Parameter #3 [ <optional> $recursive = NULL ]
  38.   }
  39. }

Which gives us a nice summary, including the doc block, and the parameter list, as well as describing where the method lives on the file system.

Well that’s very nice, but what can I do with this.

Well there are few things you can do with Reflection. The easiest is make documentation!, Reflection powers the current API at http://api.cakephp.org. Another good use of reflection would be dynamically invoking methods that a class has. By using reflection you can find out if a method with a given name exists, and invoke it.

Show Plain Text
  1. function trigger($event, $args) {
  2.     foreach ($this->_listeners as $listener) {
  3.         $reflector = new ReflectionClass(get_class($listener));
  4.         $methods = $reflector->getMethods();
  5.         foreach ($methods as $method) {
  6.             if ('on' . $event == $method->getName()) {
  7.                 $method->invoke($listener, $args);
  8.             }
  9.         }
  10.     }
  11. }

Sure you could probably recreate this simple example with method_exists however, reflection will more elegantly handle argument detection for the methods about to be invoked. In addition to looking at methods and properties and invoking methods, you can set static properties. In PHP 5.3 and beyond you will be able to generate closures of methods, as well as coerce properties to public for reading purposes.

Show Plain Text
  1. $object = new MyClass();
  2.  
  3. $myClassReflection = new ReflectionClass('MyClass');
  4. $secret = $myClassReflection->getProperty('__secret');
  5. $secret->setAccessible(true);
  6. var_dump($secret->getValue($object));

If you were to try and write MyClass::$__secret you would get an exception. However, read access allows you to create an effective Object Freezer. In addition to freezing objects, you could easily use Reflection to generate mock objects of classes if you so wished. By introspecting on the methods and required parameters you could effectively emulate any objects interface and generate a compatible object.

So while Reflection may be a fairly specialized tool it has a lot of potential and can really shine under the right applications.

Comments

Hi, I want to know if it possible to create blank method on runtime in php? something like

$object->mynewattr

for methods?

Jaime Hablutzel on 8/6/10

Jaime: Unfortunately you cannot append methods to classes at runtime in PHP. You could use __call and method delegation to provide a similar end result though.

mark story on 9/6/10

Hello, this article was one of the best about PHP reflection that I found.

Great job man.

Cheers

Vladica Savic on 15/6/10

Thanks for the concise example of using reflection

kurt krueckeberg on 6/8/10

mmm. am I the only one who creeped out by these toothy mouth images?

andrei on 31/12/10

@andrei nope. me too! LOL

Jon on 1/1/11

Do you guys know a way to create classes on the fly?

something less crappy than this:

$object = eval(‘class Fake extends realAbstact{}; return new Fake;’);

$this->value = $object->method($value);

Charlie on 30/1/11

I guess __autoload() might work.

similar question here:
http://stackoverflow.com/questions/1203625/dynamically-generate-classes-at-runtime-in-php

generally – stackoverflow is the best site to ask questions.

andrei on 30/1/11

This is a nice tutorial , this was one of the best i have got on Reflection in php

admire on 4/4/11

Great article, was having trouble putting the Reflection classes to a real world use to understand them but the documentation example really helped!

Michael on 19/7/11

Micheal: Glad to hear it helped.

mark story on 20/7/11

I used Reflection in my application. when i use it in some cases there is exit(4) illegal instruction pid thrown, something to do with the destructor. Any clues of what might cause reflection to throw an issue when used on multiple classes.

Santhosh on 1/8/11

Very nice article!

Another good thing of using Reflection class is to test important private methods!

Xiaoming Cai on 5/12/12

Hello Mark,

I found your site looking for information on Triggers and Hooks.

It looks like your Trigger function is part of something bigger? Do you have the rest of the code? is it from another project?

I would really like to play around with it, I am building a CMS and the current Trigger system I am using seems to execute the plugin when I add the alias.

Its not very Optimal.

Cheers!

Adam Patterson on 4/1/13

Have your say:

*
* You can use Textile markup, but be reasonable