merged branch fabpot/routing-options (PR #6738)

This PR was merged into the master branch.

Commits
-------

9fc7def added the UPGRADE file for Symfony 3.0
e84cad2 [Routing] updated CHANGELOG
65eca8a [Routing] added new schemes and methods options to the annotation loader
5082994 [Routing] renamed pattern to path
b357caf [Routing] renamed hostname pattern to just hostname
e803f46 made schemes and methods available in XmlFileLoader
d374e70 made schemes and methods available in YamlFileLoader
2834e7e added scheme and method setter in RouteCollection
10183de make scheme and method requirements first-class citizen in Route

Discussion
----------

Routing options

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #5989, #5990, #6049
| License       | MIT

In #5989, it has unanimously been decided to renamed `hostname_pattern` to `hostname` and `pattern` to `path`. That makes a lot of sense and I would like to do the renaming now as `hostname_pattern` is new in Symfony 2.2, so I'd like to avoid breaking BC just after the release. As we are modifying the route options, I've also included changes introduced by @Tobion in #6049 which were discussed in #5990.

As everything is BC, I think it's wise to include that in 2.2. What do you think?

---------------------------------------------------------------------------

by Tobion at 2013-01-14T18:25:53Z

I agree it should be done in 2.2. Thanks for working on it.

---------------------------------------------------------------------------

by vicb at 2013-01-14T23:11:12Z

@fabpot "Everything is BC" until it breaks BC in 3.0, that's why I'd like to see [deprecations in PR summary](https://github.com/symfony/symfony-docs/pull/2116) what do you think ?

---------------------------------------------------------------------------

by vicb at 2013-01-14T23:16:40Z

it would also be great to update the CHANGELOG with deprecations (it could also help people answering your question)

---------------------------------------------------------------------------

by fabpot at 2013-01-15T07:07:03Z

@vicb: I've just updated the CHANGELOG and created the UPGRADE file for 3.0.

---------------------------------------------------------------------------

by vicb at 2013-01-15T07:15:32Z

@fabpot thanks.
This commit is contained in:
Fabien Potencier 2013-01-15 17:25:04 +01:00
commit 89f9b24575
28 changed files with 569 additions and 154 deletions

47
UPGRADE-3.0.md Normal file
View File

@ -0,0 +1,47 @@
UPGRADE FROM 2.x to 3.0
=======================
### Routing
* Some route settings have been renamed:
* The `pattern` setting for a route has been deprecated in favor of `path`
* The `_scheme` and `_method` requirements have been moved to the `schemes` and `methods` settings
Before:
```
article_edit:
pattern: /article/{id}
requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' }
<route id="article_edit" pattern="/article/{id}">
<requirement key="_method">POST|PUT</requirement>
<requirement key="_scheme">https</requirement>
<requirement key="id">\d+</requirement>
</route>
$route = new Route();
$route->setPattern('/article/{id}');
$route->setRequirement('_method', 'POST|PUT');
$route->setRequirement('_scheme', 'https');
```
After:
```
article_edit:
path: /article/{id}
methods: [POST, PUT]
schemes: https
requirements: { 'id': '\d+' }
<route id="article_edit" pattern="/article/{id}" methods="POST PUT" schemes="https">
<requirement key="id">\d+</requirement>
</route>
$route = new Route();
$route->setPath('/article/{id}');
$route->setMethods(array('POST', 'PUT'));
$route->setSchemes('https');
```

View File

@ -92,7 +92,7 @@ EOF
? implode(', ', $requirements['_method']) : $requirements['_method']
)
: 'ANY';
$hostname = '' !== $route->getHostnamePattern() ? $route->getHostnamePattern() : 'ANY';
$hostname = '' !== $route->getHostname() ? $route->getHostname() : 'ANY';
$maxName = max($maxName, strlen($name));
$maxMethod = max($maxMethod, strlen($method));
$maxHostname = max($maxHostname, strlen($hostname));
@ -109,7 +109,7 @@ EOF
? implode(', ', $requirements['_method']) : $requirements['_method']
)
: 'ANY';
$hostname = '' !== $route->getHostnamePattern() ? $route->getHostnamePattern() : 'ANY';
$hostname = '' !== $route->getHostname() ? $route->getHostname() : 'ANY';
$output->writeln(sprintf($format, $name, $method, $hostname, $route->getPattern()));
}
}
@ -124,14 +124,14 @@ EOF
throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
}
$hostname = '' !== $route->getHostnamePattern() ? $route->getHostnamePattern() : 'ANY';
$hostname = '' !== $route->getHostname() ? $route->getHostname() : 'ANY';
$output->writeln($this->getHelper('formatter')->formatSection('router', sprintf('Route "%s"', $name)));
$output->writeln(sprintf('<comment>Name</comment> %s', $name));
$output->writeln(sprintf('<comment>Pattern</comment> %s', $route->getPattern()));
$output->writeln(sprintf('<comment>HostnamePattern</comment> %s', $hostname));
$output->writeln(sprintf('<comment>Class</comment> %s', get_class($route)));
$output->writeln(sprintf('<comment>Name</comment> %s', $name));
$output->writeln(sprintf('<comment>Pattern</comment> %s', $route->getPattern()));
$output->writeln(sprintf('<comment>Hostname</comment> %s', $hostname));
$output->writeln(sprintf('<comment>Class</comment> %s', get_class($route)));
$defaults = '';
$d = $route->getDefaults();

View File

@ -78,7 +78,7 @@ class Router extends BaseRouter implements WarmableInterface
* - the route defaults,
* - the route requirements,
* - the route pattern.
* - the route hostnamePattern.
* - the route hostname.
*
* @param RouteCollection $collection
*/
@ -94,7 +94,7 @@ class Router extends BaseRouter implements WarmableInterface
}
$route->setPattern($this->resolve($route->getPattern()));
$route->setHostnamePattern($this->resolve($route->getHostnamePattern()));
$route->setHostname($this->resolve($route->getHostname()));
}
}

View File

@ -117,12 +117,12 @@ class RoutingTest extends \PHPUnit_Framework_TestCase
);
}
public function testHostnamePatternPlaceholders()
public function testHostnamePlaceholders()
{
$routes = new RouteCollection();
$route = new Route('foo');
$route->setHostnamePattern('/before/%parameter.foo%/after/%%unescaped%%');
$route->setHostname('/before/%parameter.foo%/after/%%unescaped%%');
$routes->add('foo', $route);
@ -136,7 +136,7 @@ class RoutingTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(
'/before/foo/after/%unescaped%',
$route->getHostnamePattern()
$route->getHostname()
);
}

