-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #98 from symfony-cmf/candidates
adding candidates subsystem
- Loading branch information
Showing
7 changed files
with
335 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony CMF package. | ||
* | ||
* (c) 2011-2014 Symfony CMF | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Cmf\Component\Routing\Candidates; | ||
|
||
use Doctrine\ODM\PHPCR\DocumentManager; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
/** | ||
* A straightforward strategy that splits the URL on "/". | ||
* | ||
* If locales is set, additionally generates candidates removing the locale if | ||
* it is one of the configured locales, for non-locale specific URLs. | ||
* | ||
* @author David Buchmann <[email protected]> | ||
*/ | ||
class Candidates implements CandidatesInterface | ||
{ | ||
/** | ||
* @var array | ||
*/ | ||
protected $locales; | ||
|
||
/** | ||
* A limit to apply to the number of candidates generated. | ||
* | ||
* This is to prevent abusive requests with a lot of "/". The limit is per | ||
* batch, that is if a locale matches you could get as many as 2 * $limit | ||
* candidates if the URL has that many slashes. | ||
* | ||
* @var int | ||
*/ | ||
protected $limit; | ||
|
||
/** | ||
* @param array $locales The locales to support. | ||
* @param int $limit A limit to apply to the candidates generated. | ||
*/ | ||
public function __construct(array $locales = array(), $limit = 20) | ||
{ | ||
$this->setLocales($locales); | ||
$this->limit = $limit; | ||
} | ||
|
||
/** | ||
* Set the locales to support by this strategy. | ||
* | ||
* @param array $locales The locales to support. | ||
*/ | ||
public function setLocales(array $locales) | ||
{ | ||
$this->locales = $locales; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* Always returns true. | ||
*/ | ||
public function isCandidate($name) | ||
{ | ||
return true; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* Does nothing. | ||
*/ | ||
public function restrictQuery($queryBuilder) | ||
{ | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function getCandidates(Request $request) | ||
{ | ||
$url = $request->getPathInfo(); | ||
$candidates = $this->getCandidatesFor($url); | ||
|
||
$locale = $this->determineLocale($url); | ||
if ($locale) { | ||
$candidates = array_unique(array_merge($candidates, $this->getCandidatesFor(substr($url, strlen($locale) + 1)))); | ||
} | ||
|
||
return $candidates; | ||
} | ||
|
||
/** | ||
* Determine the locale of this URL. | ||
* | ||
* @param string $url The url to determine the locale from. | ||
* | ||
* @return string|boolean The locale if $url starts with one of the allowed locales. | ||
*/ | ||
protected function determineLocale($url) | ||
{ | ||
if (!count($this->locales)) { | ||
return false; | ||
} | ||
|
||
$matches = array(); | ||
if (preg_match('#(' . implode('|', $this->locales) . ')(/|$)#', $url, $matches)) { | ||
return $matches[1]; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Handle a possible format extension and split the $url on "/". | ||
* | ||
* $prefix is prepended to every candidate generated. | ||
* | ||
* @param string $url The URL to split. | ||
* @param string $prefix A prefix to prepend to every pattern. | ||
* | ||
* @return array Paths that could represent routes that match $url and are | ||
* child of $prefix. | ||
*/ | ||
protected function getCandidatesFor($url, $prefix = '') | ||
{ | ||
$candidates = array(); | ||
if ('/' !== $url) { | ||
// handle format extension, like .html or .json | ||
if (preg_match('/(.+)\.[a-z]+$/i', $url, $matches)) { | ||
$candidates[] = $prefix . $url; | ||
$url = $matches[1]; | ||
} | ||
|
||
$part = $url; | ||
$count = 0; | ||
while (false !== ($pos = strrpos($part, '/'))) { | ||
if (++$count > $this->limit) { | ||
return $candidates; | ||
} | ||
$candidates[] = $prefix . $part; | ||
$part = substr($url, 0, $pos); | ||
} | ||
} | ||
|
||
$candidates[] = $prefix ?: '/'; | ||
|
||
return $candidates; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony CMF package. | ||
* | ||
* (c) 2011-2014 Symfony CMF | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Cmf\Component\Routing\Candidates; | ||
|
||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
/** | ||
* Candidates is a subsystem useful for the route provider. It separates the | ||
* logic for determining possible static prefixes from the route provider. | ||
* | ||
* @author David Buchmann <[email protected]> | ||
*/ | ||
interface CandidatesInterface | ||
{ | ||
/** | ||
* @param Request $request | ||
* | ||
* @return array a list of PHPCR-ODM ids | ||
*/ | ||
function getCandidates(Request $request); | ||
|
||
/** | ||
* Determine if $name is a valid candidate, e.g. in getRouteByName. | ||
* | ||
* @param string $name | ||
* | ||
* @return boolean | ||
*/ | ||
function isCandidate($name); | ||
|
||
/** | ||
* Provide a best effort query restriction to limit a query to only find | ||
* routes that are supported. | ||
* | ||
* @param object $queryBuilder A query builder suited for the storage backend. | ||
*/ | ||
function restrictQuery($queryBuilder); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony CMF package. | ||
* | ||
* (c) 2011-2013 Symfony CMF | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Cmf\Component\Routing\Tests\Candidates; | ||
|
||
use Doctrine\Common\Collections\ArrayCollection; | ||
use Symfony\Cmf\Component\Routing\Candidates\Candidates; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\Routing\Route; | ||
use Symfony\Component\Routing\RouteCollection; | ||
|
||
class CandidatesTest extends \PHPUnit_Framework_Testcase | ||
{ | ||
/** | ||
* Everything is a candidate | ||
*/ | ||
public function testIsCandidate() | ||
{ | ||
$candidates = new Candidates(); | ||
$this->assertTrue($candidates->isCandidate('/routes')); | ||
$this->assertTrue($candidates->isCandidate('/routes/my/path')); | ||
} | ||
|
||
/** | ||
* Nothing should be called on the query builder | ||
*/ | ||
public function testRestrictQuery() | ||
{ | ||
$candidates = new Candidates(); | ||
$candidates->restrictQuery(null); | ||
} | ||
|
||
public function testGetCandidates() | ||
{ | ||
$request = Request::create('/my/path.html'); | ||
|
||
$candidates = new Candidates(); | ||
$paths = $candidates->getCandidates($request); | ||
|
||
$this->assertEquals( | ||
array( | ||
'/my/path.html', | ||
'/my/path', | ||
'/my', | ||
'/', | ||
), | ||
$paths | ||
); | ||
} | ||
|
||
public function testGetCandidatesLocales() | ||
{ | ||
$candidates = new Candidates(array('de', 'fr')); | ||
|
||
$request = Request::create('/fr/path.html'); | ||
$paths = $candidates->getCandidates($request); | ||
|
||
$this->assertEquals( | ||
array( | ||
'/fr/path.html', | ||
'/fr/path', | ||
'/fr', | ||
'/', | ||
'/path.html', | ||
'/path' | ||
), | ||
$paths | ||
); | ||
|
||
$request = Request::create('/it/path.html'); | ||
$paths = $candidates->getCandidates($request); | ||
|
||
$this->assertEquals( | ||
array( | ||
'/it/path.html', | ||
'/it/path', | ||
'/it', | ||
'/', | ||
), | ||
$paths | ||
); | ||
} | ||
|
||
public function testGetCandidatesLimit() | ||
{ | ||
$candidates = new Candidates(array(), 1); | ||
|
||
$request = Request::create('/my/path/is/deep.html'); | ||
|
||
$paths = $candidates->getCandidates($request); | ||
|
||
$this->assertEquals( | ||
array( | ||
'/my/path/is/deep.html', | ||
'/my/path/is/deep', | ||
), | ||
$paths | ||
); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.