Base.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <?php
  2. /**
  3. * Base router
  4. *
  5. * Copyright © Magento, Inc. All rights reserved.
  6. * See COPYING.txt for license details.
  7. */
  8. namespace Magento\Framework\App\Router;
  9. /**
  10. * Base router implementation.
  11. *
  12. * @SuppressWarnings(PHPMD.TooManyFields)
  13. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  14. */
  15. class Base implements \Magento\Framework\App\RouterInterface
  16. {
  17. /**
  18. * No route constant used for request
  19. */
  20. const NO_ROUTE = 'noroute';
  21. /**
  22. * @var \Magento\Framework\App\ActionFactory
  23. */
  24. protected $actionFactory;
  25. /**
  26. * @var string
  27. */
  28. protected $actionInterface = \Magento\Framework\App\ActionInterface::class;
  29. /**
  30. * @var array
  31. */
  32. protected $_modules = [];
  33. /**
  34. * @var array
  35. */
  36. protected $_dispatchData = [];
  37. /**
  38. * List of required request parameters
  39. * Order sensitive
  40. * @var string[]
  41. */
  42. protected $_requiredParams = ['moduleFrontName', 'actionPath', 'actionName'];
  43. /**
  44. * @var \Magento\Framework\App\Route\ConfigInterface
  45. */
  46. protected $_routeConfig;
  47. /**
  48. * Url security information.
  49. *
  50. * @var \Magento\Framework\Url\SecurityInfoInterface
  51. */
  52. protected $_urlSecurityInfo;
  53. /**
  54. * Core store config
  55. *
  56. * @var \Magento\Framework\App\Config\ScopeConfigInterface
  57. */
  58. protected $_scopeConfig;
  59. /**
  60. * @var \Magento\Framework\UrlInterface
  61. */
  62. protected $_url;
  63. /**
  64. * @var \Magento\Store\Model\StoreManagerInterface
  65. */
  66. protected $_storeManager;
  67. /**
  68. * @var \Magento\Framework\App\ResponseFactory
  69. */
  70. protected $_responseFactory;
  71. /**
  72. * @var \Magento\Framework\App\DefaultPathInterface
  73. */
  74. protected $_defaultPath;
  75. /**
  76. * @var \Magento\Framework\Code\NameBuilder
  77. */
  78. protected $nameBuilder;
  79. /**
  80. * @var array
  81. */
  82. protected $reservedNames = ['new', 'print', 'switch', 'return'];
  83. /**
  84. * Allows to control if we need to enable no route functionality in current router
  85. *
  86. * @var bool
  87. */
  88. protected $applyNoRoute = false;
  89. /**
  90. * @var string
  91. */
  92. protected $pathPrefix = null;
  93. /**
  94. * @var \Magento\Framework\App\Router\ActionList
  95. */
  96. protected $actionList;
  97. /**
  98. * @var \Magento\Framework\App\Router\PathConfigInterface
  99. */
  100. protected $pathConfig;
  101. /**
  102. * @param \Magento\Framework\App\Router\ActionList $actionList
  103. * @param \Magento\Framework\App\ActionFactory $actionFactory
  104. * @param \Magento\Framework\App\DefaultPathInterface $defaultPath
  105. * @param \Magento\Framework\App\ResponseFactory $responseFactory
  106. * @param \Magento\Framework\App\Route\ConfigInterface $routeConfig
  107. * @param \Magento\Framework\UrlInterface $url
  108. * @param \Magento\Framework\Code\NameBuilder $nameBuilder
  109. * @param \Magento\Framework\App\Router\PathConfigInterface $pathConfig
  110. *
  111. * @throws \InvalidArgumentException
  112. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  113. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  114. */
  115. public function __construct(
  116. \Magento\Framework\App\Router\ActionList $actionList,
  117. \Magento\Framework\App\ActionFactory $actionFactory,
  118. \Magento\Framework\App\DefaultPathInterface $defaultPath,
  119. \Magento\Framework\App\ResponseFactory $responseFactory,
  120. \Magento\Framework\App\Route\ConfigInterface $routeConfig,
  121. \Magento\Framework\UrlInterface $url,
  122. \Magento\Framework\Code\NameBuilder $nameBuilder,
  123. \Magento\Framework\App\Router\PathConfigInterface $pathConfig
  124. ) {
  125. $this->actionList = $actionList;
  126. $this->actionFactory = $actionFactory;
  127. $this->_responseFactory = $responseFactory;
  128. $this->_defaultPath = $defaultPath;
  129. $this->_routeConfig = $routeConfig;
  130. $this->_url = $url;
  131. $this->nameBuilder = $nameBuilder;
  132. $this->pathConfig = $pathConfig;
  133. }
  134. /**
  135. * Match provided request and if matched - return corresponding controller
  136. *
  137. * @param \Magento\Framework\App\RequestInterface $request
  138. * @return \Magento\Framework\App\ActionInterface|null
  139. */
  140. public function match(\Magento\Framework\App\RequestInterface $request)
  141. {
  142. $params = $this->parseRequest($request);
  143. return $this->matchAction($request, $params);
  144. }
  145. /**
  146. * Parse request URL params
  147. *
  148. * @param \Magento\Framework\App\RequestInterface $request
  149. * @return array
  150. */
  151. protected function parseRequest(\Magento\Framework\App\RequestInterface $request)
  152. {
  153. $output = [];
  154. $path = trim($request->getPathInfo(), '/');
  155. $params = explode('/', $path ? $path : $this->pathConfig->getDefaultPath());
  156. foreach ($this->_requiredParams as $paramName) {
  157. $output[$paramName] = array_shift($params);
  158. }
  159. for ($i = 0, $l = sizeof($params); $i < $l; $i += 2) {
  160. $output['variables'][$params[$i]] = isset($params[$i + 1]) ? urldecode($params[$i + 1]) : '';
  161. }
  162. return $output;
  163. }
  164. /**
  165. * Match module front name
  166. *
  167. * @param \Magento\Framework\App\RequestInterface $request
  168. * @param string $param
  169. * @return string|null
  170. */
  171. protected function matchModuleFrontName(\Magento\Framework\App\RequestInterface $request, $param)
  172. {
  173. // get module name
  174. if ($request->getModuleName()) {
  175. $moduleFrontName = $request->getModuleName();
  176. } elseif (!empty($param)) {
  177. $moduleFrontName = $param;
  178. } else {
  179. $moduleFrontName = $this->_defaultPath->getPart('module');
  180. $request->setAlias(\Magento\Framework\Url::REWRITE_REQUEST_PATH_ALIAS, '');
  181. }
  182. if (!$moduleFrontName) {
  183. return null;
  184. }
  185. return $moduleFrontName;
  186. }
  187. /**
  188. * Match controller name
  189. *
  190. * @param \Magento\Framework\App\RequestInterface $request
  191. * @param string $param
  192. * @return string
  193. */
  194. protected function matchActionPath(\Magento\Framework\App\RequestInterface $request, $param)
  195. {
  196. if ($request->getControllerName()) {
  197. $actionPath = $request->getControllerName();
  198. } elseif (!empty($param)) {
  199. $actionPath = $param;
  200. } else {
  201. $actionPath = $this->_defaultPath->getPart('controller');
  202. $request->setAlias(
  203. \Magento\Framework\Url::REWRITE_REQUEST_PATH_ALIAS,
  204. ltrim($request->getOriginalPathInfo(), '/')
  205. );
  206. }
  207. return $actionPath;
  208. }
  209. /**
  210. * Get not found controller instance
  211. *
  212. * @param string $currentModuleName
  213. * @return \Magento\Framework\App\ActionInterface|null
  214. */
  215. protected function getNotFoundAction($currentModuleName)
  216. {
  217. if (!$this->applyNoRoute) {
  218. return null;
  219. }
  220. $actionClassName = $this->getActionClassName($currentModuleName, 'noroute');
  221. if (!$actionClassName || !is_subclass_of($actionClassName, $this->actionInterface)) {
  222. return null;
  223. }
  224. // instantiate action class
  225. return $this->actionFactory->create($actionClassName);
  226. }
  227. /**
  228. * Create matched controller instance
  229. *
  230. * @param \Magento\Framework\App\RequestInterface $request
  231. * @param array $params
  232. * @return \Magento\Framework\App\ActionInterface|null
  233. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  234. * @SuppressWarnings(PHPMD.NPathComplexity)
  235. */
  236. protected function matchAction(\Magento\Framework\App\RequestInterface $request, array $params)
  237. {
  238. $moduleFrontName = $this->matchModuleFrontName($request, $params['moduleFrontName']);
  239. if (empty($moduleFrontName)) {
  240. return null;
  241. }
  242. /**
  243. * Searching router args by module name from route using it as key
  244. */
  245. $modules = $this->_routeConfig->getModulesByFrontName($moduleFrontName);
  246. if (empty($modules) === true) {
  247. return null;
  248. }
  249. /**
  250. * Going through modules to find appropriate controller
  251. */
  252. $currentModuleName = null;
  253. $actionPath = null;
  254. $action = null;
  255. $actionInstance = null;
  256. $actionPath = $this->matchActionPath($request, $params['actionPath']);
  257. $action = $request->getActionName() ?: ($params['actionName'] ?: $this->_defaultPath->getPart('action'));
  258. $this->_checkShouldBeSecure($request, '/' . $moduleFrontName . '/' . $actionPath . '/' . $action);
  259. foreach ($modules as $moduleName) {
  260. $currentModuleName = $moduleName;
  261. $actionClassName = $this->actionList->get($moduleName, $this->pathPrefix, $actionPath, $action);
  262. if (!$actionClassName || !is_subclass_of($actionClassName, $this->actionInterface)) {
  263. continue;
  264. }
  265. $actionInstance = $this->actionFactory->create($actionClassName);
  266. break;
  267. }
  268. if (null == $actionInstance) {
  269. $actionInstance = $this->getNotFoundAction($currentModuleName);
  270. if ($actionInstance === null) {
  271. return null;
  272. }
  273. $action = self::NO_ROUTE;
  274. }
  275. // set values only after all the checks are done
  276. $request->setModuleName($moduleFrontName);
  277. $request->setControllerName($actionPath);
  278. $request->setActionName($action);
  279. $request->setControllerModule($currentModuleName);
  280. $request->setRouteName($this->_routeConfig->getRouteByFrontName($moduleFrontName));
  281. if (isset($params['variables'])) {
  282. $request->setParams($params['variables']);
  283. }
  284. return $actionInstance;
  285. }
  286. /**
  287. * Build controller class name
  288. *
  289. * @param string $module
  290. * @param string $actionPath
  291. * @return string
  292. */
  293. public function getActionClassName($module, $actionPath)
  294. {
  295. $prefix = $this->pathPrefix ? 'Controller\\' . $this->pathPrefix : 'Controller';
  296. return $this->nameBuilder->buildClassName([$module, $prefix, $actionPath]);
  297. }
  298. /**
  299. * Check that request uses https protocol if it should.
  300. *
  301. * Function redirects user to correct URL if needed.
  302. *
  303. * @param \Magento\Framework\App\RequestInterface $request
  304. * @param string $path
  305. * @return void
  306. * @SuppressWarnings(PHPMD.ExitExpression)
  307. */
  308. protected function _checkShouldBeSecure(\Magento\Framework\App\RequestInterface $request, $path = '')
  309. {
  310. if ($request->getPostValue()) {
  311. return;
  312. }
  313. if ($this->pathConfig->shouldBeSecure($path) && !$request->isSecure()) {
  314. $url = $this->pathConfig->getCurrentSecureUrl($request);
  315. if ($this->_shouldRedirectToSecure()) {
  316. $url = $this->_url->getRedirectUrl($url);
  317. }
  318. $this->_responseFactory->create()->setRedirect($url)->sendResponse();
  319. exit;
  320. }
  321. }
  322. /**
  323. * Check whether redirect url should be used for secure routes
  324. *
  325. * @return bool
  326. */
  327. protected function _shouldRedirectToSecure()
  328. {
  329. return $this->_url->getUseSession();
  330. }
  331. }