/classes/Dispatcher.php
PHP | 877 lines | 612 code | 92 blank | 173 comment | 148 complexity | 4090d644ce5ea1d5c1cd84f567b9e680 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0
- <?php
- /*
- * 2007-2014 PrestaShop
- *
- * NOTICE OF LICENSE
- *
- * This source file is subject to the Open Software License (OSL 3.0)
- * that is bundled with this package in the file LICENSE.txt.
- * It is also available through the world-wide-web at this URL:
- * http://opensource.org/licenses/osl-3.0.php
- * If you did not receive a copy of the license and are unable to
- * obtain it through the world-wide-web, please send an email
- * to license@prestashop.com so we can send you a copy immediately.
- *
- * DISCLAIMER
- *
- * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
- * versions in the future. If you wish to customize PrestaShop for your
- * needs please refer to http://www.prestashop.com for more information.
- *
- * @author PrestaShop SA <contact@prestashop.com>
- * @copyright 2007-2014 PrestaShop SA
- * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
- * International Registered Trademark & Property of PrestaShop SA
- */
- /**
- * @since 1.5.0
- */
- class DispatcherCore
- {
- /**
- * List of available front controllers types
- */
- const FC_FRONT = 1;
- const FC_ADMIN = 2;
- const FC_MODULE = 3;
- /**
- * @var Dispatcher
- */
- public static $instance = null;
- /**
- * @var array List of default routes
- */
- public $default_routes = array(
- 'category_rule' => array(
- 'controller' => 'category',
- 'rule' => '{id}-{rewrite}',
- 'keywords' => array(
- 'id' => array('regexp' => '[0-9]+', 'param' => 'id_category'),
- 'rewrite' => array('regexp' => '[_a-zA-Z0-9\pL\pS-]*'),
- 'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- ),
- ),
- 'supplier_rule' => array(
- 'controller' => 'supplier',
- 'rule' => '{id}__{rewrite}',
- 'keywords' => array(
- 'id' => array('regexp' => '[0-9]+', 'param' => 'id_supplier'),
- 'rewrite' => array('regexp' => '[_a-zA-Z0-9\pL\pS-]*'),
- 'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- ),
- ),
- 'manufacturer_rule' => array(
- 'controller' => 'manufacturer',
- 'rule' => '{id}_{rewrite}',
- 'keywords' => array(
- 'id' => array('regexp' => '[0-9]+', 'param' => 'id_manufacturer'),
- 'rewrite' => array('regexp' => '[_a-zA-Z0-9\pL\pS-]*'),
- 'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- ),
- ),
- 'cms_rule' => array(
- 'controller' => 'cms',
- 'rule' => 'content/{id}-{rewrite}',
- 'keywords' => array(
- 'id' => array('regexp' => '[0-9]+', 'param' => 'id_cms'),
- 'rewrite' => array('regexp' => '[_a-zA-Z0-9\pL\pS-]*'),
- 'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- ),
- ),
- 'cms_category_rule' => array(
- 'controller' => 'cms',
- 'rule' => 'content/category/{id}-{rewrite}',
- 'keywords' => array(
- 'id' => array('regexp' => '[0-9]+', 'param' => 'id_cms_category'),
- 'rewrite' => array('regexp' => '[_a-zA-Z0-9\pL\pS-]*'),
- 'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- ),
- ),
- 'module' => array(
- 'controller' => null,
- 'rule' => 'module/{module}{/:controller}',
- 'keywords' => array(
- 'module' => array('regexp' => '[_a-zA-Z0-9_-]+', 'param' => 'module'),
- 'controller' => array('regexp' => '[_a-zA-Z0-9_-]+', 'param' => 'controller'),
- ),
- 'params' => array(
- 'fc' => 'module',
- ),
- ),
- 'product_rule' => array(
- 'controller' => 'product',
- 'rule' => '{category:/}{id}-{rewrite}{-:ean13}.html',
- 'keywords' => array(
- 'id' => array('regexp' => '[0-9]+', 'param' => 'id_product'),
- 'rewrite' => array('regexp' => '[_a-zA-Z0-9\pL\pS-]*'),
- 'ean13' => array('regexp' => '[0-9\pL]*'),
- 'category' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'categories' => array('regexp' => '[/_a-zA-Z0-9-\pL]*'),
- 'reference' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'manufacturer' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'supplier' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'price' => array('regexp' => '[0-9\.,]*'),
- 'tags' => array('regexp' => '[a-zA-Z0-9-\pL]*'),
- ),
- ),
- // Must be after the product and category rules in order to avoid conflict
- 'layered_rule' => array(
- 'controller' => 'category',
- 'rule' => '{id}-{rewrite}{/:selected_filters}',
- 'keywords' => array(
- 'id' => array('regexp' => '[0-9]+', 'param' => 'id_category'),
- /* Selected filters is used by the module blocklayered */
- 'selected_filters' => array('regexp' => '.*', 'param' => 'selected_filters'),
- 'rewrite' => array('regexp' => '[_a-zA-Z0-9\pL\pS-]*'),
- 'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- 'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
- ),
- ),
- );
- /**
- * @var bool If true, use routes to build URL (mod rewrite must be activated)
- */
- protected $use_routes = false;
- protected $multilang_activated = false;
- /**
- * @var array List of loaded routes
- */
- protected $routes = array();
- /**
- * @var string Current controller name
- */
- protected $controller;
- /**
- * @var string Current request uri
- */
- protected $request_uri;
- /**
- * @var array Store empty route (a route with an empty rule)
- */
- protected $empty_route;
- /**
- * @var string Set default controller, which will be used if http parameter 'controller' is empty
- */
- protected $default_controller;
- protected $use_default_controller = false;
- /**
- * @var string Controller to use if found controller doesn't exist
- */
- protected $controller_not_found = 'pagenotfound';
- /**
- * @var string Front controller to use
- */
- protected $front_controller = self::FC_FRONT;
- /**
- * Get current instance of dispatcher (singleton)
- *
- * @return Dispatcher
- */
- public static function getInstance()
- {
- if (!self::$instance)
- self::$instance = new Dispatcher();
- return self::$instance;
- }
- /**
- * Need to be instancied from getInstance() method
- */
- protected function __construct()
- {
- $this->use_routes = (bool)Configuration::get('PS_REWRITING_SETTINGS');
- // Select right front controller
- if (defined('_PS_ADMIN_DIR_'))
- {
- $this->front_controller = self::FC_ADMIN;
- $this->controller_not_found = 'adminnotfound';
- }
- elseif (Tools::getValue('fc') == 'module')
- {
- $this->front_controller = self::FC_MODULE;
- $this->controller_not_found = 'pagenotfound';
- }
- else
- {
- $this->front_controller = self::FC_FRONT;
- $this->controller_not_found = 'pagenotfound';
- }
- $this->setRequestUri();
- // Switch language if needed (only on front)
- if (in_array($this->front_controller, array(self::FC_FRONT, self::FC_MODULE)))
- Tools::switchLanguage();
- if (Language::isMultiLanguageActivated())
- $this->multilang_activated = true;
- $this->loadRoutes();
- }
- public function useDefaultController()
- {
- $this->use_default_controller = true;
- if ($this->default_controller === null)
- {
- if (defined('_PS_ADMIN_DIR_'))
- {
- if (isset(Context::getContext()->employee) && Validate::isLoadedObject(Context::getContext()->employee) && isset(Context::getContext()->employee->default_tab))
- $this->default_controller = Tab::getClassNameById((int)Context::getContext()->employee->default_tab);
- if (empty($this->default_controller))
- $this->default_controller = 'AdminDashboard';
- }
- elseif (Tools::getValue('fc') == 'module')
- $this->default_controller = 'default';
- else
- $this->default_controller = 'index';
- }
- return $this->default_controller;
- }
- /**
- * Find the controller and instantiate it
- */
- public function dispatch()
- {
- $controller_class = '';
- // Get current controller
- $this->getController();
- if (!$this->controller)
- $this->controller = $this->useDefaultController();
- // Dispatch with right front controller
- switch ($this->front_controller)
- {
- // Dispatch front office controller
- case self::FC_FRONT :
- $controllers = Dispatcher::getControllers(array(_PS_FRONT_CONTROLLER_DIR_, _PS_OVERRIDE_DIR_.'controllers/front/'));
- $controllers['index'] = 'IndexController';
- if (isset($controllers['auth']))
- $controllers['authentication'] = $controllers['auth'];
- if (isset($controllers['compare']))
- $controllers['productscomparison'] = $controllers['compare'];
- if (isset($controllers['contact']))
- $controllers['contactform'] = $controllers['contact'];
- if (!isset($controllers[strtolower($this->controller)]))
- $this->controller = $this->controller_not_found;
- $controller_class = $controllers[strtolower($this->controller)];
- $params_hook_action_dispatcher = array('controller_type' => self::FC_FRONT, 'controller_class' => $controller_class, 'is_module' => 0);
- break;
- // Dispatch module controller for front office
- case self::FC_MODULE :
- $module_name = Validate::isModuleName(Tools::getValue('module')) ? Tools::getValue('module') : '';
- $module = Module::getInstanceByName($module_name);
- $controller_class = 'PageNotFoundController';
- if (Validate::isLoadedObject($module) && $module->active)
- {
- $controllers = Dispatcher::getControllers(_PS_MODULE_DIR_.$module_name.'/controllers/front/');
- if (isset($controllers[strtolower($this->controller)]))
- {
- include_once(_PS_MODULE_DIR_.$module_name.'/controllers/front/'.$this->controller.'.php');
- $controller_class = $module_name.$this->controller.'ModuleFrontController';
- }
- }
- $params_hook_action_dispatcher = array('controller_type' => self::FC_FRONT, 'controller_class' => $controller_class, 'is_module' => 1);
- break;
- // Dispatch back office controller + module back office controller
- case self::FC_ADMIN :
- if ($this->use_default_controller && !Tools::getValue('token') && Validate::isLoadedObject(Context::getContext()->employee) && Context::getContext()->employee->isLoggedBack())
- Tools::redirectAdmin('index.php?controller='.$this->controller.'&token='.Tools::getAdminTokenLite($this->controller));
- $tab = Tab::getInstanceFromClassName($this->controller, Configuration::get('PS_LANG_DEFAULT'));
- $retrocompatibility_admin_tab = null;
- if ($tab->module)
- {
- if (file_exists(_PS_MODULE_DIR_.$tab->module.'/'.$tab->class_name.'.php'))
- $retrocompatibility_admin_tab = _PS_MODULE_DIR_.$tab->module.'/'.$tab->class_name.'.php';
- else
- {
- $controllers = Dispatcher::getControllers(_PS_MODULE_DIR_.$tab->module.'/controllers/admin/');
- if (!isset($controllers[strtolower($this->controller)]))
- {
- $this->controller = $this->controller_not_found;
- $controller_class = 'AdminNotFoundController';
- }
- else
- {
- // Controllers in modules can be named AdminXXX.php or AdminXXXController.php
- include_once(_PS_MODULE_DIR_.$tab->module.'/controllers/admin/'.$controllers[strtolower($this->controller)].'.php');
- $controller_class = $controllers[strtolower($this->controller)].(strpos($controllers[strtolower($this->controller)], 'Controller') ? '' : 'Controller');
- }
- }
- $params_hook_action_dispatcher = array('controller_type' => self::FC_ADMIN, 'controller_class' => $controller_class, 'is_module' => 1);
- }
- else
- {
- $controllers = Dispatcher::getControllers(array(_PS_ADMIN_DIR_.'/tabs/', _PS_ADMIN_CONTROLLER_DIR_, _PS_OVERRIDE_DIR_.'controllers/admin/'));
- if (!isset($controllers[strtolower($this->controller)]))
- {
- // If this is a parent tab, load the first child
- if (Validate::isLoadedObject($tab) && $tab->id_parent == 0 && ($tabs = Tab::getTabs(Context::getContext()->language->id, $tab->id)) && isset($tabs[0]))
- Tools::redirectAdmin(Context::getContext()->link->getAdminLink($tabs[0]['class_name']));
- $this->controller = $this->controller_not_found;
- }
- $controller_class = $controllers[strtolower($this->controller)];
- $params_hook_action_dispatcher = array('controller_type' => self::FC_ADMIN, 'controller_class' => $controller_class, 'is_module' => 0);
- if (file_exists(_PS_ADMIN_DIR_.'/tabs/'.$controller_class.'.php'))
- $retrocompatibility_admin_tab = _PS_ADMIN_DIR_.'/tabs/'.$controller_class.'.php';
- }
- // @retrocompatibility with admin/tabs/ old system
- if ($retrocompatibility_admin_tab)
- {
- include_once($retrocompatibility_admin_tab);
- include_once(_PS_ADMIN_DIR_.'/functions.php');
- runAdminTab($this->controller, !empty($_REQUEST['ajaxMode']));
- return;
- }
- break;
- default :
- throw new PrestaShopException('Bad front controller chosen');
- }
- // Instantiate controller
- try
- {
- // Loading controller
- $controller = Controller::getController($controller_class);
- // Execute hook dispatcher
- if (isset($params_hook_action_dispatcher))
- Hook::exec('actionDispatcher', $params_hook_action_dispatcher);
- // Running controller
- $controller->run();
- }
- catch (PrestaShopException $e)
- {
- $e->displayMessage();
- }
- }
- /**
- * Set request uri and iso lang
- */
- protected function setRequestUri()
- {
- // Get request uri (HTTP_X_REWRITE_URL is used by IIS)
- if (isset($_SERVER['REQUEST_URI']))
- $this->request_uri = $_SERVER['REQUEST_URI'];
- elseif (isset($_SERVER['HTTP_X_REWRITE_URL']))
- $this->request_uri = $_SERVER['HTTP_X_REWRITE_URL'];
- $this->request_uri = rawurldecode($this->request_uri);
- if (isset(Context::getContext()->shop) && is_object(Context::getContext()->shop))
- $this->request_uri = preg_replace('#^'.preg_quote(Context::getContext()->shop->getBaseURI(), '#').'#i', '/', $this->request_uri);
- // If there are several languages, get language from uri
- if ($this->use_routes && Language::isMultiLanguageActivated())
- if (preg_match('#^/([a-z]{2})(?:/.*)?$#', $this->request_uri, $m))
- {
- $_GET['isolang'] = $m[1];
- $this->request_uri = substr($this->request_uri, 3);
- }
- }
- /**
- * Load default routes group by languages
- */
- protected function loadRoutes($id_shop = null)
- {
- $context = Context::getContext();
- // Load custom routes from modules
- $modules_routes = Hook::exec('moduleRoutes', array('id_shop' => $id_shop), null, true, false);
- if (is_array($modules_routes) && count($modules_routes))
- foreach($modules_routes as $module_route)
- {
- if (is_array($module_route) && count($module_route))
- foreach($module_route as $route => $route_details)
- if (array_key_exists('controller', $route_details) && array_key_exists('rule', $route_details)
- && array_key_exists('keywords', $route_details) && array_key_exists('params', $route_details))
- {
- if (!isset($this->default_routes[$route]))
- $this->default_routes[$route] = array();
- $this->default_routes[$route] = array_merge($this->default_routes[$route], $route_details);
- }
- }
- $languages = array();
- if (isset($context->language) && !in_array($context->language->id, $languages = Language::getLanguages()))
- {
- $languages[] = (int)$context->language->id;
- // Set default routes
- foreach ($languages as $lang)
- foreach ($this->default_routes as $id => $route)
- $this->addRoute(
- $id,
- $route['rule'],
- $route['controller'],
- $lang['id_lang'],
- $route['keywords'],
- isset($route['params']) ? $route['params'] : array(),
- $id_shop
- );
- }
- // Load the custom routes prior the defaults to avoid infinite loops
- if ($this->use_routes)
- {
- // Get iso lang
- $iso_lang = Tools::getValue('isolang');
- if (isset($context->language))
- $id_lang = (int)$context->language->id;
- if ((!empty($iso_lang) && Validate::isLanguageIsoCode($iso_lang)) || !isset($id_lang))
- $id_lang = Language::getIdByIso($iso_lang);
- // Load routes from meta table
- $sql = 'SELECT m.page, ml.url_rewrite, ml.id_lang
- FROM `'._DB_PREFIX_.'meta` m
- LEFT JOIN `'._DB_PREFIX_.'meta_lang` ml ON (m.id_meta = ml.id_meta'.Shop::addSqlRestrictionOnLang('ml', $id_shop).')
- ORDER BY LENGTH(ml.url_rewrite) DESC';
- if ($results = Db::getInstance()->executeS($sql))
- foreach ($results as $row)
- {
- if ($row['url_rewrite'])
- $this->addRoute($row['page'], $row['url_rewrite'], $row['page'], $row['id_lang'], array(), array(), $id_shop);
- }
- // Set default empty route if no empty route (that's weird I know)
- if (!$this->empty_route)
- $this->empty_route = array(
- 'routeID' => 'index',
- 'rule' => '',
- 'controller' => 'index',
- );
- // Load custom routes
- foreach ($this->default_routes as $route_id => $route_data)
- if ($custom_route = Configuration::get('PS_ROUTE_'.$route_id, null, null, $id_shop))
- {
- if (isset($context->language) && !in_array($context->language->id, $languages = Language::getLanguages()))
- $languages[] = (int)$context->language->id;
- foreach ($languages as $lang)
- $this->addRoute(
- $route_id,
- $custom_route,
- $route_data['controller'],
- $lang['id_lang'],
- $route_data['keywords'],
- isset($route_data['params']) ? $route_data['params'] : array(),
- $id_shop
- );
- }
- }
- }
- /**
- *
- * @param string $route_id Name of the route (need to be uniq, a second route with same name will override the first)
- * @param string $rule Url rule
- * @param string $controller Controller to call if request uri match the rule
- * @param int $id_lang
- * @param int $id_shop
- */
- public function addRoute($route_id, $rule, $controller, $id_lang = null, array $keywords = array(), array $params = array(), $id_shop = null)
- {
- if (isset(Context::getContext()->language) && $id_lang === null)
- $id_lang = (int)Context::getContext()->language->id;
- if (isset(Context::getContext()->shop) && $id_shop === null)
- $id_shop = (int)Context::getContext()->shop->id;
- $regexp = preg_quote($rule, '#');
- if ($keywords)
- {
- $transform_keywords = array();
- preg_match_all('#\\\{(([^{}]*)\\\:)?('.implode('|', array_keys($keywords)).')(\\\:([^{}]*))?\\\}#', $regexp, $m);
- for ($i = 0, $total = count($m[0]); $i < $total; $i++)
- {
- $prepend = $m[2][$i];
- $keyword = $m[3][$i];
- $append = $m[5][$i];
- $transform_keywords[$keyword] = array(
- 'required' => isset($keywords[$keyword]['param']),
- 'prepend' => stripslashes($prepend),
- 'append' => stripslashes($append),
- );
- $prepend_regexp = $append_regexp = '';
- if ($prepend || $append)
- {
- $prepend_regexp = '('.preg_quote($prepend);
- $append_regexp = preg_quote($append).')?';
- }
- if (isset($keywords[$keyword]['param']))
- $regexp = str_replace($m[0][$i], $prepend_regexp.'(?P<'.$keywords[$keyword]['param'].'>'.$keywords[$keyword]['regexp'].')'.$append_regexp, $regexp);
- else
- $regexp = str_replace($m[0][$i], $prepend_regexp.'('.$keywords[$keyword]['regexp'].')'.$append_regexp, $regexp);
- }
- $keywords = $transform_keywords;
- }
- $regexp = '#^/'.$regexp.'$#u';
- if (!isset($this->routes[$id_shop]))
- $this->routes[$id_shop] = array();
- if (!isset($this->routes[$id_shop][$id_lang]))
- $this->routes[$id_shop][$id_lang] = array();
- $this->routes[$id_shop][$id_lang][$route_id] = array(
- 'rule' => $rule,
- 'regexp' => $regexp,
- 'controller' => $controller,
- 'keywords' => $keywords,
- 'params' => $params,
- );
- }
- /**
- * Check if a route exists
- *
- * @param string $route_id
- * @param int $id_lang
- * @param int $id_shop
- * @return bool
- */
- public function hasRoute($route_id, $id_lang = null, $id_shop = null)
- {
- if (isset(Context::getContext()->language) && $id_lang === null)
- $id_lang = (int)Context::getContext()->language->id;
- if (isset(Context::getContext()->shop) && $id_shop === null)
- $id_shop = (int)Context::getContext()->shop->id;
- return isset($this->routes[$id_shop]) && isset($this->routes[$id_shop][$id_lang]) && isset($this->routes[$id_shop][$id_lang][$route_id]);
- }
- /**
- * Check if a keyword is written in a route rule
- *
- * @param string $route_id
- * @param int $id_lang
- * @param string $keyword
- * @param int $id_shop
- * @return bool
- */
- public function hasKeyword($route_id, $id_lang, $keyword, $id_shop = null)
- {
- if ($id_shop === null)
- $id_shop = (int)Context::getContext()->shop->id;
- if (!isset($this->routes[$id_shop]))
- $this->loadRoutes($id_shop);
- if (!isset($this->routes[$id_shop]) || !isset($this->routes[$id_shop][$id_lang]) || !isset($this->routes[$id_shop][$id_lang][$route_id]))
- return false;
- return preg_match('#\{([^{}]*:)?'.preg_quote($keyword, '#').'(:[^{}]*)?\}#', $this->routes[$id_shop][$id_lang][$route_id]['rule']);
- }
- /**
- * Check if a route rule contain all required keywords of default route definition
- *
- * @param string $route_id
- * @param string $rule Rule to verify
- * @param array $errors List of missing keywords
- */
- public function validateRoute($route_id, $rule, &$errors = array())
- {
- $errors = array();
- if (!isset($this->default_routes[$route_id]))
- return false;
- foreach ($this->default_routes[$route_id]['keywords'] as $keyword => $data)
- if (isset($data['param']) && !preg_match('#\{([^{}]*:)?'.$keyword.'(:[^{}]*)?\}#', $rule))
- $errors[] = $keyword;
- return (count($errors)) ? false : true;
- }
- /**
- * Create an url from
- *
- * @param string $route_id Name the route
- * @param int $id_lang
- * @param array $params
- * @param bool $use_routes If false, don't use to create this url
- * @param string $anchor Optional anchor to add at the end of this url
- */
- public function createUrl($route_id, $id_lang = null, array $params = array(), $force_routes = false, $anchor = '', $id_shop = null)
- {
- if ($id_lang === null)
- $id_lang = (int)Context::getContext()->language->id;
- if ($id_shop === null)
- $id_shop = (int)Context::getContext()->shop->id;
- if (!isset($this->routes[$id_shop]))
- $this->loadRoutes($id_shop);
- if (!isset($this->routes[$id_shop][$id_lang][$route_id]))
- {
- $query = http_build_query($params, '', '&');
- $index_link = $this->use_routes ? '' : 'index.php';
- return ($route_id == 'index') ? $index_link.(($query) ? '?'.$query : '') : ((trim($route_id) == '') ? '' : 'index.php?controller='.$route_id).(($query) ? '&'.$query : '').$anchor;
- }
- $route = $this->routes[$id_shop][$id_lang][$route_id];
- // Check required fields
- $query_params = isset($route['params']) ? $route['params'] : array();
- foreach ($route['keywords'] as $key => $data)
- {
- if (!$data['required'])
- continue;
- if (!array_key_exists($key, $params))
- throw new PrestaShopException('Dispatcher::createUrl() miss required parameter "'.$key.'" for route "'.$route_id.'"');
- if (isset($this->default_routes[$route_id]))
- $query_params[$this->default_routes[$route_id]['keywords'][$key]['param']] = $params[$key];
- }
- // Build an url which match a route
- if ($this->use_routes || $force_routes)
- {
- $url = $route['rule'];
- $add_param = array();
- foreach ($params as $key => $value)
- {
- if (!isset($route['keywords'][$key]))
- {
- if (!isset($this->default_routes[$route_id]['keywords'][$key]))
- $add_param[$key] = $value;
- }
- else
- {
- if ($params[$key])
- $replace = $route['keywords'][$key]['prepend'].$params[$key].$route['keywords'][$key]['append'];
- else
- $replace = '';
- $url = preg_replace('#\{([^{}]*:)?'.$key.'(:[^{}]*)?\}#', $replace, $url);
- }
- }
- $url = preg_replace('#\{([^{}]*:)?[a-z0-9_]+?(:[^{}]*)?\}#', '', $url);
- if (count($add_param))
- $url .= '?'.http_build_query($add_param, '', '&');
- }
- // Build a classic url index.php?controller=foo&...
- else
- {
- $add_params = array();
- foreach ($params as $key => $value)
- if (!isset($route['keywords'][$key]) && !isset($this->default_routes[$route_id]['keywords'][$key]))
- $add_params[$key] = $value;
- if (!empty($route['controller']))
- $query_params['controller'] = $route['controller'];
- $query = http_build_query(array_merge($add_params, $query_params), '', '&');
- if ($this->multilang_activated)
- $query .= (!empty($query) ? '&' : '').'id_lang='.(int)$id_lang;
- $url = 'index.php?'.$query;
- }
- return $url.$anchor;
- }
- /**
- * Retrieve the controller from url or request uri if routes are activated
- *
- * @return string
- */
- public function getController($id_shop = null)
- {
- if (defined('_PS_ADMIN_DIR_'))
- $_GET['controllerUri'] = Tools::getvalue('controller');
- if ($this->controller)
- {
- $_GET['controller'] = $this->controller;
- return $this->controller;
- }
- if (isset(Context::getContext()->shop) && $id_shop === null)
- $id_shop = (int)Context::getContext()->shop->id;
- $controller = Tools::getValue('controller');
- if (isset($controller) && is_string($controller) && preg_match('/^([0-9a-z_-]+)\?(.*)=(.*)$/Ui', $controller, $m))
- {
- $controller = $m[1];
- if (isset($_GET['controller']))
- $_GET[$m[2]] = $m[3];
- elseif (isset($_POST['controller']))
- $_POST[$m[2]] = $m[3];
- }
- if (!Validate::isControllerName($controller))
- $controller = false;
- // Use routes ? (for url rewriting)
- if ($this->use_routes && !$controller && !defined('_PS_ADMIN_DIR_'))
- {
- if (!$this->request_uri)
- return strtolower($this->controller_not_found);
- $controller = $this->controller_not_found;
- // If the request_uri matches a static file, then there is no need to check the routes, we keep "controller_not_found" (a static file should not go through the dispatcher)
- if (!preg_match('/\.(gif|jpe?g|png|css|js|ico)$/i', parse_url($this->request_uri, PHP_URL_PATH)))
- {
- // Add empty route as last route to prevent this greedy regexp to match request uri before right time
- if ($this->empty_route)
- $this->addRoute($this->empty_route['routeID'], $this->empty_route['rule'], $this->empty_route['controller'], Context::getContext()->language->id, array(), array(), $id_shop);
- list($uri) = explode('?', $this->request_uri);
- if (isset($this->routes[$id_shop][Context::getContext()->language->id]))
- foreach ($this->routes[$id_shop][Context::getContext()->language->id] as $route)
- if (preg_match($route['regexp'], $uri, $m))
- {
- // Route found ! Now fill $_GET with parameters of uri
- foreach ($m as $k => $v)
- if (!is_numeric($k))
- $_GET[$k] = $v;
- $controller = $route['controller'] ? $route['controller'] : $_GET['controller'];
- if (!empty($route['params']))
- foreach ($route['params'] as $k => $v)
- $_GET[$k] = $v;
- // A patch for module friendly urls
- if (preg_match('#module-([a-z0-9_-]+)-([a-z0-9_]+)$#i', $controller, $m))
- {
- $_GET['module'] = $m[1];
- $_GET['fc'] = 'module';
- $controller = $m[2];
- }
- if (isset($_GET['fc']) && $_GET['fc'] == 'module')
- $this->front_controller = self::FC_MODULE;
- break;
- }
- }
- if ($controller == 'index' || preg_match('/^\/index.php(?:\?.*)?$/', $this->request_uri))
- $controller = $this->useDefaultController();
- }
- $this->controller = str_replace('-', '', $controller);
- $_GET['controller'] = $this->controller;
- return $this->controller;
- }
- /**
- * Get list of all available FO controllers
- *
- * @var mixed $dirs
- * @return array
- */
- public static function getControllers($dirs)
- {
- if (!is_array($dirs))
- $dirs = array($dirs);
- $controllers = array();
- foreach ($dirs as $dir)
- $controllers = array_merge($controllers, Dispatcher::getControllersInDirectory($dir));
- return $controllers;
- }
- /**
- * Get list of all available Module Front controllers
- *
- * @return array
- */
- public static function getModuleControllers($type = 'all', $module = null)
- {
- $modules_controllers = array();
- if (is_null($module))
- $modules = Module::getModulesOnDisk(true);
- elseif (!is_array($module))
- $modules = array(Module::getInstanceByName($module));
- else
- {
- $modules = array();
- foreach ($module as $_mod)
- $modules[] = Module::getInstanceByName($_mod);
- }
- foreach ($modules as $mod)
- {
- foreach (Dispatcher::getControllersInDirectory(_PS_MODULE_DIR_.$mod->name.'/controllers/') as $controller)
- {
- if ($type == 'admin')
- {
- if (strpos($controller, 'Admin') !== false)
- $modules_controllers[$mod->name][] = $controller;
- }
- elseif ($type == 'front')
- {
- if (strpos($controller, 'Admin') === false)
- $modules_controllers[$mod->name][] = $controller;
- }
- else
- $modules_controllers[$mod->name][] = $controller;
- }
- }
- return $modules_controllers;
- }
- /**
- * Get list of available controllers from the specified dir
- *
- * @param string dir directory to scan (recursively)
- * @return array
- */
- public static function getControllersInDirectory($dir)
- {
- if (!is_dir($dir))
- return array();
- $controllers = array();
- $controller_files = scandir($dir);
- foreach ($controller_files as $controller_filename)
- {
- if ($controller_filename[0] != '.')
- {
- if (!strpos($controller_filename, '.php') && is_dir($dir.$controller_filename))
- $controllers += Dispatcher::getControllersInDirectory($dir.$controller_filename.DIRECTORY_SEPARATOR);
- elseif ($controller_filename != 'index.php')
- {
- $key = str_replace(array('controller.php', '.php'), '', strtolower($controller_filename));
- $controllers[$key] = basename($controller_filename, '.php');
- }
- }
- }
- return $controllers;
- }
- }