PageRenderTime 26ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/vendor/symfony/lib/routing/sfPatternRouting.class.php

https://github.com/IDCI-Consulting/WebsiteEval
PHP | 530 lines | 320 code | 64 blank | 146 comment | 36 complexity | 918a3711064245effe09edc77db64a55 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * sfPatternRouting class controls the generation and parsing of URLs.
  11. *
  12. * It parses and generates URLs by delegating the work to an array of sfRoute objects.
  13. *
  14. * @package symfony
  15. * @subpackage routing
  16. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  17. * @version SVN: $Id: sfPatternRouting.class.php 32845 2011-07-28 11:50:48Z fabien $
  18. */
  19. class sfPatternRouting extends sfRouting
  20. {
  21. protected
  22. $currentRouteName = null,
  23. $currentInternalUri = array(),
  24. $routes = array(),
  25. $defaultParamsDirty = false,
  26. $cacheData = array(),
  27. $cacheChanged = false;
  28. /**
  29. * Initializes this Routing.
  30. *
  31. * Available options:
  32. *
  33. * * suffix: The default suffix
  34. * * variable_prefixes: An array of characters that starts a variable name (: by default)
  35. * * segment_separators: An array of allowed characters for segment separators (/ and . by default)
  36. * * variable_regex: A regex that match a valid variable name ([\w\d_]+ by default)
  37. * * generate_shortest_url: Whether to generate the shortest URL possible (true by default)
  38. * * extra_parameters_as_query_string: Whether to generate extra parameters as a query string
  39. * * lookup_cache_dedicated_keys: Whether to use dedicated keys for parse/generate cache (false by default)
  40. * WARNING: When this option is activated, do not use sfFileCache; use a fast access
  41. * cache backend (like sfAPCCache).
  42. *
  43. * @see sfRouting
  44. */
  45. public function initialize(sfEventDispatcher $dispatcher, sfCache $cache = null, $options = array())
  46. {
  47. $options = array_merge(array(
  48. 'variable_prefixes' => array(':'),
  49. 'segment_separators' => array('/', '.'),
  50. 'variable_regex' => '[\w\d_]+',
  51. 'load_configuration' => false,
  52. 'suffix' => '',
  53. 'generate_shortest_url' => true,
  54. 'extra_parameters_as_query_string' => true,
  55. 'lookup_cache_dedicated_keys' => false,
  56. ), $options);
  57. // for BC
  58. if ('.' == $options['suffix'])
  59. {
  60. $options['suffix'] = '';
  61. }
  62. parent::initialize($dispatcher, $cache, $options);
  63. if (null !== $this->cache && !$options['lookup_cache_dedicated_keys'] && $cacheData = $this->cache->get('symfony.routing.data'))
  64. {
  65. $this->cacheData = unserialize($cacheData);
  66. }
  67. }
  68. /**
  69. * @see sfRouting
  70. */
  71. public function loadConfiguration()
  72. {
  73. if ($this->options['load_configuration'] && $config = $this->getConfigFilename())
  74. {
  75. include($config);
  76. }
  77. parent::loadConfiguration();
  78. }
  79. /**
  80. * Added for better performance. We need to ensure that changed default parameters
  81. * are set, but resetting them everytime wastes many cpu cycles
  82. */
  83. protected function ensureDefaultParametersAreSet()
  84. {
  85. if ($this->defaultParamsDirty)
  86. {
  87. foreach ($this->routes as $route)
  88. {
  89. $route->setDefaultParameters($this->defaultParameters);
  90. }
  91. $this->defaultParamsDirty = false;
  92. }
  93. }
  94. /**
  95. * @see sfRouting
  96. */
  97. public function setDefaultParameter($key, $value)
  98. {
  99. parent::setDefaultParameter($key, $value);
  100. $this->defaultParamsDirty = true;
  101. }
  102. /**
  103. * @see sfRouting
  104. */
  105. public function setDefaultParameters($parameters)
  106. {
  107. parent::setDefaultParameters($parameters);
  108. $this->defaultParamsDirty = true;
  109. }
  110. protected function getConfigFileName()
  111. {
  112. return sfContext::getInstance()->getConfigCache()->checkConfig('config/routing.yml', true);
  113. }
  114. /**
  115. * @see sfRouting
  116. */
  117. public function getCurrentInternalUri($withRouteName = false)
  118. {
  119. return null === $this->currentRouteName ? null : $this->currentInternalUri[$withRouteName ? 0 : 1];
  120. }
  121. /**
  122. * Gets the current route name.
  123. *
  124. * @return string The route name
  125. */
  126. public function getCurrentRouteName()
  127. {
  128. return $this->currentRouteName;
  129. }
  130. /**
  131. * @see sfRouting
  132. */
  133. public function getRoutes()
  134. {
  135. return $this->routes;
  136. }
  137. /**
  138. * @see sfRouting
  139. */
  140. public function setRoutes($routes)
  141. {
  142. foreach ($routes as $name => $route)
  143. {
  144. $this->connect($name, $route);
  145. }
  146. }
  147. /**
  148. * @see sfRouting
  149. */
  150. public function hasRoutes()
  151. {
  152. return count($this->routes) ? true : false;
  153. }
  154. /**
  155. * @see sfRouting
  156. */
  157. public function clearRoutes()
  158. {
  159. if ($this->options['logging'])
  160. {
  161. $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Clear all current routes')));
  162. }
  163. $this->routes = array();
  164. }
  165. /**
  166. * Returns true if the route name given is defined.
  167. *
  168. * @param string $name The route name
  169. *
  170. * @return boolean
  171. */
  172. public function hasRouteName($name)
  173. {
  174. return isset($this->routes[$name]) ? true : false;
  175. }
  176. /**
  177. * Adds a new route at the beginning of the current list of routes.
  178. *
  179. * @see connect
  180. */
  181. public function prependRoute($name, $route)
  182. {
  183. $routes = $this->routes;
  184. $this->routes = array();
  185. $this->connect($name, $route);
  186. $this->routes = array_merge($this->routes, $routes);
  187. }
  188. /**
  189. * Adds a new route.
  190. *
  191. * Alias for the connect method.
  192. *
  193. * @see connect
  194. */
  195. public function appendRoute($name, $route)
  196. {
  197. return $this->connect($name, $route);
  198. }
  199. /**
  200. * Adds a new route before a given one in the current list of routes.
  201. *
  202. * @see connect
  203. */
  204. public function insertRouteBefore($pivot, $name, $route)
  205. {
  206. if (!isset($this->routes[$pivot]))
  207. {
  208. throw new sfConfigurationException(sprintf('Unable to insert route "%s" before inexistent route "%s".', $name, $pivot));
  209. }
  210. $routes = $this->routes;
  211. $this->routes = array();
  212. $newroutes = array();
  213. foreach ($routes as $key => $value)
  214. {
  215. if ($key == $pivot)
  216. {
  217. $this->connect($name, $route);
  218. $newroutes = array_merge($newroutes, $this->routes);
  219. }
  220. $newroutes[$key] = $value;
  221. }
  222. $this->routes = $newroutes;
  223. }
  224. /**
  225. * Adds a new route at the end of the current list of routes.
  226. *
  227. * A route string is a string with 2 special constructions:
  228. * - :string: :string denotes a named parameter (available later as $request->getParameter('string'))
  229. * - *: * match an indefinite number of parameters in a route
  230. *
  231. * Here is a very common rule in a symfony project:
  232. *
  233. * <code>
  234. * $r->connect('default', new sfRoute('/:module/:action/*'));
  235. * </code>
  236. *
  237. * @param string $name The route name
  238. * @param sfRoute $route A sfRoute instance
  239. *
  240. * @return array current routes
  241. */
  242. public function connect($name, $route)
  243. {
  244. $routes = $route instanceof sfRouteCollection ? $route : array($name => $route);
  245. foreach (self::flattenRoutes($routes) as $name => $route)
  246. {
  247. $this->routes[$name] = $route;
  248. $this->configureRoute($route);
  249. if ($this->options['logging'])
  250. {
  251. $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Connect %s "%s" (%s)', get_class($route), $name, $route->getPattern()))));
  252. }
  253. }
  254. }
  255. public function configureRoute(sfRoute $route)
  256. {
  257. $route->setDefaultParameters($this->defaultParameters);
  258. $route->setDefaultOptions($this->options);
  259. }
  260. /**
  261. * @see sfRouting
  262. */
  263. public function generate($name, $params = array(), $absolute = false)
  264. {
  265. // fetch from cache
  266. if (null !== $this->cache)
  267. {
  268. $cacheKey = 'generate_'.$name.'_'.md5(serialize(array_merge($this->defaultParameters, $params))).'_'.md5(serialize($this->options['context']));
  269. if ($this->options['lookup_cache_dedicated_keys'] && $url = $this->cache->get('symfony.routing.data.'.$cacheKey))
  270. {
  271. return $this->fixGeneratedUrl($url, $absolute);
  272. }
  273. elseif (isset($this->cacheData[$cacheKey]))
  274. {
  275. return $this->fixGeneratedUrl($this->cacheData[$cacheKey], $absolute);
  276. }
  277. }
  278. if ($name)
  279. {
  280. // named route
  281. if (!isset($this->routes[$name]))
  282. {
  283. throw new sfConfigurationException(sprintf('The route "%s" does not exist.', $name));
  284. }
  285. $route = $this->routes[$name];
  286. $this->ensureDefaultParametersAreSet();
  287. }
  288. else
  289. {
  290. // find a matching route
  291. if (false === $route = $this->getRouteThatMatchesParameters($params))
  292. {
  293. throw new sfConfigurationException(sprintf('Unable to find a matching route to generate url for params "%s".', is_object($params) ? 'Object('.get_class($params).')' : str_replace("\n", '', var_export($params, true))));
  294. }
  295. }
  296. $url = $route->generate($params, $this->options['context'], $absolute);
  297. // store in cache
  298. if (null !== $this->cache)
  299. {
  300. if ($this->options['lookup_cache_dedicated_keys'])
  301. {
  302. $this->cache->set('symfony.routing.data.'.$cacheKey, $url);
  303. }
  304. else
  305. {
  306. $this->cacheChanged = true;
  307. $this->cacheData[$cacheKey] = $url;
  308. }
  309. }
  310. return $this->fixGeneratedUrl($url, $absolute);
  311. }
  312. /**
  313. * @see sfRouting
  314. */
  315. public function parse($url)
  316. {
  317. if (false === $info = $this->findRoute($url))
  318. {
  319. $this->currentRouteName = null;
  320. $this->currentInternalUri = array();
  321. return false;
  322. }
  323. if ($this->options['logging'])
  324. {
  325. $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Match route "%s" (%s) for %s with parameters %s', $info['name'], $info['pattern'], $url, str_replace("\n", '', var_export($info['parameters'], true))))));
  326. }
  327. // store the current internal URI
  328. $this->updateCurrentInternalUri($info['name'], $info['parameters']);
  329. $route = $this->routes[$info['name']];
  330. $this->ensureDefaultParametersAreSet();
  331. $route->bind($this->options['context'], $info['parameters']);
  332. $info['parameters']['_sf_route'] = $route;
  333. return $info['parameters'];
  334. }
  335. protected function updateCurrentInternalUri($name, array $parameters)
  336. {
  337. // store the route name
  338. $this->currentRouteName = $name;
  339. $internalUri = array('@'.$this->currentRouteName, $parameters['module'].'/'.$parameters['action']);
  340. unset($parameters['module'], $parameters['action']);
  341. $params = array();
  342. foreach ($parameters as $key => $value)
  343. {
  344. $params[] = $key.'='.$value;
  345. }
  346. // sort to guaranty unicity
  347. sort($params);
  348. $params = $params ? '?'.implode('&', $params) : '';
  349. $this->currentInternalUri = array($internalUri[0].$params, $internalUri[1].$params);
  350. }
  351. /**
  352. * Finds a matching route for given URL.
  353. *
  354. * Returns false if no route matches.
  355. *
  356. * Returned array contains:
  357. *
  358. * - name: name or alias of the route that matched
  359. * - pattern: the compiled pattern of the route that matched
  360. * - parameters: array containing key value pairs of the request parameters including defaults
  361. *
  362. * @param string $url URL to be parsed
  363. *
  364. * @return array|false An array with routing information or false if no route matched
  365. */
  366. public function findRoute($url)
  367. {
  368. $url = $this->normalizeUrl($url);
  369. // fetch from cache
  370. if (null !== $this->cache)
  371. {
  372. $cacheKey = 'parse_'.$url.'_'.md5(serialize($this->options['context']));
  373. if ($this->options['lookup_cache_dedicated_keys'] && $info = $this->cache->get('symfony.routing.data.'.$cacheKey))
  374. {
  375. return unserialize($info);
  376. }
  377. elseif (isset($this->cacheData[$cacheKey]))
  378. {
  379. return $this->cacheData[$cacheKey];
  380. }
  381. }
  382. $info = $this->getRouteThatMatchesUrl($url);
  383. // store in cache
  384. if (null !== $this->cache)
  385. {
  386. if ($this->options['lookup_cache_dedicated_keys'])
  387. {
  388. $this->cache->set('symfony.routing.data.'.$cacheKey, serialize($info));
  389. }
  390. else
  391. {
  392. $this->cacheChanged = true;
  393. $this->cacheData[$cacheKey] = $info;
  394. }
  395. }
  396. return $info;
  397. }
  398. static public function flattenRoutes($routes)
  399. {
  400. $flattenRoutes = array();
  401. foreach ($routes as $name => $route)
  402. {
  403. if ($route instanceof sfRouteCollection)
  404. {
  405. $flattenRoutes = array_merge($flattenRoutes, self::flattenRoutes($route));
  406. }
  407. else
  408. {
  409. $flattenRoutes[$name] = $route;
  410. }
  411. }
  412. return $flattenRoutes;
  413. }
  414. protected function getRouteThatMatchesUrl($url)
  415. {
  416. $this->ensureDefaultParametersAreSet();
  417. foreach ($this->routes as $name => $route)
  418. {
  419. if (false === $parameters = $route->matchesUrl($url, $this->options['context']))
  420. {
  421. continue;
  422. }
  423. return array('name' => $name, 'pattern' => $route->getPattern(), 'parameters' => $parameters);
  424. }
  425. return false;
  426. }
  427. protected function getRouteThatMatchesParameters($parameters)
  428. {
  429. $this->ensureDefaultParametersAreSet();
  430. foreach ($this->routes as $route)
  431. {
  432. if ($route->matchesParameters($parameters, $this->options['context']))
  433. {
  434. return $route;
  435. }
  436. }
  437. return false;
  438. }
  439. protected function normalizeUrl($url)
  440. {
  441. // an URL should start with a '/', mod_rewrite doesn't respect that, but no-mod_rewrite version does.
  442. if ('/' != substr($url, 0, 1))
  443. {
  444. $url = '/'.$url;
  445. }
  446. // we remove the query string
  447. if (false !== $pos = strpos($url, '?'))
  448. {
  449. $url = substr($url, 0, $pos);
  450. }
  451. // remove multiple /
  452. $url = preg_replace('#/+#', '/', $url);
  453. return $url;
  454. }
  455. /**
  456. * @see sfRouting
  457. */
  458. public function shutdown()
  459. {
  460. if (null !== $this->cache && $this->cacheChanged)
  461. {
  462. $this->cacheChanged = false;
  463. $this->cache->set('symfony.routing.data', serialize($this->cacheData));
  464. }
  465. }
  466. }