PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store

Advanced Documentation With Reflection In PHP 5

Using The Extracted Document Comments

Now you should have been able to parse out the data from the comments and store it in an object. You can then do pretty much anything you want with that data. I personally like to output this data to an XML file and use an XSL stylesheet to interpret the data.

The next hurdle you will need to cover is the ability to load all the files for a site into memory, but without executing any of their code.

Fortunately there are several tools that allow you to do just this.

There are 3 main components to this process. These are:

  1. Finding all the files in the site using SPL’s recursiveDirectoryIterator.
  2. Loading those files without running their contents and without causing scope resolution issues.
  3. Pragmatically determining all the classes, interfaces and functions that have been defined.

First, the Standard PHP Library (SPL) provides a useful class for traversing directories and files pragmatically, recursiveDirectoryIterator. This class is actually far easier to use than the old directory functions.

Second, to load the files located by the recursiveDirectoryIterator, we use a side effect of the function php_check_syntax(). That is, once it is run, the functions, classes and interfaces defined in those files remain in memory and can be reflected. This is, however, much different than include, because no code will be executed.

Listing 11 listing-11.php
<?php
    /**
     * includeTree
     *
     * Invocation method for recursive directory load.
     *
     * @param   string      $sDirectory     A path to include, all subdirs will be scanned.
     * @access  private
     */
    private function includeTree($sDirectory)
    {
        $this->recurseTree(new recursiveDirectoryIterator($sDirectory));
    }
 
    /**
     * recurseTree
     *
     * Recursive function that uses a spl recursiveDirectoryIterator
     * to scan directories for files to include.
     *
     * @param   iterator    $oIterator  A path to include, all subdirs will be scanned.
     * @access  private
     */
    private function recurseTree($oIterator)
    {
        $allowedTypes = array('php', 'inc');
        while ($oIterator->valid()) {
            if ($oIterator->isDir() && !$oIterator->isDot()) {
                if ($oIterator->hasChildren()) {
                    $this->recurseTree($oIterator->getChildren());
                }
            }
            else if ($oIterator->isFile()) {
                $path = $oIterator->getPath() . '/' . $oIterator->getFilename()
                $pathinfo = pathinfo($path);
                if (in_array(strtolower($pathinfo['extension']), $allowedTypes)) {
                    php_check_syntax_external($path);
                }
            }
            $oIterator->next();
        }
    }
?>

The above two methods are designed to exist within a class, however, php_check_syntax() needs to be called from outside class scope to function as expected. So we need to create a function in the global scope called php_check_syntax_external() so that class scope is not inherited.

Listing 12 listing-12.php
<?php
    /**
     * PHP Check Syntax External
     *
     * Marshalls php_check_syntax() for the global scope and discards the result
     *
     * @param   string  $sFile  The filename to pass along.
     */
    function php_check_syntax_external($sFile)
    {
        php_check_syntax($sFile);
    }
?>

Next, we need to determine what has been declared. There are three distinct functions for this job:

  1. get_defined_functions()
  2. get_declared_classes()
  3. get_declared_interfaces()

get_defined_functions() works differently than the other two in that it returns a multidimensional array. You can see the docs for the full details but for our purposes we will just simply specify the first degree key user.

Listing 13 listing-13.php
<?php
    $aFuncs = get_defined_functions();
    $aFuncs = $aFuncs['user'];
?>

$aFuncs now contains an array of all user defined function names, to which you can loop and pass into ReflectionFunction($functionName).

To get the classes and interfaces, we must use a combination of their list functions as well as reflection to determine if they are built-in or if they were created by the user.

Listing 14 listing-14.php
<?php
    foreach(get_declared_classes() as $sClassName) {
        $oClassReflect = new ReflectionClass($sClassName);
        if($oClassReflect->isUserDefined()) {
            // Do something
        }
    }
?>

In This Article