View File

@ -20,12 +20,14 @@ namespace Symfony\Component\Routing\Annotation;
*/
class Route
{
private $pattern;
private $path;
private $name;
private $requirements;
private $options;
private $defaults;
private $hostnamePattern;
private $hostname;
private $methods;
private $schemes;
/**
* Constructor.
@ -39,9 +41,11 @@ class Route
$this->requirements = array();
$this->options = array();
$this->defaults = array();
$this->methods = array();
$this->schemes = array();
if (isset($data['value'])) {
$data['pattern'] = $data['value'];
$data['path'] = $data['value'];
unset($data['value']);
}
@ -54,24 +58,40 @@ class Route
}
}
/**
* @deprecated Deprecated in 2.2, to be removed in 3.0. Use setPath instead.
*/
public function setPattern($pattern)
{
$this->pattern = $pattern;
$this->path = $pattern;
}
/**
* @deprecated Deprecated in 2.2, to be removed in 3.0. Use getPath instead.
*/
public function getPattern()
{
return $this->pattern;
return $this->path;
}
public function setHostnamePattern($pattern)
public function setPath($path)
{
$this->hostnamePattern = $pattern;
$this->path = $path;
}
public function getHostnamePattern()
public function getPath()
{
return $this->hostnamePattern;
return $this->path;
}
public function setHostname($pattern)
{
$this->hostname = $pattern;
}
public function getHostname()
{
return $this->hostname;
}
public function setName($name)
@ -113,4 +133,24 @@ class Route
{
return $this->defaults;
}
public function setSchemes($schemes)
{
$this->schemes = is_array($schemes) ? $schemes : array($schemes);
}
public function getSchemes()
{
return $this->schemes;
}
public function setMethods($methods)
{
$this->methods = is_array($methods) ? $methods : array($methods);
}
public function getMethods()
{
return $this->methods;
}
}

View File

@ -4,6 +4,49 @@ CHANGELOG
2.2.0
-----
* [DEPRECATION] Several route settings have been renamed (the old ones will be removed in 3.0):
* The `pattern` setting for a route has been deprecated in favor of `path`
* The `_scheme` and `_method` requirements have been moved to the `schemes` and `methods` settings
Before:
```
article_edit:
pattern: /article/{id}
requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' }
<route id="article_edit" pattern="/article/{id}">
<requirement key="_method">POST|PUT</requirement>
<requirement key="_scheme">https</requirement>
<requirement key="id">\d+</requirement>
</route>
$route = new Route();
$route->setPattern('/article/{id}');
$route->setRequirement('_method', 'POST|PUT');
$route->setRequirement('_scheme', 'https');
```
After:
```
article_edit:
path: /article/{id}
methods: [POST, PUT]
schemes: https
requirements: { 'id': '\d+' }
<route id="article_edit" pattern="/article/{id}" methods="POST PUT" schemes="https">
<requirement key="id">\d+</requirement>
</route>
$route = new Route();
$route->setPath('/article/{id}');
$route->setMethods(array('POST', 'PUT'));
$route->setSchemes('https');
```
* [BC BREAK] RouteCollection does not behave like a tree structure anymore but as
a flat array of Routes. So when using PHP to build the RouteCollection, you must
make sure to add routes to the sub-collection before adding it to the parent

View File

