Chris@0: Chris@0: */ Chris@0: class Candidates implements CandidatesInterface Chris@0: { Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: protected $locales; Chris@0: Chris@0: /** Chris@0: * A limit to apply to the number of candidates generated. Chris@0: * Chris@0: * This is to prevent abusive requests with a lot of "/". The limit is per Chris@0: * batch, that is if a locale matches you could get as many as 2 * $limit Chris@0: * candidates if the URL has that many slashes. Chris@0: * Chris@0: * @var int Chris@0: */ Chris@0: protected $limit; Chris@0: Chris@0: /** Chris@0: * @param array $locales The locales to support. Chris@0: * @param int $limit A limit to apply to the candidates generated. Chris@0: */ Chris@0: public function __construct(array $locales = array(), $limit = 20) Chris@0: { Chris@0: $this->setLocales($locales); Chris@0: $this->limit = $limit; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the locales to support by this strategy. Chris@0: * Chris@0: * @param array $locales The locales to support. Chris@0: */ Chris@0: public function setLocales(array $locales) Chris@0: { Chris@0: $this->locales = $locales; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: * Chris@0: * Always returns true. Chris@0: */ Chris@0: public function isCandidate($name) Chris@0: { Chris@0: return true; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: * Chris@0: * Does nothing. Chris@0: */ Chris@0: public function restrictQuery($queryBuilder) Chris@0: { Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getCandidates(Request $request) Chris@0: { Chris@0: $url = $request->getPathInfo(); Chris@0: $candidates = $this->getCandidatesFor($url); Chris@0: Chris@0: $locale = $this->determineLocale($url); Chris@0: if ($locale) { Chris@0: $candidates = array_unique(array_merge($candidates, $this->getCandidatesFor(substr($url, strlen($locale) + 1)))); Chris@0: } Chris@0: Chris@0: return $candidates; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Determine the locale of this URL. Chris@0: * Chris@0: * @param string $url The url to determine the locale from. Chris@0: * Chris@0: * @return string|bool The locale if $url starts with one of the allowed locales. Chris@0: */ Chris@0: protected function determineLocale($url) Chris@0: { Chris@0: if (!count($this->locales)) { Chris@0: return false; Chris@0: } Chris@0: Chris@0: $matches = array(); Chris@0: if (preg_match('#^/('.implode('|', $this->locales).')(/|$)#', $url, $matches)) { Chris@0: return $matches[1]; Chris@0: } Chris@0: Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Handle a possible format extension and split the $url on "/". Chris@0: * Chris@0: * $prefix is prepended to every candidate generated. Chris@0: * Chris@0: * @param string $url The URL to split. Chris@0: * @param string $prefix A prefix to prepend to every pattern. Chris@0: * Chris@0: * @return array Paths that could represent routes that match $url and are Chris@0: * child of $prefix. Chris@0: */ Chris@0: protected function getCandidatesFor($url, $prefix = '') Chris@0: { Chris@0: $candidates = array(); Chris@0: if ('/' !== $url) { Chris@0: // handle format extension, like .html or .json Chris@0: if (preg_match('/(.+)\.[a-z]+$/i', $url, $matches)) { Chris@0: $candidates[] = $prefix.$url; Chris@0: $url = $matches[1]; Chris@0: } Chris@0: Chris@0: $part = $url; Chris@0: $count = 0; Chris@0: while (false !== ($pos = strrpos($part, '/'))) { Chris@0: if (++$count > $this->limit) { Chris@0: return $candidates; Chris@0: } Chris@0: $candidates[] = $prefix.$part; Chris@0: $part = substr($url, 0, $pos); Chris@0: } Chris@0: } Chris@0: Chris@0: $candidates[] = $prefix ?: '/'; Chris@0: Chris@0: return $candidates; Chris@0: } Chris@0: }