Refactoring to Zend Framework

Over the course of several weeks, I recently took an existing web app I had started from scratch 5 years ago and refactored it into a full stack Zend Framework application.  This involved several steps essentially to organize the code in a similar fashion to ZF’s project layout, then incorporating certain components, and then finally finishing it off.

The application was simply too complicated to convert in one fell swoop or to start a new ZF project and then go feature-by-feature rebuilding.  I was already using components from the ZF library for a lot of things (DB, cache, registry, etc.) so the refactor really consisted of converting from my series of root level page-controllers to ZF’s front-controller pattern.

I’ll share the code below for the most useful step in the conversion:   Using PEAR class naming convention, I was able to create controllers using the Application_Controllers_Name naming convention.  These controllers then extended Zend_Controller_Abstract (actually I created a custom abstract controller of my own that extended ZF’s).  This gets you most of the functionality of the framework while not having to convert all the way over to the full ZF stack.

To finish this intermediate step off, you need a way to route requests to the proper controller.  I wasn’t ready to implement the ZF front controller pattern, which would have required a bootstrap file that extends Zend_Application_Bootstrap and ends up calling the dispatcher and router…. too complicated for my code at this intermediate stage.

So I wrote the following index.php file that handles the routing itself.  You’ll need your own custom bootstrap file.

/**
 * Self-Routing Front Controller for partial ZF implementation
 *
 * @author      Chris Renner
 * @link        http://www.chrisrenner.com
 */
 
define('APPLICATION_ENV', 'production');
 
/**
 * Define path to root dir (i.e. location of index.php)
 */
define("ROOT_PATH", realpath(dirname(__FILE__)));
 
/*
 * Define path to application directory
 */
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', ROOT_PATH . '/Application');
 
/**
 * Define root directory and application directory
 */
define("DOCUMENT_ROOT", $_SERVER['DOCUMENT_ROOT']);
define("RAW_PATH", str_replace(realpath($_SERVER['DOCUMENT_ROOT']), '', ROOT_PATH));
define("APP_PATH", str_replace('\\', '/', RAW_PATH)); // have to switch the backslashes to forward slashes to not break on windows
 
/**
 * Set include paths for classes, etc.
 * Entries represent all paths that application can look for files such as classes, template files, includes files
 */
set_include_path(get_include_path . PATH_SEPARATOR . realpath(ROOT_PATH . '/library/') . PATH_SEPARATOR . realpath(ROOT_PATH));
 
/**
 * Get bootstrap instance and execute
 * sets up config, app settings, db connections
 */
require_once 'Application/Bootstrap.php';
$bootstrap = Bootstrap::getInstance(APPLICATION_ENV);
$bootstrap->run();
 
/**
 * set defaults
 */
$controllerName = 'Index';
$actionName     = 'index';  // if no action supplied, will default to indexAction
$moduleName     = null;
$moduleStr      = null;
 
if(!empty($_REQUEST['controller'])) {
    $controllerName = $_REQUEST['controller'];
}
 
if(!empty($_REQUEST['action'])) {
    $actionName = $_REQUEST['action'];
}
 
if(!empty($_REQUEST['module'])) {
    $moduleName = $_REQUEST['module'];
    $moduleStr = 'Modules_' . $moduleName . '_';
}
 
$controllerClass = 'Application_' . $moduleStr . 'Controller_' . $controllerName;
$actionMethod = $actionName . 'Action';
 
/**
 * Instantiate default request object if none provided
 */
$request = new Zend_Controller_Request_Http();
$request->setActionName($actionName);
$request->setControllerName($controllerName);
 
if($moduleName) {
    $request->setModuleName($moduleName);
}
 
/**
 * Instantiate default response object if none provided
 */
$response = new Zend_Controller_Response_Http();
 
/**
 * Instantiate new instance of the controller
 * which extends Zend_Controller_Action
 */
if(!class_exists($controllerClass)) {
    throw new Exception("Controller $controllerClass does not exist");
}
 
$obj = new $controllerClass($request, $response, $_REQUEST);
 
/**
 * Execute the requested action, pass in the $_REQUEST
 */
try {
    $obj->$actionMethod();
} catch(Exception $e) {
    $bootstrap->handleException($e, $request, $response, $_REQUEST);
}
 
/**
 * Print response if not null
 */
if($response) {
    echo $response;
}

This is not drop-in-ready code, it is shared to give you an idea of how to route your controllers outside of the full ZF stack. This code creates the request and response objects and passes them into the applicable controller Index controller is called if no controller is specified.

Be sure to add “Application_” to your autoloader scheme so that when the code constructs the class name, it can find it in your include path.

Also I need to note, this code doesn’t take into account pretty URLs, so you’ll have to re-jigger this to your own needs if you’re using .htaccess to re-route requests. The above code works with index.php?controller=Foo&action=Bar urls. In fact, my application, being an enterprise web app for one, and also being on a server that doesn’t permit URL rewriting, retained the ugly url syntax, and fortunately for me I found a ZF router for just such scenarios on Rob Allen’s blog from a long time ago. You DON’T need Rob’s router for my intermediate code, but if you’re not going to convert to pretty URLs then you will.

This code helped me get my application to a point where I could do the final conversion to ZF relatively painlessly. Good luck.