PathValidator.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <?php
  2. namespace Drupal\Core\Path;
  3. use Drupal\Component\Utility\UrlHelper;
  4. use Drupal\Core\ParamConverter\ParamNotConvertedException;
  5. use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
  6. use Drupal\Core\Routing\AccessAwareRouterInterface;
  7. use Drupal\Core\Routing\RequestContext;
  8. use Drupal\Core\Session\AccountInterface;
  9. use Drupal\Core\Url;
  10. use Symfony\Cmf\Component\Routing\RouteObjectInterface;
  11. use Symfony\Component\HttpFoundation\Request;
  12. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  13. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  14. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  15. use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
  16. /**
  17. * Provides a default path validator and access checker.
  18. */
  19. class PathValidator implements PathValidatorInterface {
  20. /**
  21. * The access aware router.
  22. *
  23. * @var \Drupal\Core\Routing\AccessAwareRouterInterface
  24. */
  25. protected $accessAwareRouter;
  26. /**
  27. * A router implementation which does not check access.
  28. *
  29. * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface
  30. */
  31. protected $accessUnawareRouter;
  32. /**
  33. * The current user.
  34. *
  35. * @var \Drupal\Core\Session\AccountInterface
  36. */
  37. protected $account;
  38. /**
  39. * The path processor.
  40. *
  41. * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface
  42. */
  43. protected $pathProcessor;
  44. /**
  45. * Creates a new PathValidator.
  46. *
  47. * @param \Drupal\Core\Routing\AccessAwareRouterInterface $access_aware_router
  48. * The access aware router.
  49. * @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $access_unaware_router
  50. * A router implementation which does not check access.
  51. * @param \Drupal\Core\Session\AccountInterface $account
  52. * The current user.
  53. * @param \Drupal\Core\PathProcessor\InboundPathProcessorInterface $path_processor
  54. * The path processor;
  55. */
  56. public function __construct(AccessAwareRouterInterface $access_aware_router, UrlMatcherInterface $access_unaware_router, AccountInterface $account, InboundPathProcessorInterface $path_processor) {
  57. $this->accessAwareRouter = $access_aware_router;
  58. $this->accessUnawareRouter = $access_unaware_router;
  59. $this->account = $account;
  60. $this->pathProcessor = $path_processor;
  61. }
  62. /**
  63. * {@inheritdoc}
  64. */
  65. public function isValid($path) {
  66. return (bool) $this->getUrlIfValid($path);
  67. }
  68. /**
  69. * {@inheritdoc}
  70. */
  71. public function getUrlIfValid($path) {
  72. return $this->getUrl($path, TRUE);
  73. }
  74. /**
  75. * {@inheritdoc}
  76. */
  77. public function getUrlIfValidWithoutAccessCheck($path) {
  78. return $this->getUrl($path, FALSE);
  79. }
  80. /**
  81. * Helper for getUrlIfValid() and getUrlIfValidWithoutAccessCheck().
  82. */
  83. protected function getUrl($path, $access_check) {
  84. $path = ltrim($path, '/');
  85. $parsed_url = UrlHelper::parse($path);
  86. $options = [];
  87. if (!empty($parsed_url['query'])) {
  88. $options['query'] = $parsed_url['query'];
  89. }
  90. if (!empty($parsed_url['fragment'])) {
  91. $options['fragment'] = $parsed_url['fragment'];
  92. }
  93. if ($parsed_url['path'] == '<front>') {
  94. return new Url('<front>', [], $options);
  95. }
  96. elseif ($parsed_url['path'] == '<none>') {
  97. return new Url('<none>', [], $options);
  98. }
  99. elseif (UrlHelper::isExternal($path) && UrlHelper::isValid($path)) {
  100. if (empty($parsed_url['path'])) {
  101. return FALSE;
  102. }
  103. return Url::fromUri($path);
  104. }
  105. $request = Request::create('/' . $path);
  106. $attributes = $this->getPathAttributes($path, $request, $access_check);
  107. if (!$attributes) {
  108. return FALSE;
  109. }
  110. $route_name = $attributes[RouteObjectInterface::ROUTE_NAME];
  111. $route_parameters = $attributes['_raw_variables']->all();
  112. return new Url($route_name, $route_parameters, $options + ['query' => $request->query->all()]);
  113. }
  114. /**
  115. * Gets the matched attributes for a given path.
  116. *
  117. * @param string $path
  118. * The path to check.
  119. * @param \Symfony\Component\HttpFoundation\Request $request
  120. * A request object with the given path.
  121. * @param bool $access_check
  122. * If FALSE then skip access check and check only whether the path is
  123. * valid.
  124. *
  125. * @return array|bool
  126. * An array of request attributes of FALSE if an exception was thrown.
  127. */
  128. protected function getPathAttributes($path, Request $request, $access_check) {
  129. if (!$access_check || $this->account->hasPermission('link to any page')) {
  130. $router = $this->accessUnawareRouter;
  131. }
  132. else {
  133. $router = $this->accessAwareRouter;
  134. }
  135. $initial_request_context = $router->getContext() ? $router->getContext() : new RequestContext();
  136. $path = $this->pathProcessor->processInbound('/' . $path, $request);
  137. try {
  138. $request_context = new RequestContext();
  139. $request_context->fromRequest($request);
  140. $router->setContext($request_context);
  141. $result = $router->match($path);
  142. }
  143. catch (ResourceNotFoundException $e) {
  144. $result = FALSE;
  145. }
  146. catch (ParamNotConvertedException $e) {
  147. $result = FALSE;
  148. }
  149. catch (AccessDeniedHttpException $e) {
  150. $result = FALSE;
  151. }
  152. catch (MethodNotAllowedException $e) {
  153. $result = FALSE;
  154. }
  155. $router->setContext($initial_request_context);
  156. return $result;
  157. }
  158. }