Spiga

Optimizing Zend Routing

by Gabi Solomon

After playing with zend framework for a while, and reading left and right, i came to the conclusion that i need to find a way to optimize the Routing in my application.
If you havent notice by now, the routing in Zend takes quite a while, and it increases with the number of routes you have, and with the number of routes that use Regular Expressions.

The easiest way to optimise it would be to reduce the number of rules that use regular expressions and the number of rules all together. But for SEO reasons that is not always possible.

Current Situation

Info: I have my routes wrote in INI files.

In my application i have started to have quite a few rules, so i have thought i would break them into separate files and include only the rules that i need for the section i am in. While that sounds like a plan, its not that simple … i still need to have all the routes on page generation, because i have links point to a different section, so i need all the routes.

Solution

The solution that i came with is to write a plugin that will include some routes routeStartup and the rest on routeShutdown.

Step 1 – Routes files

To make this work, you need to have a directory where to store the routes ini files.
I have placed it in my application config dir. I recommend having a default one that is always included. Here is an example of a routing file:
[php]
lang_default.type = “GSD_Controller_Router_Route_Language”
lang_default.route = “:language/:controller/:action/*”
lang_default.defaults.module = default
lang_default.defaults.controller = index
lang_default.defaults.action = index
lang_default.defaults.language = ro
lang_default.reqs.language = “(ro|en)”
[/php]

Step 2 – Bootstraping the plugin

I have a simple bootstrap class that i wrote about, and i overwrote the setupRoutes method, but you can port this to your own setup with ease if you have used plugins before.
[php]
public static function setupRoutes() {
$plugin = new GSD_Controller_Plugin_Router();
$plugin->setDir(self::$root . ‘/application/config/routes/’);
$plugin->setDefault(‘default.ini’);
self::$frontController->registerPlugin($plugin);
}
[/php]

In the code you see that i add a default file that is included everytime. just because some routes are 90% used.

Step 3 – The actual plugin

Here it is, full and uncensored :
[php]

/**
* Front Controller Plugin
*
* @uses Zend_Controller_Plugin_Abstract
* @category GSD
* @package GSD_Controller
* @subpackage Plugins
*/
class GSD_Controller_Plugin_Router extends Zend_Controller_Plugin_Abstract
{
protected $_dir;
protected $_default = array();
protected $_request;

protected $_initialConfig;
protected $_remainingConfig;

public function routeStartup(Zend_Controller_Request_Abstract $request)
{
// define some routes (URLs)
$router = Zend_Controller_Front::getInstance()->getRouter();

$this->setRequest($request);

$config = $this->getInitial();
$router->addConfig($config);
}

public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$router = Zend_Controller_Front::getInstance()->getRouter();

$config = $this->getRemaining();

$router->addConfig($config);
}

public function setDir($dir) {
$this->_dir = $dir;
}

public function setDefault($default) {
if (is_array($default)) {
$this->_default = array_merge($this->_default, $default);
} else {
$this->_default[] = $default;
}
}

public function setRequest($request) {
$this->_request = $request;
}

public function getInitial() {
if ($this->_initialConfig == null) {
$this->parseIniDir();
}

return $this->_initialConfig;
}

public function getRemaining() {
if ($this->_remainingConfig == null) {
$this->parseIniDir();
}

return $this->_remainingConfig;
}

protected function parseIniDir() {
$files = $this->getFiles();
$this->_default;

$this->_default[] = $this->determineInitial();

$this->_initialConfig = new Zend_Config(array(), true);
$this->_remainingConfig = new Zend_Config(array(), true);

if (is_array($files)) {
foreach ($files as $file) {
$routerFile = $this->compilePath($file);

if (in_array($file, $this->_default)) {
$this->_initialConfig->merge(new Zend_Config_Ini($routerFile));
} else {
$this->_remainingConfig->merge(new Zend_Config_Ini($routerFile));
}
}
}
}

protected function getFiles() {
if (is_dir($this->_dir)) {
$dir = new DirectoryIterator($this->_dir);
$files = array();
foreach($dir as $fileInfo) {
if(!$fileInfo->isDot() && $fileInfo->isFile()) {
$files[] = $fileInfo->__toString();
}
}

return $files;
}

return false;
}

protected function getOtherRoutes() {
$routes->merge(new Zend_Config_Ini($routerFile));
}

protected function determineInitial() {
if ($this->_request) {
$uri = $this->_request->getRequestUri();
$base = $this->_request->getBasePath() . ‘/’;

$request = str_replace($base, ”, $uri);
$requestParts = explode(‘/’, $request);

$lang = $requestParts[0];
$section = $requestParts[1];

if (!empty($section) && $section == ‘user’) {
return ‘user.ini’;
}

}
return false;
}

protected function compilePath($file) {
return $this->_dir . ‘/’ . $file;
}
}
[/php]

In there you can see that the plugin reads all the ini files and at first only includes the ones from the default route you specify in bootstrap, and the one determined in the method determineInitial. I left there a small part of my method to give you an example of how i detect wich file to include based on the URL.

After routing, the routeShutdown is fired and all the rest of the routes are added into the router.

Final words

I think that cutting down on the number of routes at routing really speeds up your application, especially if its one with high trafic. All those regular expressions really make a mark in your server load.
PS: Load test your application, i had a wake up call about this a few weeks ago.

Unfortunately i am really short on time so i didn’t do some comparations to see what improvements you get. If you have the time and do it out of curiosity i would appreciate if you would post a comment with your results ( i take critics well … at least i like to think so :-P )

Awaiting curiously your comments.
Cheers

  • http://www.air-jordan-15.com air jordan 15

    Mark S. is definitely on the right track. If you want to get a professional looking email address, Id recommend buying your name domain name, like or
    gucci store
    If its common it might be difficult to get, however, be creative and you can usually find something.

  • ww

    cao ni ma