Router.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Routing;
  11. use Symfony\Component\Config\Loader\LoaderInterface;
  12. use Symfony\Component\Config\ConfigCache;
  13. use Psr\Log\LoggerInterface;
  14. use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface;
  15. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  16. use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
  17. /**
  18. * The Router class is an example of the integration of all pieces of the
  19. * routing system for easier use.
  20. *
  21. * @author Fabien Potencier <fabien@symfony.com>
  22. */
  23. class Router implements RouterInterface
  24. {
  25. /**
  26. * @var UrlMatcherInterface|null
  27. */
  28. protected $matcher;
  29. /**
  30. * @var UrlGeneratorInterface|null
  31. */
  32. protected $generator;
  33. /**
  34. * @var RequestContext
  35. */
  36. protected $context;
  37. /**
  38. * @var LoaderInterface
  39. */
  40. protected $loader;
  41. /**
  42. * @var RouteCollection|null
  43. */
  44. protected $collection;
  45. /**
  46. * @var mixed
  47. */
  48. protected $resource;
  49. /**
  50. * @var array
  51. */
  52. protected $options = array();
  53. /**
  54. * @var LoggerInterface|null
  55. */
  56. protected $logger;
  57. /**
  58. * Constructor.
  59. *
  60. * @param LoaderInterface $loader A LoaderInterface instance
  61. * @param mixed $resource The main resource to load
  62. * @param array $options An array of options
  63. * @param RequestContext $context The context
  64. * @param LoggerInterface $logger A logger instance
  65. */
  66. public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null)
  67. {
  68. $this->loader = $loader;
  69. $this->resource = $resource;
  70. $this->logger = $logger;
  71. $this->context = null === $context ? new RequestContext() : $context;
  72. $this->setOptions($options);
  73. }
  74. /**
  75. * Sets options.
  76. *
  77. * Available options:
  78. *
  79. * * cache_dir: The cache directory (or null to disable caching)
  80. * * debug: Whether to enable debugging or not (false by default)
  81. * * resource_type: Type hint for the main resource (optional)
  82. *
  83. * @param array $options An array of options
  84. *
  85. * @throws \InvalidArgumentException When unsupported option is provided
  86. */
  87. public function setOptions(array $options)
  88. {
  89. $this->options = array(
  90. 'cache_dir' => null,
  91. 'debug' => false,
  92. 'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
  93. 'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
  94. 'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
  95. 'generator_cache_class' => 'ProjectUrlGenerator',
  96. 'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
  97. 'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
  98. 'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
  99. 'matcher_cache_class' => 'ProjectUrlMatcher',
  100. 'resource_type' => null,
  101. 'strict_requirements' => true,
  102. );
  103. // check option names and live merge, if errors are encountered Exception will be thrown
  104. $invalid = array();
  105. foreach ($options as $key => $value) {
  106. if (array_key_exists($key, $this->options)) {
  107. $this->options[$key] = $value;
  108. } else {
  109. $invalid[] = $key;
  110. }
  111. }
  112. if ($invalid) {
  113. throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid)));
  114. }
  115. }
  116. /**
  117. * Sets an option.
  118. *
  119. * @param string $key The key
  120. * @param mixed $value The value
  121. *
  122. * @throws \InvalidArgumentException
  123. */
  124. public function setOption($key, $value)
  125. {
  126. if (!array_key_exists($key, $this->options)) {
  127. throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
  128. }
  129. $this->options[$key] = $value;
  130. }
  131. /**
  132. * Gets an option value.
  133. *
  134. * @param string $key The key
  135. *
  136. * @return mixed The value
  137. *
  138. * @throws \InvalidArgumentException
  139. */
  140. public function getOption($key)
  141. {
  142. if (!array_key_exists($key, $this->options)) {
  143. throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
  144. }
  145. return $this->options[$key];
  146. }
  147. /**
  148. * {@inheritdoc}
  149. */
  150. public function getRouteCollection()
  151. {
  152. if (null === $this->collection) {
  153. $this->collection = $this->loader->load($this->resource, $this->options['resource_type']);
  154. }
  155. return $this->collection;
  156. }
  157. /**
  158. * {@inheritdoc}
  159. */
  160. public function setContext(RequestContext $context)
  161. {
  162. $this->context = $context;
  163. if (null !== $this->matcher) {
  164. $this->getMatcher()->setContext($context);
  165. }
  166. if (null !== $this->generator) {
  167. $this->getGenerator()->setContext($context);
  168. }
  169. }
  170. /**
  171. * {@inheritdoc}
  172. */
  173. public function getContext()
  174. {
  175. return $this->context;
  176. }
  177. /**
  178. * {@inheritdoc}
  179. */
  180. public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
  181. {
  182. return $this->getGenerator()->generate($name, $parameters, $referenceType);
  183. }
  184. /**
  185. * {@inheritdoc}
  186. */
  187. public function match($pathinfo)
  188. {
  189. return $this->getMatcher()->match($pathinfo);
  190. }
  191. /**
  192. * Gets the UrlMatcher instance associated with this Router.
  193. *
  194. * @return UrlMatcherInterface A UrlMatcherInterface instance
  195. */
  196. public function getMatcher()
  197. {
  198. if (null !== $this->matcher) {
  199. return $this->matcher;
  200. }
  201. if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
  202. return $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context);
  203. }
  204. $class = $this->options['matcher_cache_class'];
  205. $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']);
  206. if (!$cache->isFresh($class)) {
  207. $dumper = new $this->options['matcher_dumper_class']($this->getRouteCollection());
  208. $options = array(
  209. 'class' => $class,
  210. 'base_class' => $this->options['matcher_base_class'],
  211. );
  212. $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
  213. }
  214. require_once $cache;
  215. return $this->matcher = new $class($this->context);
  216. }
  217. /**
  218. * Gets the UrlGenerator instance associated with this Router.
  219. *
  220. * @return UrlGeneratorInterface A UrlGeneratorInterface instance
  221. */
  222. public function getGenerator()
  223. {
  224. if (null !== $this->generator) {
  225. return $this->generator;
  226. }
  227. if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
  228. $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger);
  229. } else {
  230. $class = $this->options['generator_cache_class'];
  231. $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']);
  232. if (!$cache->isFresh($class)) {
  233. $dumper = new $this->options['generator_dumper_class']($this->getRouteCollection());
  234. $options = array(
  235. 'class' => $class,
  236. 'base_class' => $this->options['generator_base_class'],
  237. );
  238. $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
  239. }
  240. require_once $cache;
  241. $this->generator = new $class($this->context, $this->logger);
  242. }
  243. if ($this->generator instanceof ConfigurableRequirementsInterface) {
  244. $this->generator->setStrictRequirements($this->options['strict_requirements']);
  245. }
  246. return $this->generator;
  247. }
  248. }