Archive for August, 2009

Today I needed to generate source code documentation for a webservice APIs we are in coding. One of the guys in the office suggested I take a look at Zend_Reflection. It turns out its quite simple to write PHP code to parse source files and extract the documentation from them. I took a little time to write this sample program.

This program is quite simple: it has a single class called TestClass with one method: addIntegers(). Below this class I wrote a little code which parses the file and then outputs the information. You can use this and a templating system to generate doc. I used it to write the documentation so that we can easily add it to our Wiki.

The script I wrote parses the documentation style used in Zend Framework. Here’s a sample class:

<?php

/**
 * This is a sample test class.
 *
 * This class has only one method: addIntegers() which adds
 * two integers. Its a sample class for our test.
 *
 */

class TestClass
{
    /**
     * This method adds two integers.
     *
     * This method adds the two given integers and returns
     * its sum. If only one integer is given it sums zero.
     *
     * @param int $number1  The first integer
     * @param int $number2  The second integer
     * @return int The sum of $number1 and $number2.
     *
     */

    public function addIntegers( $number1, $number2 = 0 ) {
         return $number1 + $number2;
    }
}

 

Read the rest of the post to see how to easily parse this code…
(more…)

Zend Framework recently introduced the concept of modules so that your applications become more manegable. The application on startup will load the resources defined in the main application.ini file. To configure each module there is a resource called modules that calls the bootstraping code for each module on every request. If you want to load resources on a per module basis you need to write a bootstrap for each module. This can also generate clashes if two modules try to load the same resource. Additionally this process can have a negative impact on your applications performance.

In this blog I propose a different strategy: I created a controller action plugin that will load resources dynamically using the main bootstraping code. The idea is that after the routes are parsed and before the action is executed this code will load the module’s config file and call the bootstrap again so that it will load the resources defined in this config.

This method has several advantages: only the resources for the module that is being called are loaded. Becuase only one config file is read there is no possiblity of clashes.

Here’s the code:

<?php

/**
 * Bootstraps the module used in the current request
 * It merges the options from main application.ini with the options from a
 * module specific application.ini.
 * I’m using the fact that a resources are loaded only once, so a resource
 * declared in main application.ini is initialized there. If you wish to use some resource
 * in every module use the key default (see bellow).
 *
 * The keys default and disable in the main application.ini have a special meaning.
 * default: It is merged initally so a key default.resources.xxx is added as resource.xxx
 *          so it load the resource xxx as a default for all modules
 * disable: It is used to disable keys after merging so a key disable.resources.xxx disables
 *          this resource in all modules.
 */

class Easytech_Controller_Plugin_ModuleBootstrap extends Zend_Controller_Plugin_Abstract
{
    const MODULE_APPLICATION_INI = "configs/application.ini";

    protected $_bootstrap;

    protected $_autoloader;

    public function dispatchLoopStartup( Zend_Controller_Request_Abstract $request )
    {
        $moduleName = $request->getModuleName();
        if ( empty( $moduleName ) ) {
            $moduleName = ‘default’;
        }

        $front = Zend_Controller_Front::getInstance();
        $moduleDir = $front->getModuleDirectory( $moduleName );
        if ( empty( $moduleDir ) ) {
            $moduleDir = APPLICATION_PATH;
        }
       
        // Para ser usado en los ini.
        define(‘MODULE_PATH’, $moduleDir );
        define(‘MODULE_NAME’, $moduleName );

        // Create a autoloader for this module
        $autoloader = new Zend_Application_Module_Autoloader(array(
            ‘namespace’ => $moduleName,
            ‘basePath’  => $moduleDir,
            ));

        $this->_bootstrap = $front->getParam(‘bootstrap’);
        $globalOptions = $this->_bootstrap->getOptions();

        // Default options from main application.ini
        $options = array();
        if ( isset( $globalOptions[‘default’] ) ) {
            $options = $globalOptions[‘default’];
        }
        if ( isset( $globalOptions[ $moduleName ] ) ) {
            $options = $this->_bootstrap->mergeOptions($options, $globalOptions[ $moduleName ]);
        }

        $applicationFile = $moduleDir . DIRECTORY_SEPARATOR . self::MODULE_APPLICATION_INI;
        if ( isset( $globalOptions[ ‘application_file’ ] ) ) {
            $applicationFile  = $moduleDir . DIRECTORY_SEPARATOR . $globalOptions[ ‘application_file’ ];
        }
        $options = $this->_bootstrap->mergeOptions( $options, $this->_loadConfig( $applicationFile ) );
        if ( isset( $globalOptions[‘disable’] ) ) {
            $options = $this->unsetOptions($options, $globalOptions[‘disable’] );
        }
        $this->_bootstrap->setOptions( $options );
        $this->_bootstrap->bootstrap();
    }

    /**
     * Unset options recursively.
     *
     * @param  array $array1
     * @param  mixed $array2
     * @return array
     */

    public function unsetOptions(array $array1, $array2 = null)
    {
        if (is_array($array2)) {
            foreach ($array2 as $key => $val) {
                if ( ! isset( $array1[ $key ] ) ) {
                    continue;
                }
                if ( is_array( $array2[ $key ] ) ) {
                    $array1[ $key ] = $this->unsetOptions( $array1[ $key ], $array2[ $key ]);
                } else {
                    unset( $array1[ $key ] );
                }
            }
        }
        return $array1;
    }

   /**
     * Based heavily on Zend_Application->_loadConfig
     * Load configuration file of options
     *
     * @param  string $file
     * @throws Zend_Application_Exception When invalid configuration file is provided
     * @return array
     */

    protected function _loadConfig($file)
    {
        if (! Zend_Loader::isReadable($file) ) {
            return;
        }
        $environment = $this->_bootstrap->getApplication()->getEnvironment();
        $suffix      = strtolower(pathinfo($file, PATHINFO_EXTENSION));

        switch ($suffix) {
            case ‘ini’:
                $config = new Zend_Config_Ini($file, $environment);
                break;

            case ‘xml’:
                $config = new Zend_Config_Xml($file, $environment);
                break;

            case ‘php’:
            case ‘inc’:
                $config = include $file;
                if (!is_array($config)) {
                    throw new Zend_Application_Exception(‘Invalid configuration file provided; PHP file does not return array value’);
                }
                return $config;
                break;

            default:
                throw new Zend_Application_Exception(‘Invalid configuration file provided; unknown config type’);
                break;
            }
            return $config->toArray();
        }

    }