4 * This file is part of the Symfony CMF package.
6 * (c) 2011-2015 Symfony CMF
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Cmf\Component\Routing\NestedMatcher;
14 use Symfony\Component\HttpFoundation\Request;
15 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
16 use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
17 use Symfony\Cmf\Component\Routing\RouteProviderInterface;
20 * A more flexible approach to matching. The route collection to match against
21 * can be dynamically determined based on the request and users can inject
22 * their own filters or use a custom final matching strategy.
24 * The nested matcher splits matching into three configurable steps:
26 * 1) Get potential matches from a RouteProviderInterface
27 * 2) Apply any RouteFilterInterface to reduce the route collection
28 * 3) Have FinalMatcherInterface select the best match of the remaining routes
30 * @author Larry Garfield
31 * @author David Buchmann
33 class NestedMatcher implements RequestMatcherInterface
36 * The route provider responsible for the first-pass match.
38 * @var RouteProviderInterface
40 protected $routeProvider;
45 * @var FinalMatcherInterface
47 protected $finalMatcher;
50 * An array of RouteFilterInterface objects.
52 * @var RouteFilterInterface[]
54 protected $filters = array();
57 * Array of RouteFilterInterface objects, sorted.
59 * @var RouteFilterInterface[]
61 protected $sortedFilters = array();
64 * Constructs a new NestedMatcher.
66 * @param RouteProviderInterface $provider The route provider this matcher
68 * @param FinalMatcherInterface $final The Final Matcher to match the
71 public function __construct(
72 RouteProviderInterface $provider = null,
73 FinalMatcherInterface $final = null
75 if (null !== $provider) {
76 $this->setRouteProvider($provider);
78 if (null !== $final) {
79 $this->setFinalMatcher($final);
84 * Sets the route provider for the matching plan.
86 * @param RouteProviderInterface $provider A source of routes.
88 * @return NestedMatcher this object to have a fluent interface
90 public function setRouteProvider(RouteProviderInterface $provider)
92 $this->routeProvider = $provider;
98 * Adds a partial matcher to the matching plan.
100 * Partial matchers will be run in the order in which they are added.
102 * @param RouteFilterInterface $filter
103 * @param int $priority (optional) The priority of the
104 * filter. Higher number filters will
105 * be used first. Defaults to 0.
107 * @return NestedMatcher this object to have a fluent interface
109 public function addRouteFilter(RouteFilterInterface $filter, $priority = 0)
111 if (empty($this->filters[$priority])) {
112 $this->filters[$priority] = array();
115 $this->filters[$priority][] = $filter;
116 $this->sortedFilters = array();
122 * Sets the final matcher for the matching plan.
124 * @param FinalMatcherInterface $final The final matcher that will have to
125 * pick the route that will be used.
127 * @return NestedMatcher this object to have a fluent interface
129 public function setFinalMatcher(FinalMatcherInterface $final)
131 $this->finalMatcher = $final;
139 public function matchRequest(Request $request)
141 $collection = $this->routeProvider->getRouteCollectionForRequest($request);
142 if (!count($collection)) {
143 throw new ResourceNotFoundException();
146 // Route filters are expected to throw an exception themselves if they
147 // end up filtering the list down to 0.
148 foreach ($this->getRouteFilters() as $filter) {
149 $collection = $filter->filter($collection, $request);
152 $attributes = $this->finalMatcher->finalMatch($collection, $request);
158 * Sorts the filters and flattens them.
160 * @return RouteFilterInterface[] the filters ordered by priority
162 public function getRouteFilters()
164 if (empty($this->sortedFilters)) {
165 $this->sortedFilters = $this->sortFilters();
168 return $this->sortedFilters;
172 * Sort filters by priority.
174 * The highest priority number is the highest priority (reverse sorting).
176 * @return RouteFilterInterface[] the sorted filters
178 protected function sortFilters()
180 $sortedFilters = array();
181 krsort($this->filters);
183 foreach ($this->filters as $filters) {
184 $sortedFilters = array_merge($sortedFilters, $filters);
187 return $sortedFilters;