LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Monday, October 9. 2006

PHP 5's Reflection API

As Davey Shafik noted recently, he and I have been working together on some web services for the Zend Framework. In doing so, I've become very familiar with PHP 5's Reflection API, and am coming to love it.

When I first read about the Reflection API in a pre-PHP 5 changelog, my initial reaction was, "who cares?" I simply failed to see how it was a useful addition to the language. Having done some projects recently that needed to know something about the classes they are using, I now understand when and how it can be used. It shines when you need to work with classes that may not be defined when you write your code -- any code that dispatches to other classes, basically.

So, what sorts of things can you do with the Reflection API? Here's a list of some of the things I've done:

  • Determine if a method exists in a class
  • Retrieve and grab metainformation from a class, method, or function's phpdoc docblock (I used this heavily in developing Zend_Server_Reflection)
  • Determine if a method is static, public, private, or protected
  • Retrieve function/method parameters and determine position, whether or not the parameter is optional and what the default value might be, and the name of the parameter (i.e., what variable name is used to identify it)
  • Invoke a function or method, with a variable number of arguments. This can be used in place of call_user_func/call_user_func_array(), and $method->invoke() allows for static method calls as well (I had to file a bug with php.net as static calls aren't allowed for invokeArgs()).
  • Instantiate an object instance with a variable number of arguments to the constructor using newInstanceArgs(). This is something I've looked for for some time now; previously, the only solutions were to use eval() (yuck!) or have your constructors all accept an associative array of arguments. This is a much nicer, more flexible, solution.

The various Reflection classes can all be extended. However, since they're all very interrelated, I've found it easier to proxy them, and override methods as necessary. For instance, in the Zend_Server_Reflection tree, I needed class reflection to return an array of Zend_Server_Reflection_Methods, which did quite a few pieces of introspection on the docblock (getting method prototypes, hinting to parameters the variable types and descriptions, etc.). So, I defined something like this:


class Reflection_Class
{
    public function __construct(ReflectionClass $r)
    {
        $this->_reflection = $r;

        foreach ($r->getMethods() as $method) {
            $this->_methods[] = new Reflection_Method($method);
        }
    }

    public function __call($method, $args)
    {
        if (method_exists($r, $method)) {
            return $r->{$method}($args);
        }
    }

    public function getMethods()
    {
        return $this->_methods;
    }
}
 

Obviously, this is shorthand, but you get the idea.

The Reflection API came in very handy with the various server components as we could have a central set of classes for doing function and class introspection that could then be used to define the dispatch callbacks the server could utilize. Additionally, I implemented a __wakeup() method that basically restores the entire reflection architecture, allowing us to serialize the server definitions between calls -- which greatly reduces the amount of processing that needs to occur on subsequent calls.

We're also using it in the MVC components, specifically in the Dispatchers. Again, this allows us to (a) determine if a method exists for dispatch, and (b) call it with any arguments we may need. It also allows us to easily instantiate action controller objects using variable numbers of arguments.

If you're doing any sort of coding for a plugin architecture, I highly recommend getting to know the Reflection API; it's very powerful and can add some very nice, simple, flexibility to your code.

Posted by Matthew Weier O'Phinney in PHP at 10:58 | Comments (2) | Trackbacks (2)

Trackbacks
Trackback specific URI for this entry

Matthew O'Phinney's Blog: PHP 5's Reflection API
Weblog: PHPDeveloper.org
Tracked: Oct 09, 14:26
Zend_XmlRpc_Server
As noted previously by myself and Davey, I've been working on Zend_XmlRpc_Server for some months now. In the past couple weeks, I've refactored it to push the class/function reflection into Zend_Server_Reflection, and, in doing
Weblog: phly, boy, phly
Tracked: Oct 15, 22:24

Comments
Display comments as (Linear | Threaded)

Just curious.

Why do you feel you should/need use reflection to determine if a method exists when PHP has the method_exists function?

The only benefit I see reflection having over the function method_exists is you can determine the exiatance of a method/function on the source before it's parsed/executed.

In which case, why not just use method_exists? Unless your developing a code generator of somekind and needed to know pre-runtime whether the method was defined?

Cheers :-)
#1 Hockey on 2007-12-10 04:50 (Reply)
method_exists() is fine if you have already instantiated an object in the class. However, when working with an XML-RPC server or other RPC-like server (Zend_Rest_Server is more RPC-like than RESTful, for instance), you may attach a number of classes, but you'll only need to instantiate one to answer the request. So, why bother creating a bunch of objects you'll ultimately never use?

Additionally, we need to build the method map. As I noted, there may be more than one class associated with the server; would you run method_exists() over each and every class? And what happens if multiple classes have the same method? (In XML-RPC, this is not a problem due to its pseudo-namespaces.)

Finally, it's not just a matter of determining if a given method exists. We're using the Reflection API to determine number and type of arguments (the latter is done by also examining the phpdoc blocks), return values (also by examining docblocks), and 'method help text' (also from docblocks). Since only public methods are allowed to be exposed, the Reflection API also helps us to enforce that policy.

So, in summary: method_exists() is too simplistic for the needs of the service we were developing. The Reflection API gave us the rich feature set we needed.
#1.1 Matthew Weier O'Phinney (Link) on 2007-12-10 08:57 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back July '08
Mon Tue Wed Thu Fri Sat Sun
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Paul M. Jones
  • Mike Naberezny
  • Shahar Evron
  • Planet PHP
  • Zend Where I now work
  • Garden.org Where I once worked

Archives

July 2008
June 2008
May 2008
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml best practices
xml books
xml conferences
xml dojo
xml dpc08
xml file_fortune
xml linux
xml mvc
xml pear
xml personal
xml php
xml programming
xml ubuntu
xml webinar
xml zendcon
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net