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
- $modelReflector = new ReflectionClass('Model');
- $debugReflector = new ReflectionFunction('debug');
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.
- $modelReflector = new ReflectionClass('Model');
- $method = $modelReflector->getMethod('find');
- $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.
- /**
- * Returns a result set array.
- *
- * Also used to perform new-notation finds, where the first argument is type of find operation to perform
- * (all / first / count / neighbors / list / threaded ),
- * second parameter options for finding ( indexed array, including: 'conditions', 'limit',
- * 'recursive', 'page', 'fields', 'offset', 'order')
- *
- * Eg: find('all', array(
- * 'conditions' => array('name' => 'Thomas Anderson'),
- * 'fields' => array('name', 'email'),
- * 'order' => 'field3 DESC',
- * 'recursive' => 2,
- * 'group' => 'type'));
- *
- * Specifying 'fields' for new-notation 'list':
- * - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value.
- * - If a single field is specified, 'id' is used for key and specified field is used for value.
- * - If three fields are specified, they are used (in order) for key, value and group.
- * - Otherwise, first and second fields are used for key and value.
- *
- * @param array $conditions SQL conditions array, or type of find operation (all / first / count / neighbors / list / threaded)
- * @param mixed $fields Either a single string of a field name, or an array of field names, or options for matching
- * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC")
- * @param integer $recursive The number of levels deep to fetch associated records
- * @return array Array of records
- * @access public
- * @link http://book.cakephp.org/view/449/find
- */
- Method [ <user> public method find ] {
- @@ /usr/LOCAL/lib/php5/cakephp1.2/cake/libs/model/model.php 1908 - 1978
- - Parameters [4] {
- Parameter #0 [ <optional> $conditions = NULL ]
- Parameter #1 [ <optional> $fields = Array ]
- Parameter #2 [ <optional> $order = NULL ]
- Parameter #3 [ <optional> $recursive = NULL ]
- }
- }
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.
- function trigger($event, $args) {
- foreach ($this->_listeners as $listener) {
- $methods = $reflector->getMethods();
- foreach ($methods as $method) {
- if ('on' . $event == $method->getName()) {
- $method->invoke($listener, $args);
- }
- }
- }
- }
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.
- $object = new MyClass();
- $myClassReflection = new ReflectionClass('MyClass');
- $secret = $myClassReflection->getProperty('__secret');
- $secret->setAccessible(true);
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.
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 6/8/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 6/9/10
Hello, this article was one of the best about PHP reflection that I found.
Great job man.
Cheers
Vladica Savic on 6/15/10
Thanks for the concise example of using reflection
kurt krueckeberg on 8/6/10
mmm. am I the only one who creeped out by these toothy mouth images?
andrei on 12/31/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 1/30/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 1/30/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 7/19/11
Micheal: Glad to hear it helped.
mark story on 7/20/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 8/1/11
Very nice article!
Another good thing of using Reflection class is to test important private methods!
Xiaoming Cai on 12/5/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 1/4/13
I do not even know how I ended up right here, however I believed this
put up was great. I don’t ralize who you’re however cetainly you’re
going too a famous blogger in the event you aare not already.
Cheers!
nlp coaching books on 9/25/15