/** * @param HttpRequest $httpRequest * * @return Response */ public function handle(HttpRequest $httpRequest) { try { $routeParameters = $this->urlMatcher->matchRequest($httpRequest); $httpRequest->attributes->add($routeParameters); $request = $this->extractRequestFromRouteParameters($routeParameters); $controller = $this->extractControllerFromRouteParameters($routeParameters); return $controller->handleRequest($request, $this->serviceContainer); } catch (ValidationExceptionSet $exception) { return $this->errorResponseFactory->createBadRequestResponse($exception); } catch (BadRequestHttpException $exception) { return $this->errorResponseFactory->createBadRequestResponse($exception); } catch (NotFoundHttpException $exception) { return $this->errorResponseFactory->createNotFoundResponse($exception); } catch (MethodNotAllowedException $exception) { $exception = new MethodNotAllowedHttpException($exception->getAllowedMethods(), 'Used request method not allowed. Allowed: ' . implode(', ', $exception->getAllowedMethods())); return $this->errorResponseFactory->createMethodNotAllowedResponse($exception); } catch (\Exception $exception) { return $this->errorResponseFactory->createInternalServerErrorResponse($exception); } }
/** * @param Request $request */ public function match(Request $request) { // Initialize the context that is also used by the generator (assuming matcher and generator share the same // context instance). $this->context->fromRequest($request); if ($request->attributes->has('_controller')) { // Routing is already done. return; } // Add attributes based on the request (routing). try { // Matching a request is more powerful than matching a URL path + context, so try that first. if ($this->matcher instanceof RequestMatcherInterface) { $parameters = $this->matcher->matchRequest($request); } else { $parameters = $this->matcher->match($request->getPathInfo()); } if (null !== $this->logger) { $this->logger->info(sprintf('Matched route "%s" (parameters: %s)', $parameters['_route'], $this->parametersToString($parameters))); } $request->attributes->add($parameters); unset($parameters['_route']); unset($parameters['_controller']); $request->attributes->set('_route_params', $parameters); } catch (ResourceNotFoundException $e) { $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo()); throw new NotFoundHttpException($message, $e); } catch (MethodNotAllowedException $e) { $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getPathInfo(), strtoupper(implode(', ', $e->getAllowedMethods()))); throw new MethodNotAllowedException($e->getAllowedMethods(), $message); } }
/** * Constructor. * * RequestStack will become required in 3.0. * * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher * @param RequestStack $requestStack A RequestStack instance * @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface) * @param LoggerInterface|null $logger The logger * * @throws \InvalidArgumentException */ public function __construct($matcher, $requestStack = null, $context = null, $logger = null) { if ($requestStack instanceof RequestContext || $context instanceof LoggerInterface || $logger instanceof RequestStack) { $tmp = $requestStack; $requestStack = $logger; $logger = $context; $context = $tmp; @trigger_error('The ' . __METHOD__ . ' method now requires a RequestStack to be given as second argument as ' . __CLASS__ . '::setRequest method will not be supported anymore in 3.0.', E_USER_DEPRECATED); } elseif (!$requestStack instanceof RequestStack) { @trigger_error('The ' . __METHOD__ . ' method now requires a RequestStack instance as ' . __CLASS__ . '::setRequest method will not be supported anymore in 3.0.', E_USER_DEPRECATED); } if (null !== $requestStack && !$requestStack instanceof RequestStack) { throw new \InvalidArgumentException('RequestStack instance expected.'); } if (null !== $context && !$context instanceof RequestContext) { throw new \InvalidArgumentException('RequestContext instance expected.'); } if (null !== $logger && !$logger instanceof LoggerInterface) { throw new \InvalidArgumentException('Logger must implement LoggerInterface.'); } if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); } if (null === $context && !$matcher instanceof RequestContextAwareInterface) { throw new \InvalidArgumentException('You must either pass a RequestContext or the matcher must implement RequestContextAwareInterface.'); } $this->matcher = $matcher; $this->context = $context ?: $matcher->getContext(); $this->requestStack = $requestStack; $this->logger = $logger; }
/** * Constructor. * * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher * @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface) * @param LoggerInterface|null $logger The logger * * @throws \InvalidArgumentException */ public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = null) { if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); } if (null === $context && !$matcher instanceof RequestContextAwareInterface) { throw new \InvalidArgumentException('You must either pass a RequestContext or the matcher must implement RequestContextAwareInterface.'); } $this->matcher = $matcher; $this->context = $context ?: $matcher->getContext(); $this->logger = $logger; }
protected function getAllowedMethods($uri) { try { $route = $this->requestMatcher->matchRequest(Request::create($uri, 'OPTIONS')); if (isset($route['allowedMethods'])) { return explode(',', $route['allowedMethods']); } } catch (ResourceNotFoundException $e) { // the provider doesn't care about a not found } catch (MethodNotAllowedException $e) { // neither does it care about a method not allowed } return array(); }
/** * Constructor. * * RequestStack will become required in 3.0. * * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher * @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface) * @param LoggerInterface|null $logger The logger * @param RequestStack|null $requestStack A RequestStack instance * * @throws \InvalidArgumentException */ public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = null, RequestStack $requestStack = null) { if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); } if (null === $context && !$matcher instanceof RequestContextAwareInterface) { throw new \InvalidArgumentException('You must either pass a RequestContext or the matcher must implement RequestContextAwareInterface.'); } if (!$requestStack instanceof RequestStack) { @trigger_error('The ' . __METHOD__ . ' method now requires a RequestStack instance as ' . __CLASS__ . '::setRequest method will not be supported anymore in 3.0.', E_USER_DEPRECATED); } $this->matcher = $matcher; $this->context = $context ?: $matcher->getContext(); $this->requestStack = $requestStack; $this->logger = $logger; }
/** * Matches a path in the router. * * @param string $path * The request path. * @param array $exclude * An array of paths or system paths to skip. * * @return \Symfony\Component\HttpFoundation\Request * A populated request object or NULL if the path couldn't be matched. */ protected function getRequestForPath($path, array $exclude) { if (!empty($exclude[$path])) { return NULL; } // @todo Use the RequestHelper once https://drupal.org/node/2090293 is // fixed. $request = Request::create($this->context->getBaseUrl() . '/' . $path); // Performance optimization: set a short accept header to reduce overhead in // AcceptHeaderMatcher when matching the request. $request->headers->set('Accept', 'text/html'); // Find the system path by resolving aliases, language prefix, etc. $processed = $this->pathProcessor->processInbound($path, $request); if (empty($processed) || !empty($exclude[$processed])) { // This resolves to the front page, which we already add. return NULL; } $request->attributes->set('_system_path', $processed); // Attempt to match this path to provide a fully built request. try { $request->attributes->add($this->router->matchRequest($request)); return $request; } catch (ParamNotConvertedException $e) { return NULL; } catch (ResourceNotFoundException $e) { return NULL; } catch (MethodNotAllowedException $e) { return NULL; } catch (AccessDeniedHttpException $e) { return NULL; } }
/** * {@inheritdoc} */ public function isValid($path) { // External URLs and the front page are always valid. if ($path == '<front>' || UrlHelper::isExternal($path)) { return TRUE; } // Check the routing system. $collection = $this->routeProvider->getRoutesByPattern('/' . $path); if ($collection->count() == 0) { return FALSE; } $request = RequestHelper::duplicate($this->requestStack->getCurrentRequest(), '/' . $path); $request->attributes->set('_system_path', $path); // We indicate that a menu administrator is running the menu access check. $request->attributes->set('_menu_admin', TRUE); // Attempt to match this path to provide a fully built request to the // access checker. try { $request->attributes->add($this->requestMatcher->matchRequest($request)); } catch (ParamNotConvertedException $e) { return FALSE; } // Consult the access manager. $routes = $collection->all(); $route = reset($routes); return $this->accessManager->check($route, $request, $this->account); }
/** * @return RequestMatcherInterface|UrlMatcherInterface */ public function getMatcher() { /* we may not set the context in DynamicRouter::setContext as this * would lead to symfony cache warmup problems. * a request matcher does not need the request context separately as it * can get it from the request. */ if ($this->matcher instanceof RequestContextAwareInterface) { $this->matcher->setContext($this->getContext()); } return $this->matcher; }
/** * Tests the breadcrumb for a user path. * * @covers ::build() * @covers ::getRequestForPath() */ public function testBuildWithUserPath() { $this->context->expects($this->once())->method('getPathInfo')->will($this->returnValue('/user/1/edit')); $this->setupStubPathProcessor(); $route_1 = new Route('/user/1'); $this->requestMatcher->expects($this->exactly(1))->method('matchRequest')->will($this->returnCallback(function (Request $request) use($route_1) { if ($request->getPathInfo() == '/user/1') { return array(RouteObjectInterface::ROUTE_NAME => 'user_page', RouteObjectInterface::ROUTE_OBJECT => $route_1, '_raw_variables' => new ParameterBag(array())); } })); $this->setupAccessManagerToAllow(); $this->titleResolver->expects($this->once())->method('getTitle')->with($this->anything(), $route_1)->will($this->returnValue('Admin')); $links = $this->builder->build($this->getMock('Drupal\\Core\\Routing\\RouteMatchInterface')); $this->assertEquals(array(0 => new Link('Home', new Url('<front>')), 1 => new Link('Admin', new Url('user_page'))), $links); }
/** * Matches a path in the router. * * @param \Symfony\Component\HttpFoundation\Request $request * Page request object. * @param string $path * Path to look up. * * @return \Symfony\Component\HttpFoundation\Request|null * A populated request object or NULL if the patch could not be matched. */ protected function getRequestForPath(Request $request, $path) { // @todo Use RequestHelper::duplicate once https://drupal.org/node/2090293 // is fixed. $route_request = Request::create($request->getBaseUrl() . '/' . $path); // Find the system path by resolving aliases, language prefix, etc. $processed = $this->pathProcessor->processInbound($path, $route_request); $route_request->attributes->set('_system_path', $processed); // Attempt to match this path to provide a fully built request. try { $route_request->attributes->add($this->router->matchRequest($route_request)); return $route_request; } catch (ParamNotConvertedException $e) { return NULL; } catch (ResourceNotFoundException $e) { return NULL; } }
/** * Tests the breadcrumb for a user path. * * @covers ::build() * @covers ::getRequestForPath() */ public function testBuildWithUserPath() { $this->context->expects($this->once())->method('getPathInfo')->will($this->returnValue('/user/1/edit')); $this->setupStubPathProcessor(); $route_1 = new Route('/user/1'); $this->requestMatcher->expects($this->exactly(1))->method('matchRequest')->will($this->returnCallback(function (Request $request) use($route_1) { if ($request->getPathInfo() == '/user/1') { return array(RouteObjectInterface::ROUTE_NAME => 'user_page', RouteObjectInterface::ROUTE_OBJECT => $route_1, '_raw_variables' => new ParameterBag(array())); } })); $link_user = '******'; $link_front = '<a href="/">Home</a>'; $this->linkGenerator->expects($this->at(0))->method('generate')->with('Admin', 'user_page', array(), array('html' => TRUE))->will($this->returnValue($link_user)); $this->linkGenerator->expects($this->at(1))->method('generate')->with('Home', '<front>', array(), array())->will($this->returnValue($link_front)); $this->setupAccessManagerWithTrue(); $this->titleResolver->expects($this->once())->method('getTitle')->with($this->anything(), $route_1)->will($this->returnValue('Admin')); $links = $this->builder->build($this->getMock('Drupal\\Core\\Routing\\RouteMatchInterface')); $this->assertEquals(array(0 => '<a href="/">Home</a>', 1 => $link_user), $links); }