Route determination starts with the search for the current route compliance with the routes, determined in the system. The process begins here:
app/code/core/Mage/Core/Controller/Varien/Front.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Varien_Profiler::start('mage::dispatch::routers_match'); $i = 0; while (!$request->isDispatched() && $i++<100) { foreach ($this->_routers as $router) { if ($router->match($this->getRequest())) { break; } } } Varien_Profiler::stop('mage::dispatch::routers_match'); if ($i>100) { Mage::throwException('Front controller reached 100 router match iterations'); } // This event gives possibility to launch something before sending output (allow cookie setting) Mage::dispatchEvent('controller_front_send_response_before', array('front'=>$this)); Varien_Profiler::start('mage::app::dispatch::send_response'); $this->getResponse()->sendResponse(); Varien_Profiler::stop('mage::app::dispatch::send_response'); Mage::dispatchEvent('controller_front_send_response_after', array('front'=>$this)); return $this; |
There are 4 routes defined in Magento by default:
Mage_Core_Controller_Varien_Router_Admin
Mage_Core_Controller_Varien_Router_Standard
Mage_Cms_Controller_Router
Mage_Core_Controller_Varien_Router_Default
As we know from Magento Front Controller Pattern article, $this->_routers variable contains all the defined routes. The searching will be thereby performed in this array. It will be carried out among all the routes and will stop at the first match found. If no matches are found, the route defined for 404 will be processed or the ‘Front controller reached 100 router match iterations’ message will be displayed:
1 2 3 |
if ($i>100) { Mage::throwException('Front controller reached 100 router match iterations'); } |
Main steps of the routing process:
- module determination
- controller determination
- action to perform detection
- data for the current request detection
Let’s take a look at the routes initialization:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Varien_Profiler::start('mage::app::init_front_controller::collect_routers'); foreach ($routersInfo as $routerCode => $routerInfo) { if (isset($routerInfo['disabled']) && $routerInfo['disabled']) { continue; } if (isset($routerInfo['class'])) { $router = new $routerInfo['class']; if (isset($routerInfo['area'])) { $router->collectRoutes($routerInfo['area'], $routerCode); } $this->addRouter($routerCode, $router); } } Varien_Profiler::stop('mage::app::init_front_controller::collect_routers'); Mage::dispatchEvent('controller_front_init_routers', array('front'=>$this)); // Add default router at the last $default = new Mage_Core_Controller_Varien_Router_Default(); $this->addRouter('default', $default); |
Administrative routes are being initialized first:
app/code/core/Mage/Core/Controller/Varien/Router/Admin.php
1 2 3 4 5 6 7 8 9 10 11 |
public function collectRoutes($configArea, $useRouterName) { if ((string)Mage::getConfig()->getNode(Mage_Adminhtml_Helper_Data::XML_PATH_USE_CUSTOM_ADMIN_PATH)) { $customUrl = (string)Mage::getConfig()->getNode(Mage_Adminhtml_Helper_Data::XML_PATH_CUSTOM_ADMIN_PATH); $xmlPath = Mage_Adminhtml_Helper_Data::XML_PATH_ADMINHTML_ROUTER_FRONTNAME; if ((string)Mage::getConfig()->getNode($xmlPath) != $customUrl) { Mage::getConfig()->setNode($xmlPath, $customUrl, true); } } parent::collectRoutes($configArea, $useRouterName); } |
The route, administrative part is accessible at, is verified here. It is also checked if it is redefined or not. Considering Admin is the extension of Standard, the following is next executed:
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 32 33 34 35 36 37 38 39 40 41 42 |
parent::collectRoutes($configArea, $useRouterName); app/code/core/Mage/Core/Controller/Varien/Router/Standard.php public function collectRoutes($configArea, $useRouterName) { $routers = array(); $routersConfigNode = Mage::getConfig()->getNode($configArea.'/routers'); if($routersConfigNode) { $routers = $routersConfigNode->children(); } foreach ($routers as $routerName=>$routerConfig) { $use = (string)$routerConfig->use; if ($use == $useRouterName) { $modules = array((string)$routerConfig->args->module); if ($routerConfig->args->modules) { foreach ($routerConfig->args->modules->children() as $customModule) { if ($customModule) { if ($before = $customModule->getAttribute('before')) { $position = array_search($before, $modules); if ($position === false) { $position = 0; } array_splice($modules, $position, 0, (string)$customModule); } elseif ($after = $customModule->getAttribute('after')) { $position = array_search($after, $modules); if ($position === false) { $position = count($modules); } array_splice($modules, $position+1, 0, (string)$customModule); } else { $modules[] = (string)$customModule; } } } } $frontName = (string)$routerConfig->args->frontName; $this->addModule($frontName, $modules, $routerName); } } } |
All the routes defined in the configuration are picked and processed here, considering before and after events. _modules and _routes arrays of modules and routes correspondingly for the current route type are also filled:
app/code/core/Mage/Core/Controller/Varien/Router/Standard.php
1 2 3 4 5 6 |
public function addModule($frontName, $moduleName, $routeName) { $this->_modules[$frontName] = $moduleName; $this->_routes[$routeName] = $frontName; return $this; } |
Next, this route object is added to the general routes list:
app/code/core/Mage/Core/Controller/Varien/Front.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$router = new $routerInfo['class']; if (isset($routerInfo['area'])) { $router->collectRoutes($routerInfo['area'], $routerCode); } $this->addRouter($routerCode, $router); /** * Adding new router * * @param string $name * @param Mage_Core_Controller_Varien_Router_Abstract $router * @return Mage_Core_Controller_Varien_Front */ public function addRouter($name, Mage_Core_Controller_Varien_Router_Abstract $router) { $router->setFront($this); $this->_routers[$name] = $router; return $this; } |
Standard routes are initialized the same way.
It can be demonstrated easier as follows:
1 2 3 4 5 6 7 |
$router = new Mage_Core_Controller_Varien_Router_Admin(); $router->collectRoutes('admin','admin'); $this->addRouter('admin',$router); $router = new Mage_Core_Controller_Varien_Router_Standard(); $router->collectRoutes('frontend','standard'); $this->addRouter('standard',$router); |
CMS (“static”) routes are added via controller_front_init_routers event next:
app/code/core/Mage/Cms/etc/config.xml
1 2 3 4 5 6 7 8 9 10 |
<events> <controller_front_init_routers> <observers> <cms> <class>Mage_Cms_Controller_Router</class> <method>initControllerRouters</method> </cms> </observers> </controller_front_init_routers> </events> |
app/code/core/Mage/Cms/Controller/Router.php
1 2 3 4 5 6 7 |
public function initControllerRouters($observer) { /* @var $front Mage_Core_Controller_Varien_Front */ $front = $observer->getEvent()->getFront(); $front->addRouter('cms', $this); } |
After all, Default 404/no-route routes:
1 2 3 |
// Add default router at the last $default = new Mage_Core_Controller_Varien_Router_Default(); $this->addRouter('default', $default); |
After routes initialization, searching for appropriate things is performed:
1 2 3 4 5 6 7 |
while (!$request->isDispatched() && $i++<100) { foreach ($this->_routers as $router) { if ($router->match($this->getRequest())) { break; } } } |
As Admin routes are an extension to Standard, we first look more closely at Standard. It’s primarily checked if the administrative interface is active:
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * checking if this admin if yes then we don't use this router * * @return bool */ protected function _beforeModuleMatch() { if (Mage::app()->getStore()->isAdmin()) { return false; } return true; } |
Default route is defined next:
1 2 3 4 5 6 7 8 |
public function fetchDefault() { $this->getFront()->setDefault(array( 'module' => 'core', 'controller' => 'index', 'action' => 'index' )); } |
The current request goes next; it is taken to pieces:
1 2 3 4 5 6 7 8 |
$front = $this->getFront(); $path = trim($request->getPathInfo(), '/'); if ($path) { $p = explode('/', $path); } else { $p = explode('/', $this->_getDefaultPath()); } |
For example, for the following path: catalog/category/view/id/10 we get:
1 2 3 4 5 6 7 |
$p = array( 0 => catalog, 1 => category, 2 => view, 3 => id, 4 => 10 ); |
First 3 elements are principal here; they indicate module, controller and action for the current request.
Next, the module is determined from its frontName:
1 2 3 4 |
/** * Searching router args by module name from route using it as key */ $modules = $this->getModuleByFrontName($module); |
The controller and the action for the current request are thereafter defined:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// get controller name if ($request->getControllerName()) { $controller = $request->getControllerName(); } else { if (!empty($p[1])) { $controller = $p[1]; } else { $controller = $front->getDefault('controller'); $request->setAlias( Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS, ltrim($request->getOriginalPathInfo(), '/') ); } } // get action name if (empty($action)) { if ($request->getActionName()) { $action = $request->getActionName(); } else { $action = !empty($p[2]) ? $p[2] : $front->getDefault('action'); } } |
It’s also checked if the connection is safe and if the controller file exists:
1 2 3 4 5 6 7 |
//checking if this place should be secure $this->_checkShouldBeSecure($request, '/'.$module.'/'.$controller.'/'.$action); $controllerClassName = $this->_validateControllerClassName($realModule, $controller); if (!$controllerClassName) { continue; } |
Controller object is created next. It’s checked if the specified action exists in the controller:
1 2 3 4 |
// instantiate controller class $controllerInstance = Mage::getControllerInstance($controllerClassName, $request, $front->getResponse()); if (!$controllerInstance->hasAction($action)) { |
1 2 |
continue; } |
It’s after all flagged that the route is processed. Module, controller and action are defined and action is performed for the current request.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$request->setModuleName($module); $request->setControllerName($controller); $request->setActionName($action); $request->setControllerModule($realModule); // set parameters from pathinfo for ($i = 3, $l = sizeof($p); $i < $l; $i += 2) { $request->setParam($p[$i], isset($p[$i+1]) ? urldecode($p[$i+1]) : ''); } // dispatch action $request->setDispatched(true); $controllerInstance->dispatch($action); return true; |
Control returns to dispatch FrontController method, where “output” is defined for the current request.