@ -28,9 +28,10 @@ use Symfony\Component\Config\Loader\LoaderResolverInterface;
* The @Route annotation can be set on the class (for global parameters),
* and on each method.
*
* The @Route annotation main value is the route pattern. The annotation also
* recognizes three parameters: requirements, options, and name. The name parameter
* is mandatory. Here is an example of how you should be able to use it:
* The @Route annotation main value is the route path. The annotation also
* recognizes several parameters: requirements, options, defaults, schemes,
* methods, hostname, and name. The name parameter is mandatory.
* Here is an example of how you should be able to use it:
*
* /**
* * @Route("/Blog")
@ -108,11 +109,13 @@ abstract class AnnotationClassLoader implements LoaderInterface
}
$globals = array(
'pattern' => '',
'requirements' => array(),
'options' => array(),
'defaults' => array(),
'hostname_pattern' => '',
'path' => '',
'requirements' => array(),
'options' => array(),
'defaults' => array(),
'schemes' => array(),
'methods' => array(),
'hostname' => '',
);
$class = new \ReflectionClass($class);
@ -121,8 +124,11 @@ abstract class AnnotationClassLoader implements LoaderInterface
}
if ($annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) {
if (null !== $annot->getPattern()) {
$globals['pattern'] = $annot->getPattern();
// for BC reasons
if (null !== $annot->getPath()) {
$globals['path'] = $annot->getPath();
} elseif (null !== $annot->getPattern()) {
$globals['path'] = $annot->getPattern();
}
if (null !== $annot->getRequirements()) {
@ -137,8 +143,16 @@ abstract class AnnotationClassLoader implements LoaderInterface
$globals['defaults'] = $annot->getDefaults();
}
if (null !== $annot->getHostnamePattern()) {
$globals['hostname_pattern'] = $annot->getHostnamePattern();
if (null !== $annot->getSchemes()) {
$globals['schemes'] = $annot->getSchemes();
}
if (null !== $annot->getMethods()) {
$globals['methods'] = $annot->getMethods();
}
if (null !== $annot->getHostname()) {
$globals['hostname'] = $annot->getHostname();
}
}
@ -172,13 +186,15 @@ abstract class AnnotationClassLoader implements LoaderInterface
}
$requirements = array_replace($globals['requirements'], $annot->getRequirements());
$options = array_replace($globals['options'], $annot->getOptions());
$schemes = array_replace($globals['schemes'], $annot->getSchemes());
$methods = array_replace($globals['methods'], $annot->getMethods());
$hostnamePattern = $annot->getHostnamePattern();
if (null === $hostnamePattern) {
$hostnamePattern = $globals['hostname_pattern'];
$hostname = $annot->getHostname();
if (null === $hostname) {
$hostname = $globals['hostname'];
}
$route = new Route($globals['pattern'].$annot->getPattern(), $defaults, $requirements, $options, $hostnamePattern);
$route = new Route($globals['path'].$annot->getPath(), $defaults, $requirements, $options, $hostname, $schemes, $methods);
$this->configureRoute($route, $class, $method, $annot);

View File

@ -113,13 +113,25 @@ class XmlFileLoader extends FileLoader
*/
protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
{
if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('pattern')) {
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "pattern" attribute.', $path));
if ('' === ($id = $node->getAttribute('id')) || (!$node->hasAttribute('pattern') && !$node->hasAttribute('path'))) {
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" attribute.', $path));
}
if ($node->hasAttribute('pattern')) {
if ($node->hasAttribute('path')) {
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" cannot define both a "path" and a "pattern" attribute. Use only "path".', $path));
}
$node->setAttribute('path', $node->getAttribute('pattern'));
$node->removeAttribute('pattern');
}
$schemes = array_filter(explode(' ', $node->getAttribute('schemes')));
$methods = array_filter(explode(' ', $node->getAttribute('methods')));
list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
$route = new Route($node->getAttribute('pattern'), $defaults, $requirements, $options, $node->getAttribute('hostname-pattern'));
$route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('hostname'), $schemes, $methods);
$collection->add($id, $route);
}
@ -141,7 +153,9 @@ class XmlFileLoader extends FileLoader
$type = $node->getAttribute('type');
$prefix = $node->getAttribute('prefix');
$hostnamePattern = $node->hasAttribute('hostname-pattern') ? $node->getAttribute('hostname-pattern') : null;
$hostname = $node->hasAttribute('hostname') ? $node->getAttribute('hostname') : null;
$schemes = $node->hasAttribute('schemes') ? array_filter(explode(' ', $node->getAttribute('schemes'))) : null;
$methods = $node->hasAttribute('methods') ? array_filter(explode(' ', $node->getAttribute('methods'))) : null;
list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
@ -150,8 +164,14 @@ class XmlFileLoader extends FileLoader
$subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
/* @var $subCollection RouteCollection */
$subCollection->addPrefix($prefix);
if (null !== $hostnamePattern) {
$subCollection->setHostnamePattern($hostnamePattern);
if (null !== $hostname) {
$subCollection->setHostname($hostname);
}
if (null !== $schemes) {
$subCollection->setSchemes($schemes);
}
if (null !== $methods) {
$subCollection->setMethods($methods);
}
$subCollection->addDefaults($defaults);
$subCollection->addRequirements($requirements);

View File

@ -28,7 +28,7 @@ use Symfony\Component\Config\Loader\FileLoader;
class YamlFileLoader extends FileLoader
{
private static $availableKeys = array(
'resource', 'type', 'prefix', 'pattern', 'hostname_pattern', 'defaults', 'requirements', 'options',
'resource', 'type', 'prefix', 'pattern', 'path', 'hostname', 'schemes', 'methods', 'defaults', 'requirements', 'options',
);
/**
@ -63,6 +63,15 @@ class YamlFileLoader extends FileLoader
}
foreach ($config as $name => $config) {
if (isset($config['pattern'])) {
if (isset($config['path'])) {
throw new \InvalidArgumentException(sprintf('The file "%s" cannot define both a "path" and a "pattern" attribute. Use only "path".', $path));
}
$config['path'] = $config['pattern'];
unset($config['pattern']);
}
$this->validate($config, $name, $path);
if (isset($config['resource'])) {
@ -98,9 +107,11 @@ class YamlFileLoader extends FileLoader
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
$options = isset($config['options']) ? $config['options'] : array();
$hostnamePattern = isset($config['hostname_pattern']) ? $config['hostname_pattern'] : null;
$hostname = isset($config['hostname']) ? $config['hostname'] : '';
$schemes = isset($config['schemes']) ? $config['schemes'] : array();
$methods = isset($config['methods']) ? $config['methods'] : array();
$route = new Route($config['pattern'], $defaults, $requirements, $options, $hostnamePattern);
$route = new Route($config['path'], $defaults, $requirements, $options, $hostname, $schemes, $methods);
$collection->add($name, $route);
}
@ -120,15 +131,23 @@ class YamlFileLoader extends FileLoader
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
$options = isset($config['options']) ? $config['options'] : array();
$hostnamePattern = isset($config['hostname_pattern']) ? $config['hostname_pattern'] : null;
$hostname = isset($config['hostname']) ? $config['hostname'] : null;
$schemes = isset($config['schemes']) ? $config['schemes'] : null;
$methods = isset($config['methods']) ? $config['methods'] : null;
$this->setCurrentDir(dirname($path));
$subCollection = $this->import($config['resource'], $type, false, $file);
/* @var $subCollection RouteCollection */
$subCollection->addPrefix($prefix);
if (null !== $hostnamePattern) {
$subCollection->setHostnamePattern($hostnamePattern);
if (null !== $hostname) {
$subCollection->setHostname($hostname);
}
if (null !== $schemes) {
$subCollection->setSchemes($schemes);
}
if (null !== $methods) {
$subCollection->setMethods($methods);
}
$subCollection->addDefaults($defaults);
$subCollection->addRequirements($requirements);
@ -158,9 +177,9 @@ class YamlFileLoader extends FileLoader
$path, $name, implode('", "', $extraKeys), implode('", "', self::$availableKeys)
));
}
if (isset($config['resource']) && isset($config['pattern'])) {
if (isset($config['resource']) && isset($config['path'])) {
throw new \InvalidArgumentException(sprintf(
'The routing file "%s" must not specify both the "resource" key and the "pattern" key for "%s". Choose between an import and a route definition.',
'The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". Choose between an import and a route definition.',
$path, $name
));
}
@ -170,9 +189,9 @@ class YamlFileLoader extends FileLoader
$name, $path
));
}
if (!isset($config['resource']) && !isset($config['pattern'])) {
if (!isset($config['resource']) && !isset($config['path'])) {
throw new \InvalidArgumentException(sprintf(
'You must define a "pattern" for the route "%s" in file "%s".',
'You must define a "path" for the route "%s" in file "%s".',
$name, $path
));
}

View File

@ -17,6 +17,16 @@
<xsd:element name="routes" type="routes" />
<xsd:simpleType name="word">
<xsd:restriction base="xsd:string">
<xsd:pattern value="([a-zA-Z]){3,}"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="stringlist">
<xsd:list itemType="word"/>
</xsd:simpleType>
<xsd:complexType name="routes">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="import" type="import" />
@ -36,8 +46,11 @@
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="id" type="xsd:string" use="required" />
<xsd:attribute name="pattern" type="xsd:string" use="required" />
<xsd:attribute name="hostname-pattern" type="xsd:string" />
<xsd:attribute name="path" type="xsd:string" />
<xsd:attribute name="pattern" type="xsd:string" />
<xsd:attribute name="hostname" type="xsd:string" />
<xsd:attribute name="schemes" type="stringlist" />
<xsd:attribute name="methods" type="stringlist" />
</xsd:complexType>
<xsd:complexType name="import">
@ -46,7 +59,9 @@
<xsd:attribute name="resource" type="xsd:string" use="required" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="prefix" type="xsd:string" />
<xsd:attribute name="hostname-pattern" type="xsd:string" />
<xsd:attribute name="hostname" type="xsd:string" />
<xsd:attribute name="schemes" type="stringlist" />
<xsd:attribute name="methods" type="stringlist" />
</xsd:complexType>
<xsd:complexType name="element">

View File

@ -48,16 +48,16 @@ class TraceableUrlMatcher extends UrlMatcher
if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
// does it match without any requirements?
$r = new Route($route->getPattern(), $route->getDefaults(), array(), $route->getOptions());
$r = new Route($route->getPath(), $route->getDefaults(), array(), $route->getOptions());
$cr = $r->compile();
if (!preg_match($cr->getRegex(), $pathinfo)) {
$this->addTrace(sprintf('Pattern "%s" does not match', $route->getPattern()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
$this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
continue;
}
foreach ($route->getRequirements() as $n => $regex) {
$r = new Route($route->getPattern(), $route->getDefaults(), array($n => $regex), $route->getOptions());
$r = new Route($route->getPath(), $route->getDefaults(), array($n => $regex), $route->getOptions());
$cr = $r->compile();
if (in_array($n, $cr->getVariables()) && !preg_match($cr->getRegex(), $pathinfo)) {
@ -104,10 +104,10 @@ class TraceableUrlMatcher extends UrlMatcher
private function addTrace($log, $level = self::ROUTE_DOES_NOT_MATCH, $name = null, $route = null)
{
$this->traces[] = array(
'log' => $log,
'name' => $name,
'level' => $level,
'pattern' => null !== $route ? $route->getPattern() : null,
'log' => $log,
'name' => $name,
'level' => $level,
'path' => null !== $route ? $route->getPath() : null,
);
}
}

View File

@ -15,6 +15,7 @@ namespace Symfony\Component\Routing;
* A Route describes a route and its parameters.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Tobias Schultze <http://tobion.de>
*
* @api
*/
@ -23,14 +24,24 @@ class Route implements \Serializable
/**
* @var string
*/
private $pattern = '/';
private $path = '/';
/**
* @var string
*/
private $hostnamePattern = '';
private $hostname = '';
/**
/**
* @var array
*/
private $schemes = array();
/**
* @var array
*/
private $methods = array();
/**
* @var array
*/
private $defaults = array();
@ -57,52 +68,68 @@ class Route implements \Serializable
*
* * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
*
* @param string $pattern The path pattern to match
* @param array $defaults An array of default parameter values
* @param array $requirements An array of requirements for parameters (regexes)
* @param array $options An array of options
* @param string $hostnamePattern The hostname pattern to match
* @param string $path The path pattern to match
* @param array $defaults An array of default parameter values
* @param array $requirements An array of requirements for parameters (regexes)
* @param array $options An array of options
* @param string $hostname The hostname pattern to match
* @param string|array $schemes A required URI scheme or an array of restricted schemes
* @param string|array $methods A required HTTP method or an array of restricted methods
*
* @api
*/
public function __construct($pattern, array $defaults = array(), array $requirements = array(), array $options = array(), $hostnamePattern = '')
public function __construct($path, array $defaults = array(), array $requirements = array(), array $options = array(), $hostname = '', $schemes = array(), $methods = array())
{
$this->setPattern($pattern);
$this->setPath($path);
$this->setDefaults($defaults);
$this->setRequirements($requirements);
$this->setOptions($options);
$this->setHostnamePattern($hostnamePattern);
$this->setHostname($hostname);
// The conditions make sure that an initial empty $schemes/$methods does not override the corresponding requirement.
// They can be removed when the BC layer is removed.
if ($schemes) {
$this->setSchemes($schemes);
}
if ($methods) {
$this->setMethods($methods);
}
}
public function serialize()
{
return serialize(array(
'pattern' => $this->pattern,
'hostnamePattern' => $this->hostnamePattern,
'defaults' => $this->defaults,
'path' => $this->path,
'hostname' => $this->hostname,
'defaults' => $this->defaults,
'requirements' => $this->requirements,
'options' => $this->options,
'options' => $this->options,
'schemes' => $this->schemes,
'methods' => $this->methods,
));
}
public function unserialize($data)
{
$data = unserialize($data);
$this->pattern = $data['pattern'];
$this->hostnamePattern = $data['hostnamePattern'];
$this->path = $data['path'];
$this->hostname = $data['hostname'];
$this->defaults = $data['defaults'];
$this->requirements = $data['requirements'];
$this->options = $data['options'];
$this->schemes = $data['schemes'];
$this->methods = $data['methods'];
}
/**
* Returns the pattern for the path.
*
* @return string The pattern
*
* @deprecated Deprecated in 2.2, to be removed in 3.0. Use setPath instead.
*/
public function getPattern()
{
return $this->pattern;
return $this->path;
}
/**
@ -113,12 +140,38 @@ class Route implements \Serializable
* @param string $pattern The pattern
*
* @return Route The current Route instance
*
* @deprecated Deprecated in 2.2, to be removed in 3.0. Use setPath instead.
*/
public function setPattern($pattern)
{
return $this->setPath($pattern);
}
/**
* Returns the pattern for the path.
*
* @return string The path pattern
*/
public function getPath()
{
return $this->path;
}
/**
* Sets the pattern for the path.
*
* This method implements a fluent interface.
*
* @param string $path The path pattern
*
* @return Route The current Route instance
*/
public function setPath($path)
{
// A pattern must start with a slash and must not have multiple slashes at the beginning because the
// generated path for this route would be confused with a network path, e.g. '//domain.com/path'.
$this->pattern = '/' . ltrim(trim($pattern), '/');
$this->path = '/' . ltrim(trim($path), '/');
$this->compiled = null;
return $this;
@ -129,9 +182,9 @@ class Route implements \Serializable
*
* @return string The pattern
*/
public function getHostnamePattern()
public function getHostname()
{
return $this->hostnamePattern;
return $this->hostname;
}
/**
@ -141,9 +194,83 @@ class Route implements \Serializable
*
* @return Route The current Route instance
*/
public function setHostnamePattern($pattern)
public function setHostname($pattern)
{
$this->hostnamePattern = (string) $pattern;
$this->hostname = (string) $pattern;
$this->compiled = null;
return $this;
}
/**
* Returns the lowercased schemes this route is restricted to.
* So an empty array means that any scheme is allowed.
*
* @return array The schemes
*/
public function getSchemes()
{
return $this->schemes;
}
/**
* Sets the schemes (e.g. 'https') this route is restricted to.
* So an empty array means that any scheme is allowed.
*
* This method implements a fluent interface.
*
* @param string|array $schemes The scheme or an array of schemes
*
* @return Route The current Route instance
*/
public function setSchemes($schemes)
{
$this->schemes = array_map('strtolower', (array) $schemes);
// this is to keep BC and will be removed in a future version
if ($this->schemes) {
$this->requirements['_scheme'] = implode('|', $this->schemes);
} else {
unset($this->requirements['_scheme']);
}
$this->compiled = null;
return $this;
}
/**
* Returns the uppercased HTTP methods this route is restricted to.
* So an empty array means that any method is allowed.
*
* @return array The schemes
*/
public function getMethods()
{
return $this->methods;
}
/**
* Sets the HTTP methods (e.g. 'POST') this route is restricted to.
* So an empty array means that any method is allowed.
*
* This method implements a fluent interface.
*
* @param string|array $methods The method or an array of methods
*
* @return Route The current Route instance
*/
public function setMethods($methods)
{
$this->methods = array_map('strtoupper', (array) $methods);
// this is to keep BC and will be removed in a future version
if ($this->methods) {
$this->requirements['_method'] = implode('|', $this->methods);
} else {
unset($this->requirements['_method']);
}
$this->compiled = null;
return $this;
@ -453,6 +580,13 @@ class Route implements \Serializable
throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key));
}
// this is to keep BC and will be removed in a future version
if ('_scheme' === $key) {
$this->setSchemes(explode('|', $regex));
} elseif ('_method' === $key) {
$this->setMethods(explode('|', $regex));
}
return $regex;
}
}

View File

@ -235,7 +235,7 @@ class RouteCollection implements \IteratorAggregate, \Countable
$options = func_num_args() > 3 ? func_get_arg(3) : array();
foreach ($this->routes as $route) {
$route->setPattern('/' . $prefix . $route->getPattern());
$route->setPath('/' . $prefix . $route->getPath());
$route->addDefaults($defaults);
$route->addRequirements($requirements);
$route->addOptions($options);
@ -261,10 +261,10 @@ class RouteCollection implements \IteratorAggregate, \Countable
* @param array $defaults An array of default values
* @param array $requirements An array of requirements
*/
public function setHostnamePattern($pattern, array $defaults = array(), array $requirements = array())
public function setHostname($pattern, array $defaults = array(), array $requirements = array())
{
foreach ($this->routes as $route) {
$route->setHostnamePattern($pattern);
$route->setHostname($pattern);
$route->addDefaults($defaults);
$route->addRequirements($requirements);
}
@ -318,6 +318,30 @@ class RouteCollection implements \IteratorAggregate, \Countable
}
}
/**
* Sets the schemes (e.g. 'https') all child routes are restricted to.
*
* @param string|array $schemes The scheme or an array of schemes
*/
public function setSchemes($schemes)
{
foreach ($this->routes as $route) {
$route->setSchemes($schemes);
}
}
/**
* Sets the HTTP methods (e.g. 'POST') all child routes are restricted to.
*
* @param string|array $methods The method or an array of methods
*/
public function setMethods($methods)
{
foreach ($this->routes as $route) {
$route->setMethods($methods);
}
}
/**
* Returns an array of resources loaded to build this collection.
*

View File

@ -46,8 +46,8 @@ class RouteCompiler implements RouteCompilerInterface
$hostnameRegex = null;
$hostnameTokens = array();
if ('' !== $hostnamePattern = $route->getHostnamePattern()) {
$result = self::compilePattern($route, $hostnamePattern, true);
if ('' !== $hostname = $route->getHostname()) {
$result = self::compilePattern($route, $hostname, true);
$hostnameVariables = $result['variables'];
$variables = array_merge($variables, $hostnameVariables);
@ -56,9 +56,9 @@ class RouteCompiler implements RouteCompilerInterface
$hostnameRegex = $result['regex'];
}
$pattern = $route->getPattern();
$path = $route->getPath();
$result = self::compilePattern($route, $pattern, false);
$result = self::compilePattern($route, $path, false);
$staticPrefix = $result['staticPrefix'];

View File

@ -36,11 +36,14 @@ class RouteTest extends \PHPUnit_Framework_TestCase
{
return array(
array('value', '/Blog', 'getPattern'),
array('value', '/Blog', 'getPath'),
array('requirements', array('_method' => 'GET'), 'getRequirements'),
array('options', array('compiler_class' => 'RouteCompiler'), 'getOptions'),
array('name', 'blog_index', 'getName'),
array('defaults', array('_controller' => 'MyBlogBundle:Blog:index'), 'getDefaults'),
array('hostname_pattern', array('{locale}.example.com'), 'getHostnamePattern')
array('schemes', array('https'), 'getSchemes'),
array('methods', array('GET', 'POST'), 'getMethods'),
array('hostname', array('{locale}.example.com'), 'getHostname')
);
}
}

View File

@ -4,7 +4,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<r:route id="blog_show" pattern="/blog/{slug}" hostname-pattern="{_locale}.example.com">
<r:route id="blog_show" pattern="/blog/{slug}" hostname="{_locale}.example.com">
<r:default key="_controller">MyBundle:Blog:show</r:default>
<requirement xmlns="http://symfony.com/schema/routing" key="slug">\w+</requirement>
<r2:requirement xmlns:r2="http://symfony.com/schema/routing" key="_locale">en|fr|de</r2:requirement>

View File

@ -4,7 +4,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="blog_show" pattern="/blog/{slug}" hostname-pattern="{locale}.example.com">
<route id="blog_show" pattern="/blog/{slug}" hostname="{locale}.example.com">
<default key="_controller">MyBundle:Blog:show</default>
<requirement key="_method">GET</requirement>
<requirement key="locale">\w+</requirement>

View File

@ -1,7 +1,7 @@
blog_show:
pattern: /blog/{slug}
defaults: { _controller: MyBlogBundle:Blog:show }
hostname_pattern: "{locale}.example.com"
requirements: { '_method': 'GET', 'locale': '\w+' }
pattern: /blog/{slug}
defaults: { _controller: MyBlogBundle:Blog:show }
hostname : "{locale}.example.com"
requirements: { '_method': 'GET', 'locale': '\w+' }
options:
compiler_class: RouteCompiler

View File

@ -4,7 +4,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<import resource="validpattern.xml" prefix="/{foo}" hostname-pattern="{locale}.example.com">
<import resource="validpattern.xml" prefix="/{foo}" hostname="{locale}.example.com">
<default key="foo">123</default>
<requirement key="foo">\d+</requirement>
<option key="foo">bar</option>

View File

@ -1,7 +1,7 @@
blog_show:
resource: validpattern.yml
prefix: /{foo}
defaults: { 'foo': '123' }
requirements: { 'foo': '\d+' }
options: { 'foo': 'bar' }
hostname_pattern: "{locale}.example.com"
resource: validpattern.yml
prefix: /{foo}
defaults: { 'foo': '123' }
requirements: { 'foo': '\d+' }
options: { 'foo': 'bar' }
hostname: "{locale}.example.com"

View File

@ -89,10 +89,12 @@ class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
{
$routeDatas = array_replace(array(
'name' => 'route',
'pattern' => '/',
'path' => '/',
'requirements' => array(),
'options' => array(),
'defaults' => array(),
'schemes' => array(),
'methods' => array(),
), $routeDatas);
$this->reader
@ -103,7 +105,7 @@ class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
$routeCollection = $this->loader->load($className);
$route = $routeCollection->get($routeDatas['name']);
$this->assertSame($routeDatas['pattern'], $route->getPattern(), '->load preserves pattern annotation');
$this->assertSame($routeDatas['path'], $route->getPath(), '->load preserves path annotation');
$this->assertSame($routeDatas['requirements'],$route->getRequirements(), '->load preserves requirements annotation');
$this->assertCount(0, array_intersect($route->getOptions(), $routeDatas['options']), '->load preserves options annotation');
$this->assertSame(array_replace($routeDatas['defaults'], $methodArgs), $route->getDefaults(), '->load preserves defaults annotation');

View File

@ -43,10 +43,10 @@ class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(1, count($routes), 'One route is loaded');
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
$route = $routes['blog_show'];
$this->assertEquals('/blog/{slug}', $route->getPattern());
$this->assertEquals('/blog/{slug}', $route->getPath());
$this->assertEquals('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
$this->assertEquals('GET', $route->getRequirement('_method'));
$this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
$this->assertEquals('{locale}.example.com', $route->getHostname());
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
}
}

View File

@ -44,11 +44,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(1, count($routes), 'One route is loaded');
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
$route = $routes['blog_show'];
$this->assertEquals('/blog/{slug}', $route->getPattern());
$this->assertEquals('/blog/{slug}', $route->getPath());
$this->assertEquals('MyBundle:Blog:show', $route->getDefault('_controller'));
$this->assertEquals('GET', $route->getRequirement('_method'));
$this->assertEquals('\w+', $route->getRequirement('locale'));
$this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
$this->assertEquals('{locale}.example.com', $route->getHostname());
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
}
@ -59,11 +59,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertCount(1, $routeCollection, 'One route is loaded');
$route = $routeCollection->get('blog_show');
$this->assertEquals('/blog/{slug}', $route->getPattern());
$this->assertEquals('/blog/{slug}', $route->getPath());
$this->assertEquals('MyBundle:Blog:show', $route->getDefault('_controller'));
$this->assertEquals('\w+', $route->getRequirement('slug'));
$this->assertEquals('en|fr|de', $route->getRequirement('_locale'));
$this->assertEquals('{_locale}.example.com', $route->getHostnamePattern());
$this->assertEquals('{_locale}.example.com', $route->getHostname());
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
}
@ -75,12 +75,12 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(1, count($routes), 'One route is loaded');
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
$this->assertEquals('/{foo}/blog/{slug}', $routes['blog_show']->getPattern());
$this->assertEquals('/{foo}/blog/{slug}', $routes['blog_show']->getPath());
$this->assertEquals('MyBundle:Blog:show', $routes['blog_show']->getDefault('_controller'));
$this->assertEquals('123', $routes['blog_show']->getDefault('foo'));
$this->assertEquals('\d+', $routes['blog_show']->getRequirement('foo'));
$this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
$this->assertEquals('{locale}.example.com', $routes['blog_show']->getHostnamePattern());
$this->assertEquals('{locale}.example.com', $routes['blog_show']->getHostname());
}
/**

View File

@ -70,7 +70,7 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$route = $routeCollection->get('#$péß^a|');
$this->assertInstanceOf('Symfony\Component\Routing\Route', $route);
$this->assertSame('/true', $route->getPattern());
$this->assertSame('/true', $route->getPath());
}
public function testLoadWithPattern()
@ -82,11 +82,11 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(1, count($routes), 'One route is loaded');
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
$route = $routes['blog_show'];
$this->assertEquals('/blog/{slug}', $route->getPattern());
$this->assertEquals('/blog/{slug}', $route->getPath());
$this->assertEquals('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
$this->assertEquals('GET', $route->getRequirement('_method'));
$this->assertEquals('\w+', $route->getRequirement('locale'));
$this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
$this->assertEquals('{locale}.example.com', $route->getHostname());
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
}
@ -98,11 +98,11 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(1, count($routes), 'One route is loaded');
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
$this->assertEquals('/{foo}/blog/{slug}', $routes['blog_show']->getPattern());
$this->assertEquals('/{foo}/blog/{slug}', $routes['blog_show']->getPath());
$this->assertEquals('MyBlogBundle:Blog:show', $routes['blog_show']->getDefault('_controller'));
$this->assertEquals('123', $routes['blog_show']->getDefault('foo'));
$this->assertEquals('\d+', $routes['blog_show']->getRequirement('foo'));
$this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
$this->assertEquals('{locale}.example.com', $routes['blog_show']->getHostnamePattern());
$this->assertEquals('{locale}.example.com', $routes['blog_show']->getHostname());
}
}

View File

@ -105,7 +105,7 @@ EOF;
$string .= sprintf("%s|-coll %s\n", $prefix, $route->getPrefix());
$string .= $this->collectionToString($route, $prefix.'| ');
} else {
$string .= sprintf("%s|-route %s %s\n", $prefix, $route->getName(), $route->getRoute()->getPattern());
$string .= sprintf("%s|-route %s %s\n", $prefix, $route->getName(), $route->getRoute()->getPath());
}
}

View File

@ -368,7 +368,7 @@ class UrlMatcherTest extends \PHPUnit_Framework_TestCase
$coll = new RouteCollection();
$coll->add('foo', new Route('/foo/{foo}'));
$coll->add('bar', new Route('/bar/{foo}', array(), array(), array(), '{locale}.example.net'));
$coll->setHostnamePattern('{locale}.example.com');
$coll->setHostname('{locale}.example.com');
$matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
$this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar'));

View File

@ -33,7 +33,7 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
$collection->add('foo', new Route('/foo'));
$collection->add('foo', new Route('/foo1'));
$this->assertEquals('/foo1', $collection->get('foo')->getPattern());
$this->assertEquals('/foo1', $collection->get('foo')->getPath());
}
public function testDeepOverriddenRoute()
@ -50,8 +50,8 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
$collection1->addCollection($collection2);
$collection->addCollection($collection1);
$this->assertEquals('/foo2', $collection1->get('foo')->getPattern());
$this->assertEquals('/foo2', $collection->get('foo')->getPattern());
$this->assertEquals('/foo2', $collection1->get('foo')->getPath());
$this->assertEquals('/foo2', $collection->get('foo')->getPath());
}
public function testIteratorWithOverriddenRoutes()
@ -63,7 +63,7 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
$collection1->add('foo', new Route('/foo1'));
$collection->addCollection($collection1);
$this->assertEquals('/foo1', $this->getFirstNamedRoute($collection, 'foo')->getPattern());
$this->assertEquals('/foo1', $this->getFirstNamedRoute($collection, 'foo')->getPath());
}
public function testCount()
@ -110,7 +110,7 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
$collection1 = new RouteCollection();
$collection1->add('foo', $foo1 = new Route('/foo1'));
$collection->addCollection($collection1, '/{foo}', array('foo' => 'foo'), array('foo' => '\d+'), array('foo' => 'bar'));
$this->assertEquals('/{foo}/foo1', $collection->get('foo')->getPattern(), '->addCollection() can add a prefix to all merged routes');
$this->assertEquals('/{foo}/foo1', $collection->get('foo')->getPath(), '->addCollection() can add a prefix to all merged routes');
$this->assertEquals(array('foo' => 'foo'), $collection->get('foo')->getDefaults(), '->addCollection() can add a prefix to all merged routes');
$this->assertEquals(array('foo' => '\d+'), $collection->get('foo')->getRequirements(), '->addCollection() can add a prefix to all merged routes');
$this->assertEquals(
@ -137,8 +137,8 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
$collection->addPrefix(' / ');
$this->assertSame('', $collection->getPrefix(), '->addPrefix() trims the prefix and a single slash has no effect');
$collection->addPrefix('/{admin}', array('admin' => 'admin'), array('admin' => '\d+'), array('foo' => 'bar'));
$this->assertEquals('/{admin}/foo', $collection->get('foo')->getPattern(), '->addPrefix() adds a prefix to all routes');
$this->assertEquals('/{admin}/bar', $collection->get('bar')->getPattern(), '->addPrefix() adds a prefix to all routes');
$this->assertEquals('/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() adds a prefix to all routes');
$this->assertEquals('/{admin}/bar', $collection->get('bar')->getPath(), '->addPrefix() adds a prefix to all routes');
$this->assertEquals(array('admin' => 'admin'), $collection->get('foo')->getDefaults(), '->addPrefix() adds defaults to all routes');
$this->assertEquals(array('admin' => 'admin'), $collection->get('bar')->getDefaults(), '->addPrefix() adds defaults to all routes');
$this->assertEquals(array('admin' => '\d+'), $collection->get('foo')->getRequirements(), '->addPrefix() adds requirements to all routes');
@ -155,8 +155,8 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('/0/{admin}', $collection->getPrefix(), '->addPrefix() ensures a prefix must start with a slash and must not end with a slash');
$collection->addPrefix('/ /');
$this->assertSame('/ /0/{admin}', $collection->getPrefix(), '->addPrefix() can handle spaces if desired');
$this->assertSame('/ /0/{admin}/foo', $collection->get('foo')->getPattern(), 'the route pattern is in synch with the collection prefix');
$this->assertSame('/ /0/{admin}/bar', $collection->get('bar')->getPattern(), 'the route pattern in a sub-collection is in synch with the collection prefix');
$this->assertSame('/ /0/{admin}/foo', $collection->get('foo')->getPath(), 'the route path is in synch with the collection prefix');
$this->assertSame('/ /0/{admin}/bar', $collection->get('bar')->getPath(), 'the route path in a sub-collection is in synch with the collection prefix');
}
public function testAddPrefixOverridesDefaultsAndRequirements()
@ -224,7 +224,7 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
$this->assertNull($collection1->get(0), '->get() does not disclose internal child RouteCollection');
}
public function testSetHostnamePattern()
public function testSetHostname()
{
$collection = new RouteCollection();
$routea = new Route('/a');
@ -232,9 +232,9 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
$collection->add('a', $routea);
$collection->add('b', $routeb);
$collection->setHostnamePattern('{locale}.example.com');
$collection->setHostname('{locale}.example.com');
$this->assertEquals('{locale}.example.com', $routea->getHostnamePattern());
$this->assertEquals('{locale}.example.com', $routeb->getHostnamePattern());
$this->assertEquals('{locale}.example.com', $routea->getHostname());
$this->assertEquals('{locale}.example.com', $routeb->getHostname());
}
}

View File

@ -18,25 +18,33 @@ class RouteTest extends \PHPUnit_Framework_TestCase
public function testConstructor()
{
$route = new Route('/{foo}', array('foo' => 'bar'), array('foo' => '\d+'), array('foo' => 'bar'), '{locale}.example.com');
$this->assertEquals('/{foo}', $route->getPattern(), '__construct() takes a pattern as its first argument');
$this->assertEquals('/{foo}', $route->getPath(), '__construct() takes a path as its first argument');
$this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '__construct() takes defaults as its second argument');
$this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '__construct() takes requirements as its third argument');
$this->assertEquals('bar', $route->getOption('foo'), '__construct() takes options as its fourth argument');
$this->assertEquals('{locale}.example.com', $route->getHostnamePattern(), '__construct() takes a hostname pattern as its fifth argument');
$this->assertEquals('{locale}.example.com', $route->getHostname(), '__construct() takes a hostname pattern as its fifth argument');
$route = new Route('/', array(), array(), array(), '', array('Https'), array('POST', 'put'));
$this->assertEquals(array('https'), $route->getSchemes(), '__construct() takes schemes as its sixth argument and lowercases it');
$this->assertEquals(array('POST', 'PUT'), $route->getMethods(), '__construct() takes methods as its seventh argument and uppercases it');
$route = new Route('/', array(), array(), array(), '', 'Https', 'Post');
$this->assertEquals(array('https'), $route->getSchemes(), '__construct() takes a single scheme as its sixth argument');
$this->assertEquals(array('POST'), $route->getMethods(), '__construct() takes a single method as its seventh argument');
}
public function testPattern()
public function testPath()
{
$route = new Route('/{foo}');
$route->setPattern('/{bar}');
$this->assertEquals('/{bar}', $route->getPattern(), '->setPattern() sets the pattern');
$route->setPattern('');
$this->assertEquals('/', $route->getPattern(), '->setPattern() adds a / at the beginning of the pattern if needed');
$route->setPattern('bar');
$this->assertEquals('/bar', $route->getPattern(), '->setPattern() adds a / at the beginning of the pattern if needed');
$this->assertEquals($route, $route->setPattern(''), '->setPattern() implements a fluent interface');
$route->setPattern('//path');
$this->assertEquals('/path', $route->getPattern(), '->setPattern() does not allow two slashes "//" at the beginning of the pattern as it would be confused with a network path when generating the path from the route');
$route->setPath('/{bar}');
$this->assertEquals('/{bar}', $route->getPath(), '->setPath() sets the path');
$route->setPath('');
$this->assertEquals('/', $route->getPath(), '->setPath() adds a / at the beginning of the path if needed');
$route->setPath('bar');
$this->assertEquals('/bar', $route->getPath(), '->setPath() adds a / at the beginning of the path if needed');
$this->assertEquals($route, $route->setPath(''), '->setPath() implements a fluent interface');
$route->setPath('//path');
$this->assertEquals('/path', $route->getPath(), '->setPath() does not allow two slahes "//" at the beginning of the path as it would be confused with a network path when generating the path from the route');
}
public function testOptions()
@ -85,7 +93,7 @@ class RouteTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() returns a requirement');
$this->assertNull($route->getRequirement('bar'), '->getRequirement() returns null if a requirement is not defined');
$route->setRequirements(array('foo' => '^\d+$'));
$this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() removes ^ and $ from the pattern');
$this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() removes ^ and $ from the path');
$this->assertEquals($route, $route->setRequirements(array()), '->setRequirements() implements a fluent interface');
$route->setRequirements(array('foo' => '\d+'));
@ -98,7 +106,7 @@ class RouteTest extends \PHPUnit_Framework_TestCase
{
$route = new Route('/{foo}');
$route->setRequirement('foo', '^\d+$');
$this->assertEquals('\d+', $route->getRequirement('foo'), '->setRequirement() removes ^ and $ from the pattern');
$this->assertEquals('\d+', $route->getRequirement('foo'), '->setRequirement() removes ^ and $ from the path');
}
/**
@ -122,11 +130,55 @@ class RouteTest extends \PHPUnit_Framework_TestCase
);
}
public function testHostnamePattern()
public function testHostname()
{
$route = new Route('/');
$route->setHostnamePattern('{locale}.example.net');
$this->assertEquals('{locale}.example.net', $route->getHostnamePattern(), '->setHostnamePattern() sets the hostname pattern');
$route->setHostname('{locale}.example.net');
$this->assertEquals('{locale}.example.net', $route->getHostname(), '->setHostname() sets the hostname pattern');
}
public function testScheme()
{
$route = new Route('/');
$this->assertEquals(array(), $route->getSchemes(), 'schemes is initialized with array()');
$route->setSchemes('hTTp');
$this->assertEquals(array('http'), $route->getSchemes(), '->setSchemes() accepts a single scheme string and lowercases it');
$route->setSchemes(array('HttpS', 'hTTp'));
$this->assertEquals(array('https', 'http'), $route->getSchemes(), '->setSchemes() accepts an array of schemes and lowercases them');
}
public function testSchemeIsBC()
{
$route = new Route('/');
$route->setRequirement('_scheme', 'http|https');
$this->assertEquals('http|https', $route->getRequirement('_scheme'));
$this->assertEquals(array('http', 'https'), $route->getSchemes());
$route->setSchemes(array('hTTp'));
$this->assertEquals('http', $route->getRequirement('_scheme'));
$route->setSchemes(array());
$this->assertNull($route->getRequirement('_scheme'));
}
public function testMethod()
{
$route = new Route('/');
$this->assertEquals(array(), $route->getMethods(), 'methods is initialized with array()');
$route->setMethods('gEt');
$this->assertEquals(array('GET'), $route->getMethods(), '->setMethods() accepts a single method string and uppercases it');
$route->setMethods(array('gEt', 'PosT'));
$this->assertEquals(array('GET', 'POST'), $route->getMethods(), '->setMethods() accepts an array of methods and uppercases them');
}
public function testMethodIsBC()
{
$route = new Route('/');
$route->setRequirement('_method', 'GET|POST');
$this->assertEquals('GET|POST', $route->getRequirement('_method'));
$this->assertEquals(array('GET', 'POST'), $route->getMethods());
$route->setMethods(array('gEt'));
$this->assertEquals('GET', $route->getRequirement('_method'));
$route->setMethods(array());
$this->assertNull($route->getRequirement('_method'));
}
public function testCompile()