Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Hostname pattern for routes (symfony/symfony#3378) #3

Merged
merged 11 commits into from

2 participants

@fabpot

While reviewing your PR, I've made some changes and fixes. Have a look at each individual commit as I've also fixed some CS issues that makes the whole diff more difficult to read.

Here are what I've done so far:

  • fixed indentation of dumped collections by the PHP matcher dumper
  • added support for the XML loader
  • fixed Route annotation for hostname (should be hostname_pattern instead of hostnamePattern)
  • added hostname support in UrlMatcher
  • fixed route generation with a hostname pattern when the hostname is the same as the current one (no need to generate an absolute URL in this case)
  • added some more unit tests
@fabpot fabpot referenced this pull request in symfony/symfony
Merged

[2.2][Routing] hostname pattern for routes #3378

@arnaud-lb arnaud-lb referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@arnaud-lb arnaud-lb merged commit 5a17f81 into arnaud-lb:hostname-routes
@fabpot fabpot referenced this pull request from a commit in symfony/symfony
@fabpot fabpot merged branch arnaud-lb/hostname-routes (PR #3378)
This PR was merged into the master branch.

Commits
-------

17f51a1 Merge pull request #6 from Tobion/hostname-routes
e120a7a fix API of RouteCollection
26e5684 some type fixes
514e27a [Routing] fix PhpMatcherDumper that returned numeric-indexed params that are returned besides named placeholders by preg_match
7ed3013 switch to array_replace instead of array_merge
94ec653 removed irrelevant string case in XmlFileLoader
9ffe3de synchronize the fixtures in different formats and fix default for numeric requirement
6cd3457 fixed CS
8366b8a [Routing] fixed validity check for hostname params in UrlGenerator
a8ce621 [Routing] added support for hostname in the apache matcher dumper
562174a [Routing] fixed indentation of dumped collections
1489021 fixed CS
a270458 [Routing] added some more unit tests
153fcf2 [Routing] added some unit tests for the PHP loader
68da6ad [Routing] added support for hostname in the XML loader
3dfca47 [Routing] added some unit tests for the YAML loader
92f9c15 [Routing] changed CompiledRoute signature to be more consistent
d91e5a2 [Routing] fixed Route annotation for hostname (should be hostname_pattern instead of hostnamePattern)
62de881 [Routing] clarified a variable content
11b4378 [Routing] added hostname support in UrlMatcher
fc015d5 [Routing] fixed route generation with a hostname pattern when the hostname is the same as the current one (no need to force the generated URL to be absolute)
462999d [Routing] display hostname pattern in router:debug output
805806a [Routing] added hostname matching support to UrlGenerator
7a15e00 [Routing] added hostname matching support to AnnotationClassLoader
cab450c [Routing] added hostname matching support to YamlFileLoader
85d11af [Routing] added hostname matching support to PhpMatcherDumper
402359b [Routing] added hostname matching support to RouteCompiler
add3658 [Routing] added hostname matching support to Route and RouteCollection
23feb37 [Routing] added hostname matching support to CompiledRoute

Discussion
----------

[2.2][Routing] hostname pattern for routes

Bug fix: no
Feature addition: yes
Fixes the following tickets: #1762, #3276
Backwards compatibility break: no
Symfony2 tests pass: yes

This adds a hostname_pattern property to routes. It works like the pattern property (hostname_pattern can have variables, requirements, etc). The hostname_pattern property can be set on both routes and route collections.

Yaml example:

``` yaml
# Setting the hostname_pattern for a whole collection of routes

AcmeBundle:
    resource: "@AcmeBundle/Controller/"
    type: annotation
    prefix: /
    hostname_pattern: {locale}.example.com
    requirements:
        locale: en|fr

# Setting the hostname_pattern for single route

some_route:
    pattern: /hello/{name}
    hostname_pattern: {locale}.example.com
    requirements:
        locale: en|fr
        name: \w+
    defaults:
        _controller: Foo:bar:baz
```

Annotations example:

``` php
<?php

/**
 * Inherits requirements and hostname pattern from the collection
 * @Route("/foo")
 */
public function fooAction();

/**
 * Set a specific hostnamePattern for this route only
 * @Route("/foo", hostnamePattern="{_locale}.example.com", requirements={"_locale="fr|en"})
 */
public function fooAction();

```

Performance:

Consecutive routes with the same hostname pattern are grouped, and a single test is made against the hostname for this group, so the overhead is very low:

```
@Route("/foo", hostnamePattern="a.example.com")
@Route("/bar", hostnamePattern="a.example.com")
@Route("/baz", hostnamePattern="b.example.com")
```

is compiled like this:

```
if (hostname matches a.example.com) {
    // test route "/foo"
    // test route "/bar"
}
if (hostname matches b.example.com) {
    // test route "/baz"
}
```

The PR also tries harder to optimize routes sharing the same prefix:

```
@Route("/cafe")
@Route("/cacao")
@Route("/coca")
```

is compiled like this:

```
if (url starts with /c) {
    if (url starts with /ca) {
        // test route "/cafe"
        // test route "/cacao"
    }
    // test route "/coca"
}
```

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

by Koc at 2012-02-16T14:14:19Z

Interesting. Have you looked at #3057, #3002?

Killer feature of #3057 : multiple hostnames per route.

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

by arnaud-lb at 2012-02-16T14:21:28Z

@Koc yes, the main difference is that this PR allows variables in the hostname pattern, with requirements, etc just like the path pattern. The other PRs use a `_host` requirement, which works like the `_method` requirement (takes a list of allowed hostnames separated by `|`).

> Killer feature of #3057 : multiple hostnames per route.

If you have multiple tlds you can easily do it like this:

``` yaml
hostbased_route:
  pattern:  /
  hostname_pattern: symfony.{tld}
  requirements:
     tld: org|com
```

Or with completely different domain names:

``` yaml
hostbased_route:
  pattern:  /
  hostname_pattern: {domain}
  requirements:
     domain: example\.com|symfony\.com
```

Requirements allow DIC %parameters%, so you can also put you domains in your config.yml.

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

by Koc at 2012-02-16T15:52:16Z

wow, nice! So looks like this PR closes my #3276 ticket?

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

by arnaud-lb at 2012-02-16T15:53:55Z

Yes, apparently :)

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

by Koc at 2012-02-16T15:56:53Z

I cann't find method `ParameterBag::resolveValue` calling in this PR, like here https://github.com/symfony/symfony/pull/3316/files

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

by arnaud-lb at 2012-02-16T16:03:48Z

I think it's in core already

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

by Koc at 2012-02-16T16:11:38Z

looks like yes
https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php#L81

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

by dlsniper at 2012-02-16T19:37:57Z

This PR looks great, it's something like this I've been waiting for.

I know @fabpot said he's working on something similar but I think if he agrees with this it could be a great addition to the core.

@fabpot , @stof any objections about this PR if gets fully done?

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

by stof at 2012-02-16T20:00:21Z

Well, we already have 2 other implementations for this stuff in the PRs. @fabpot please take time to look at them

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

by stof at 2012-02-16T20:03:17Z

This one is absolutely not tested and seems to break the existing tests according to the description. So it cannot be reviewed as is.

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

by dlsniper at 2012-02-16T22:00:24Z

@stof I understand it's a WIP but the other PRs where ignored as well and like you've said, there's a bunch of PRs already on this issue all doing a thing or another. So an early feedback on this, or any other, could lead it to the right path in order to finally solve this issue.

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

by arnaud-lb at 2012-02-17T23:57:28Z

Added tests; others are passing now

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

by arnaud-lb at 2012-02-22T21:10:20Z

I'm going to add support for the Apache dumper and the XML loader; does this PR have a chance to be merged ? cc @fabpot @stof

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

by stof at 2012-02-22T22:05:23Z

@arnaud-lb We need to wait @fabpot's mind about the way he prefers to implement it to know which one can be merged.

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

by IjinPL at 2012-02-27T02:01:57Z

Forked @arnaud-lb *hostname_pattern* to add XML parasing support.

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

by stof at 2012-04-03T23:59:12Z

@arnaud-lb Please rebase your branch. It conflicts with master because of the move of the tests

@fabpot @vicb ping

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

by dlsniper at 2012-04-13T19:52:23Z

Hi,

If @arnaud-lb won't be able to rebase this I could help with some work on this but there's still the problem of actually choosing the right PR(s) for this issue. @blogsh says in his last commit that this PR is a bit better in his opinion but @fabpot needs to decide in the end.

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

by arnaud-lb at 2012-04-14T17:26:55Z

@stof rebased

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

by nomack84 at 2012-04-20T13:01:00Z

@fabpot Any final word about this pull request? It would be nice to have this feature ready for 2.1.

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

by asm89 at 2012-04-24T21:27:50Z

Using the `{_locale}` placeholder in the host would set the locale for the request just like it does now?

Another thing I'm wondering is how/if it should be possible to set the hostname pattern for all your routes, or at least when importing routes? Otherwise you'll end up repeating the same host pattern over and over again. I think this is also important when importing routes from third party bundles.

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

by fabpot at 2012-04-25T01:17:51Z

I'm reviewing this PR and I'm going to make some modifications. I will send a PR to @arnaud-lb soon.

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

by fabpot at 2012-04-25T03:10:18Z

I've sent a PR to @arnaud-lb arnaud-lb/symfony#3 that fixes some minor bugs and add support in more classes.

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

by fabpot at 2012-04-25T03:12:52Z

@asm89:

Placeholders in the hostname are managed in the same way as the ones from the URL pattern.

You can set a hostname pattern for a collection (like the prefix for URL patterns).

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

by Tobion at 2012-04-25T09:31:19Z

I think we need to change the contents of $variables, $tokens, and $hostnameTokens in the CompiledRoute. They contain redundant information and the content structure of these variables ist not documentation in any way. If we remove duplicated content and put it in a (single) well defined variable, it would also reduce the information that need to be saved in the generated class by the UrlGeneratorDumper.

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

by arnaud-lb at 2012-04-26T08:54:21Z

@fabpot thanks :) I've merged it

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

by stof at 2012-04-26T12:08:40Z

A rebase is needed

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

by fabpot at 2012-04-26T13:28:08Z

no need to rebase, I will resolve the conflicts when merging. I've still have some minor changes to do before merging though. Anyone willing to have a look at implementing the Apache dumper part?

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

by Tobion at 2012-04-26T14:59:00Z

@fabpot you want to merge this for 2.1 although it introduces big changes that need extensive review and testing? But #3958 is not considered for 2.1? I thought we are in some sort of feature freeze for the components in order to not postpone the release.

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

by fabpot at 2012-04-26T17:21:09Z

@Tobion: I never said it will be in 2.1. The plan is to create a 2.1 branch soon so that we can continue working on 2.2.

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

by Koc at 2012-04-26T19:46:43Z

https://twitter.com/#!/fabpot/status/178502663690915840
c94bdf6
@mmucklo mmucklo referenced this pull request from a commit
@fabpot fabpot merged branch arnaud-lb/hostname-routes (PR #3378)
This PR was merged into the master branch.

Commits
-------

17f51a1 Merge pull request #6 from Tobion/hostname-routes
e120a7a fix API of RouteCollection
26e5684 some type fixes
514e27a [Routing] fix PhpMatcherDumper that returned numeric-indexed params that are returned besides named placeholders by preg_match
7ed3013 switch to array_replace instead of array_merge
94ec653 removed irrelevant string case in XmlFileLoader
9ffe3de synchronize the fixtures in different formats and fix default for numeric requirement
6cd3457 fixed CS
8366b8a [Routing] fixed validity check for hostname params in UrlGenerator
a8ce621 [Routing] added support for hostname in the apache matcher dumper
562174a [Routing] fixed indentation of dumped collections
1489021 fixed CS
a270458 [Routing] added some more unit tests
153fcf2 [Routing] added some unit tests for the PHP loader
68da6ad [Routing] added support for hostname in the XML loader
3dfca47 [Routing] added some unit tests for the YAML loader
92f9c15 [Routing] changed CompiledRoute signature to be more consistent
d91e5a2 [Routing] fixed Route annotation for hostname (should be hostname_pattern instead of hostnamePattern)
62de881 [Routing] clarified a variable content
11b4378 [Routing] added hostname support in UrlMatcher
fc015d5 [Routing] fixed route generation with a hostname pattern when the hostname is the same as the current one (no need to force the generated URL to be absolute)
462999d [Routing] display hostname pattern in router:debug output
805806a [Routing] added hostname matching support to UrlGenerator
7a15e00 [Routing] added hostname matching support to AnnotationClassLoader
cab450c [Routing] added hostname matching support to YamlFileLoader
85d11af [Routing] added hostname matching support to PhpMatcherDumper
402359b [Routing] added hostname matching support to RouteCompiler
add3658 [Routing] added hostname matching support to Route and RouteCollection
23feb37 [Routing] added hostname matching support to CompiledRoute

Discussion
----------

[2.2][Routing] hostname pattern for routes

Bug fix: no
Feature addition: yes
Fixes the following tickets: #1762, #3276
Backwards compatibility break: no
Symfony2 tests pass: yes

This adds a hostname_pattern property to routes. It works like the pattern property (hostname_pattern can have variables, requirements, etc). The hostname_pattern property can be set on both routes and route collections.

Yaml example:

``` yaml
# Setting the hostname_pattern for a whole collection of routes

AcmeBundle:
    resource: "@AcmeBundle/Controller/"
    type: annotation
    prefix: /
    hostname_pattern: {locale}.example.com
    requirements:
        locale: en|fr

# Setting the hostname_pattern for single route

some_route:
    pattern: /hello/{name}
    hostname_pattern: {locale}.example.com
    requirements:
        locale: en|fr
        name: \w+
    defaults:
        _controller: Foo:bar:baz
```

Annotations example:

``` php
<?php

/**
 * Inherits requirements and hostname pattern from the collection
 * @Route("/foo")
 */
public function fooAction();

/**
 * Set a specific hostnamePattern for this route only
 * @Route("/foo", hostnamePattern="{_locale}.example.com", requirements={"_locale="fr|en"})
 */
public function fooAction();

```

Performance:

Consecutive routes with the same hostname pattern are grouped, and a single test is made against the hostname for this group, so the overhead is very low:

```
@Route("/foo", hostnamePattern="a.example.com")
@Route("/bar", hostnamePattern="a.example.com")
@Route("/baz", hostnamePattern="b.example.com")
```

is compiled like this:

```
if (hostname matches a.example.com) {
    // test route "/foo"
    // test route "/bar"
}
if (hostname matches b.example.com) {
    // test route "/baz"
}
```

The PR also tries harder to optimize routes sharing the same prefix:

```
@Route("/cafe")
@Route("/cacao")
@Route("/coca")
```

is compiled like this:

```
if (url starts with /c) {
    if (url starts with /ca) {
        // test route "/cafe"
        // test route "/cacao"
    }
    // test route "/coca"
}
```

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

by Koc at 2012-02-16T14:14:19Z

Interesting. Have you looked at #3057, #3002?

Killer feature of #3057 : multiple hostnames per route.

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

by arnaud-lb at 2012-02-16T14:21:28Z

@Koc yes, the main difference is that this PR allows variables in the hostname pattern, with requirements, etc just like the path pattern. The other PRs use a `_host` requirement, which works like the `_method` requirement (takes a list of allowed hostnames separated by `|`).

> Killer feature of #3057 : multiple hostnames per route.

If you have multiple tlds you can easily do it like this:

``` yaml
hostbased_route:
  pattern:  /
  hostname_pattern: symfony.{tld}
  requirements:
     tld: org|com
```

Or with completely different domain names:

``` yaml
hostbased_route:
  pattern:  /
  hostname_pattern: {domain}
  requirements:
     domain: example\.com|symfony\.com
```

Requirements allow DIC %parameters%, so you can also put you domains in your config.yml.

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

by Koc at 2012-02-16T15:52:16Z

wow, nice! So looks like this PR closes my #3276 ticket?

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

by arnaud-lb at 2012-02-16T15:53:55Z

Yes, apparently :)

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

by Koc at 2012-02-16T15:56:53Z

I cann't find method `ParameterBag::resolveValue` calling in this PR, like here https://github.com/symfony/symfony/pull/3316/files

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

by arnaud-lb at 2012-02-16T16:03:48Z

I think it's in core already

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

by Koc at 2012-02-16T16:11:38Z

looks like yes
https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php#L81

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

by dlsniper at 2012-02-16T19:37:57Z

This PR looks great, it's something like this I've been waiting for.

I know @fabpot said he's working on something similar but I think if he agrees with this it could be a great addition to the core.

@fabpot , @stof any objections about this PR if gets fully done?

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

by stof at 2012-02-16T20:00:21Z

Well, we already have 2 other implementations for this stuff in the PRs. @fabpot please take time to look at them

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

by stof at 2012-02-16T20:03:17Z

This one is absolutely not tested and seems to break the existing tests according to the description. So it cannot be reviewed as is.

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

by dlsniper at 2012-02-16T22:00:24Z

@stof I understand it's a WIP but the other PRs where ignored as well and like you've said, there's a bunch of PRs already on this issue all doing a thing or another. So an early feedback on this, or any other, could lead it to the right path in order to finally solve this issue.

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

by arnaud-lb at 2012-02-17T23:57:28Z

Added tests; others are passing now

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

by arnaud-lb at 2012-02-22T21:10:20Z

I'm going to add support for the Apache dumper and the XML loader; does this PR have a chance to be merged ? cc @fabpot @stof

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

by stof at 2012-02-22T22:05:23Z

@arnaud-lb We need to wait @fabpot's mind about the way he prefers to implement it to know which one can be merged.

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

by IjinPL at 2012-02-27T02:01:57Z

Forked @arnaud-lb *hostname_pattern* to add XML parasing support.

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

by stof at 2012-04-03T23:59:12Z

@arnaud-lb Please rebase your branch. It conflicts with master because of the move of the tests

@fabpot @vicb ping

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

by dlsniper at 2012-04-13T19:52:23Z

Hi,

If @arnaud-lb won't be able to rebase this I could help with some work on this but there's still the problem of actually choosing the right PR(s) for this issue. @blogsh says in his last commit that this PR is a bit better in his opinion but @fabpot needs to decide in the end.

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

by arnaud-lb at 2012-04-14T17:26:55Z

@stof rebased

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

by nomack84 at 2012-04-20T13:01:00Z

@fabpot Any final word about this pull request? It would be nice to have this feature ready for 2.1.

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

by asm89 at 2012-04-24T21:27:50Z

Using the `{_locale}` placeholder in the host would set the locale for the request just like it does now?

Another thing I'm wondering is how/if it should be possible to set the hostname pattern for all your routes, or at least when importing routes? Otherwise you'll end up repeating the same host pattern over and over again. I think this is also important when importing routes from third party bundles.

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

by fabpot at 2012-04-25T01:17:51Z

I'm reviewing this PR and I'm going to make some modifications. I will send a PR to @arnaud-lb soon.

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

by fabpot at 2012-04-25T03:10:18Z

I've sent a PR to @arnaud-lb arnaud-lb/symfony#3 that fixes some minor bugs and add support in more classes.

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

by fabpot at 2012-04-25T03:12:52Z

@asm89:

Placeholders in the hostname are managed in the same way as the ones from the URL pattern.

You can set a hostname pattern for a collection (like the prefix for URL patterns).

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

by Tobion at 2012-04-25T09:31:19Z

I think we need to change the contents of $variables, $tokens, and $hostnameTokens in the CompiledRoute. They contain redundant information and the content structure of these variables ist not documentation in any way. If we remove duplicated content and put it in a (single) well defined variable, it would also reduce the information that need to be saved in the generated class by the UrlGeneratorDumper.

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

by arnaud-lb at 2012-04-26T08:54:21Z

@fabpot thanks :) I've merged it

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

by stof at 2012-04-26T12:08:40Z

A rebase is needed

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

by fabpot at 2012-04-26T13:28:08Z

no need to rebase, I will resolve the conflicts when merging. I've still have some minor changes to do before merging though. Anyone willing to have a look at implementing the Apache dumper part?

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

by Tobion at 2012-04-26T14:59:00Z

@fabpot you want to merge this for 2.1 although it introduces big changes that need extensive review and testing? But #3958 is not considered for 2.1? I thought we are in some sort of feature freeze for the components in order to not postpone the release.

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

by fabpot at 2012-04-26T17:21:09Z

@Tobion: I never said it will be in 2.1. The plan is to create a 2.1 branch soon so that we can continue working on 2.2.

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

by Koc at 2012-04-26T19:46:43Z

https://twitter.com/#!/fabpot/status/178502663690915840
82356b4
@arnaud-lb arnaud-lb referenced this pull request from a commit
@fabpot fabpot merged branch pborreli/typos-friday (PR #6364)
This PR was merged into the master branch.

Commits
-------

c84907b Merge pull request #3 from Tobion/patch-5
9ab5e4f fix typos in PhpMatcherDumper
60eeacd Fixed typos
4a073e6 [Form] Fixed duplicate case expression

Discussion
----------

Fixed Typos

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

by Tobion at 2012-12-14T22:51:05Z

:+1: Thanks for fixing some of my typos.
@pborreli see pborreli/symfony#3
c3f1a46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 221 additions and 143 deletions.
  1. +1 −1  src/Symfony/Component/Routing/Annotation/Route.php
  2. +5 −5 src/Symfony/Component/Routing/CompiledRoute.php
  3. +20 −25 src/Symfony/Component/Routing/Generator/UrlGenerator.php
  4. +6 −6 src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
  5. +3 −2 src/Symfony/Component/Routing/Loader/XmlFileLoader.php
  6. +2 −0  src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
  7. +31 −38 src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php
  8. +7 −8 src/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php
  9. +6 −4 src/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php
  10. +12 −25 src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
  11. +6 −1 src/Symfony/Component/Routing/Matcher/UrlMatcher.php
  12. +0 −1  src/Symfony/Component/Routing/RouteCollection.php
  13. +4 −5 src/Symfony/Component/Routing/RouteCompiler.php
  14. +2 −1  src/Symfony/Component/Routing/Tests/Annotation/RouteTest.php
  15. +2 −2 src/Symfony/Component/Routing/Tests/CompiledRouteTest.php
  16. +3 −2 src/Symfony/Component/Routing/Tests/Fixtures/validpattern.php
  17. +1 −1  src/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml
  18. +4 −3 src/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml
  19. +1 −1  src/Symfony/Component/Routing/Tests/Fixtures/validresource.xml
  20. +6 −5 src/Symfony/Component/Routing/Tests/Fixtures/validresource.yml
  21. +0 −1  src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
  22. +21 −0 src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
  23. +4 −0 src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
  24. +7 −0 src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
  25. +7 −0 src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
  26. +2 −3 src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php
  27. +35 −0 src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
  28. +14 −0 src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
  29. +0 −2  src/Symfony/Component/Routing/Tests/RouteCompilerTest.php
  30. +9 −1 src/Symfony/Component/Routing/Tests/RouteTest.php
View
2  src/Symfony/Component/Routing/Annotation/Route.php
@@ -44,7 +44,7 @@ public function __construct(array $data)
}
foreach ($data as $key => $value) {
- $method = 'set'.$key;
+ $method = 'set'.str_replace('_', '', $key);
if (!method_exists($this, $method)) {
throw new \BadMethodCallException(sprintf("Unknown property '%s' on annotation '%s'.", $key, get_class($this)));
}
View
10 src/Symfony/Component/Routing/CompiledRoute.php
@@ -35,23 +35,23 @@ class CompiledRoute
* @param string $staticPrefix The static prefix of the compiled route
* @param string $regex The regular expression to use to match this route
* @param array $tokens An array of tokens to use to generate URL for this route
- * @param array $variables An array of variables
* @param array $pathVariables An array of path variables
- * @param array $hostnameVariables An array of hostname variables
* @param array $hostnameRegex Hostname regex
* @param array $hostnameTokens Hostname tokens
+ * @param array $hostnameVariables An array of hostname variables
+ * @param array $variables An array of variables (variables defined in the path and in the hostname patterns)
*/
- public function __construct(Route $route, $staticPrefix, $regex, array $tokens, array $variables, array $pathVariables = array(), array $hostnameVariables = array(), $hostnameRegex = null, array $hostnameTokens = array())
+ public function __construct(Route $route, $staticPrefix, $regex, array $tokens, array $pathVariables, $hostnameRegex = null, array $hostnameTokens = array(), array $hostnameVariables = array(), array $variables = array())
{
$this->route = $route;
$this->staticPrefix = $staticPrefix;
$this->regex = $regex;
$this->tokens = $tokens;
- $this->variables = $variables;
$this->pathVariables = $pathVariables;
- $this->hostnameVariables = $hostnameVariables;
$this->hostnameRegex = $hostnameRegex;
$this->hostnameTokens = $hostnameTokens;
+ $this->hostnameVariables = $hostnameVariables;
+ $this->variables = $variables;
}
/**
View
45 src/Symfony/Component/Routing/Generator/UrlGenerator.php
@@ -133,25 +133,6 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
$url = '/';
}
- if ($hostnameTokens) {
- $host = '';
- foreach ($hostnameTokens as $token) {
- if ('variable' === $token[0]) {
- if (in_array($tparams[$token[3]], array(null, '', false), true)) {
- // check requirement
- if ($tparams[$token[3]] && !preg_match('#^'.$token[2].'$#', $tparams[$token[3]])) {
- throw new InvalidParameterException(sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $tparams[$token[3]]));
- }
- }
-
- $host = $token[1].$tparams[$token[3]].$host;
-
- } elseif ('text' === $token[0]) {
- $host = $token[1].$host;
- }
- }
- }
-
// add a query string if needed
$extra = array_diff_key($originParameters, $variables, $defaults);
if ($extra && $query = http_build_query($extra)) {
@@ -160,7 +141,7 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
$url = $this->context->getBaseUrl().$url;
- if ($this->context->getHost()) {
+ if ($host = $this->context->getHost()) {
$scheme = $this->context->getScheme();
if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) {
$absolute = true;
@@ -168,15 +149,29 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
}
if ($hostnameTokens) {
- $absolute = true;
- }
+ $ghost = '';
+ foreach ($hostnameTokens as $token) {
+ if ('variable' === $token[0]) {
+ if (in_array($tparams[$token[3]], array(null, '', false), true)) {
+ // check requirement
+ if ($tparams[$token[3]] && !preg_match('#^'.$token[2].'$#', $tparams[$token[3]])) {
+ throw new InvalidParameterException(sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $tparams[$token[3]]));
+ }
+ }
- if ($absolute) {
+ $ghost = $token[1].$tparams[$token[3]].$ghost;
+ } elseif ('text' === $token[0]) {
+ $ghost = $token[1].$ghost;
+ }
+ }
- if (!$hostnameTokens) {
- $host = $this->context->getHost();
+ if ($ghost != $host) {
+ $host = $ghost;
+ $absolute = true;
}
+ }
+ if ($absolute) {
$port = '';
if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
$port = ':'.$this->context->getHttpPort();
View
12 src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
@@ -58,7 +58,7 @@
abstract class AnnotationClassLoader implements LoaderInterface
{
protected $reader;
- protected $routeAnnotationClass = 'Symfony\\Component\\Routing\\Annotation\\Route';
+ protected $routeAnnotationClass = 'Symfony\\Component\\Routing\\Annotation\\Route';
protected $defaultRouteIndex;
/**
@@ -98,11 +98,11 @@ public function load($class, $type = null)
}
$globals = array(
- 'pattern' => '',
- 'requirements' => array(),
- 'options' => array(),
- 'defaults' => array(),
- 'hostname_pattern' => null,
+ 'pattern' => '',
+ 'requirements' => array(),
+ 'options' => array(),
+ 'defaults' => array(),
+ 'hostname_pattern' => null,
);
$class = new \ReflectionClass($class);
View
5 src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -76,6 +76,7 @@ protected function parseNode(RouteCollection $collection, \DOMElement $node, $pa
$resource = (string) $node->getAttribute('resource');
$type = (string) $node->getAttribute('type');
$prefix = (string) $node->getAttribute('prefix');
+ $hostnamePattern = (string) $node->getAttribute('hostname-pattern');
$defaults = array();
$requirements = array();
@@ -102,7 +103,7 @@ protected function parseNode(RouteCollection $collection, \DOMElement $node, $pa
}
$this->setCurrentDir(dirname($path));
- $collection->addCollection($this->import($resource, ('' !== $type ? $type : null), false, $file), $prefix, $defaults, $requirements, $options);
+ $collection->addCollection($this->import($resource, ('' !== $type ? $type : null), false, $file), $prefix, $defaults, $requirements, $options, $hostnamePattern);
break;
default:
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
@@ -159,7 +160,7 @@ protected function parseRoute(RouteCollection $collection, \DOMElement $definiti
}
}
- $route = new Route((string) $definition->getAttribute('pattern'), $defaults, $requirements, $options);
+ $route = new Route((string) $definition->getAttribute('pattern'), $defaults, $requirements, $options, (string) $definition->getAttribute('hostname-pattern'));
$collection->add((string) $definition->getAttribute('id'), $route);
}
View
2  src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
@@ -23,6 +23,7 @@
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="pattern" type="xsd:string" />
+ <xsd:attribute name="hostname-pattern" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="import">
@@ -36,6 +37,7 @@
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="prefix" type="xsd:string" />
<xsd:attribute name="class" type="xsd:string" />
+ <xsd:attribute name="hostname-pattern" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="element" mixed="true">
View
69 src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\Routing\Matcher\Dumper;
/**
- * Collection of routes with attributes
+ * Collection of routes with attributes.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*/
@@ -23,8 +23,10 @@ class DumperCollection implements \IteratorAggregate
private $attributes;
/**
- * @param array $routes Array of DumperCollection|DumperRoute
- * @param array $atributes Array of attributes
+ * Constructor.
+ *
+ * @param array $routes Array of DumperCollection|DumperRoute
+ * @param array $atributes Array of attributes
*/
public function __construct(array $routes = array(), array $attributes = array())
{
@@ -33,7 +35,7 @@ public function __construct(array $routes = array(), array $attributes = array()
}
/**
- * Returns the parent collection
+ * Returns the parent collection.
*
* @return DumperCollection The parent collection
*/
@@ -43,7 +45,7 @@ public function getParent()
}
/**
- * Sets the parent collection
+ * Sets the parent collection.
*
* @param DumperCollection $parent The parent collection
*/
@@ -53,7 +55,7 @@ public function setParent(DumperCollection $parent)
}
/**
- * Returns the child routes
+ * Returns the child routes.
*
* @return array Array of DumperCollection|DumperRoute
*/
@@ -63,9 +65,10 @@ public function getRoutes()
}
/**
- * Gets a route by index
+ * Gets a route by index.
+ *
+ * @param int $index The index
*
- * @param int $index The index
* @return DumperCollection|DumperRoute The route at given index
*/
public function getRoute($index)
@@ -74,7 +77,7 @@ public function getRoute($index)
}
/**
- * Adds a route or route collection
+ * Adds a route or route collection.
*
* @param DumperCollection|DumperRoute The route or route collection
*/
@@ -87,9 +90,10 @@ public function addRoute($route)
}
/**
- * Returns true if the attribute is defined
+ * Returns true if the attribute is defined.
*
* @param string $name The attribute name
+ *
* @return Boolean true if the attribute is defined, false otherwise
*/
public function has($name)
@@ -98,23 +102,20 @@ public function has($name)
}
/**
- * Returns an attribute by name
+ * Returns an attribute by name.
+ *
+ * @param string $name The attribute name
+ * @param mixed $default Default value is the attribute doesn't exist
*
- * @param string $name The attribute name
- * @param mixed $default Default value is the attribute doesn't exist
* @return mixed The attribute value
*/
public function get($name, $default = null)
{
- if ($this->has($name)) {
- return $this->attributes[$name];
- } else {
- return $default;
- }
+ return $this->has($name) ? $this->attributes[$name] : $default;
}
/**
- * Sets an attribute by name
+ * Sets an attribute by name.
*
* @param string $name The attribute name
* @param mixed $value The attribute value
@@ -125,7 +126,7 @@ public function set($name, $value)
}
/**
- * Returns an iterator over the children
+ * Returns an iterator over the children.
*
* @return \Iterator The iterator
*/
@@ -135,11 +136,12 @@ public function getIterator()
}
/**
- * Clones the collection and its parents, up to the given parent
+ * Clones the collection and its parents, up to the given parent.
*
- * Children are reset
+ * Children are reset.
+ *
+ * @param DumperCollection $until If given, cloning will stop before this parent
*
- * @param DumperCollection $until If given, cloning will stop before this parent
* @return DumperCollection The cloned collection
*/
public function cloneHierarchyUntil(DumperCollection $until = null)
@@ -160,35 +162,27 @@ public function cloneHierarchyUntil(DumperCollection $until = null)
}
/**
- * Returns the root of the collection
+ * Returns the root of the collection.
*
* @return DumperCollection|null The root collection
*/
public function getRoot()
{
- if (null !== $parent = $this->parent) {
- return $parent->getRoot();
- } else {
- return $this;
- }
+ return (null !== $parent = $this->parent) ? $parent->getRoot() : $this;
}
/**
- * Returns an array of parent collections, from the closer parent to the root
+ * Returns an array of parent collections, from the closer parent to the root.
*
* @return array Array of DumperCollection parents
*/
public function getParents()
{
- if ($parent = $this->parent) {
- return array_merge(array($parent), $parent->getParents());
- } else {
- return array();
- }
+ return $this->parent ? array_merge(array($this->parent), $this->parent->getParents()) : array();
}
/**
- * Returns an array of this collection and its parents, from this collection to the root
+ * Returns an array of this collection and its parents, from this collection to the root.
*
* @return array Array of DumperCollection collections
*/
@@ -198,7 +192,7 @@ public function getParentsAndSelf()
}
/**
- * Returns a debug string representation of this collection
+ * Returns a debug string representation of this collection.
*
* @param Callable $toString Callback used to get the string representation of each individual child
* @param string $prefix String prepended to each line
@@ -216,4 +210,3 @@ public function toString($toString, $prefix)
return $string;
}
}
-
View
15 src/Symfony/Component/Routing/Matcher/Dumper/DumperPrefixCollection.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\Routing\Matcher\Dumper;
/**
- * Prefix tree of routes preserving routes order
+ * Prefix tree of routes preserving routes order.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*/
@@ -21,7 +21,7 @@ class DumperPrefixCollection extends DumperCollection
private $prefix;
/**
- * Returns the prefix
+ * Returns the prefix.
*
* @return string The prefix
*/
@@ -31,7 +31,7 @@ public function getPrefix()
}
/**
- * Sets the prefix
+ * Sets the prefix.
*
* @param string $prefix The prefix
*/
@@ -41,9 +41,10 @@ public function setPrefix($prefix)
}
/**
- * Adds a route in the tree
+ * Adds a route in the tree.
+ *
+ * @param DumperRoute $route The route
*
- * @param DumperRoute $route The route
* @return DumperPrefixCollection The node the route was added to
*/
public function addPrefixRoute(DumperRoute $route)
@@ -52,8 +53,8 @@ public function addPrefixRoute(DumperRoute $route)
if ($this->getPrefix() === $prefix) {
$this->addRoute($route);
- return $this;
+ return $this;
} else if ('' === $this->getPrefix() || 0 === strpos($prefix, $this->getPrefix())) {
$prev = $this;
for ($i = strlen($this->getPrefix()); $i < strlen($prefix); ++$i) {
@@ -66,10 +67,8 @@ public function addPrefixRoute(DumperRoute $route)
$collection->addRoute($route);
return $collection;
-
} else {
return $this->getParent()->addPrefixRoute($route);
}
}
}
-
View
10 src/Symfony/Component/Routing/Matcher/Dumper/DumperRoute.php
@@ -15,7 +15,7 @@
use Symfony\Component\Routing\RouteCollection;
/**
- * Container for a Route
+ * Container for a Route.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*/
@@ -26,6 +26,8 @@ class DumperRoute
private $parentCollection;
/**
+ * Constructor.
+ *
* @param string $name The route name
* @param Route $route The route
* @param RouteCollection $parentCollection The parent of the route
@@ -38,7 +40,7 @@ public function __construct($name, Route $route, RouteCollection $parentCollecti
}
/**
- * Returns the route name
+ * Returns the route name.
*
* @return string The route name
*/
@@ -48,7 +50,7 @@ public function getName()
}
/**
- * Returns the route
+ * Returns the route.
*
* @return Route The route
*/
@@ -58,7 +60,7 @@ public function getRoute()
}
/**
- * Returns the parent collection
+ * Returns the parent collection.
*
* @return RouteCollection the parent collection
*/
View
37 src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
@@ -130,13 +130,13 @@ private function countDirectChildRoutes(RouteCollection $routes)
private function compileRoutes(RouteCollection $routes, $supportsRedirections)
{
$code = '';
- $indent = 0;
$collections = $this->groupRoutesByHostnameRegex($routes)->getRoot();
$fetchedHostname = false;
foreach ($collections as $collection) {
- if ($regex = $collection->get('hostnameRegex')) {
+ $indent = 0;
+ if ($regex = $collection->get('hostname_regex')) {
if (!$fetchedHostname) {
$code .= " \$hostname = \$this->context->getHost();\n\n";
$fetchedHostname = true;
@@ -152,7 +152,6 @@ private function compileRoutes(RouteCollection $routes, $supportsRedirections)
$code .= $this->indentCode($lines, $indent);
if ($regex) {
- $indent = 0;
$code .= " }\n\n";
}
}
@@ -164,15 +163,11 @@ private function compilePrefixRoutes(DumperPrefixCollection $collection, $suppor
{
$code = '';
$indent = 0;
-
$prefix = $collection->getPrefix();
-
$optimizable = 1 < strlen($prefix) && 1 < count($collection->getRoutes());
-
$optimizedPrefix = $parentPrefix;
if ($optimizable) {
-
$optimizedPrefix = $prefix;
$code .= sprintf(" if (0 === strpos(\$pathinfo, %s)) {\n", var_export($prefix, true));
@@ -189,7 +184,6 @@ private function compilePrefixRoutes(DumperPrefixCollection $collection, $suppor
}
if ($optimizable) {
- $indent = 0;
$code .= " }\n\n";
}
@@ -307,7 +301,6 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren
// optimize parameters array
if (($matches || $hostnameMatches) && $compiledRoute->getDefaults()) {
-
$vars = array();
if ($matches) {
$vars[] = '$matches';
@@ -349,22 +342,22 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren
}
/**
- * Prepends the given number of spaces at the begining of each line
+ * Prepends the given number of spaces at the begining of each line.
*
* @param string $lines Lines of code
* @param int $width The number of spaces
+ *
* @return string Indented lines
*/
private function indentCode($lines, $width)
{
- return preg_replace('#^(?=.)#', str_repeat(' ', $width), $lines);
+ return preg_replace('#^(?=.)#m', str_repeat(' ', $width), $lines);
}
/**
- * Groups consecutive routes having the same hostnameRegex
+ * Groups consecutive routes having the same hostname regex.
*
- * The results is a collection of collections of routes having the same
- * hostnameRegex
+ * The results is a collection of collections of routes having the same hostname regex.
*/
private function groupRoutesByHostnameRegex(RouteCollection $routes, DumperCollection $root = null, DumperCollection $collection = null)
{
@@ -374,30 +367,24 @@ private function groupRoutesByHostnameRegex(RouteCollection $routes, DumperColle
if (null === $collection) {
$collection = new DumperCollection();
- $collection->set('hostnameRegex', null);
+ $collection->set('hostname_regex', null);
$root->addRoute($collection);
}
foreach ($routes as $name => $route) {
-
if ($route instanceof RouteCollection) {
-
$collection = $this->groupRoutesByHostnameRegex($route, $root, $collection);
-
} else {
-
$regex = $route->compile()->getHostnameRegex();
- if ($regex !== $collection->get('hostnameRegex')) {
-
+ if ($regex !== $collection->get('hostname_regex')) {
$collection = new DumperCollection();
- $collection->set('hostnameRegex', $regex);
+ $collection->set('hostname_regex', $regex);
$root->addRoute($collection);
}
$collection->addRoute(new DumperRoute($name, $route, $routes));
-
}
}
@@ -405,14 +392,14 @@ private function groupRoutesByHostnameRegex(RouteCollection $routes, DumperColle
}
/**
- * Organizes the routes into a prefix tree
+ * Organizes the routes into a prefix tree.
*
* Routes order is preserved such that traversing the tree will traverse the
* routes in the origin order
*/
private function buildPrefixTree(DumperCollection $collection)
{
- $tree = new DumperPrefixCollection;
+ $tree = new DumperPrefixCollection();
$tree->setPrefix('');
$current = $tree;
View
7 src/Symfony/Component/Routing/Matcher/UrlMatcher.php
@@ -132,6 +132,11 @@ protected function matchCollection($pathinfo, RouteCollection $routes)
if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
continue;
}
+ $hostnameMatches = array();
+
+ if ($compiledRoute->getHostnameRegex() && !preg_match($compiledRoute->getHostnameRegex(), $this->context->getHost(), $hostnameMatches)) {
+ continue;
+ }
// check HTTP method requirement
if ($req = $route->getRequirement('_method')) {
@@ -157,7 +162,7 @@ protected function matchCollection($pathinfo, RouteCollection $routes)
continue;
}
- return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name));
+ return array_merge($this->mergeDefaults($hostnameMatches + $matches, $route->getDefaults()), array('_route' => $name));
}
}
View
1  src/Symfony/Component/Routing/RouteCollection.php
@@ -352,5 +352,4 @@ public function setHostnamePattern($pattern)
}
}
}
-
}
View
9 src/Symfony/Component/Routing/RouteCompiler.php
@@ -39,7 +39,6 @@ public function compile(Route $route)
$hostnameTokens = array();
if (null !== $hostnamePattern = $route->getHostnamePattern()) {
-
$result = $this->compilePattern($route, $hostnamePattern, false);
$hostnameVariables = $result['variables'];
@@ -65,11 +64,11 @@ public function compile(Route $route)
$staticPrefix,
$regex,
$tokens,
- array_unique($variables),
$pathVariables,
- $hostnameVariables,
$hostnameRegex,
- $hostnameTokens
+ $hostnameTokens,
+ $hostnameVariables,
+ array_unique($variables)
);
}
@@ -160,7 +159,7 @@ private function compilePattern(Route $route, $pattern, $isPath)
private function computeRegexp(array $tokens, $index, $firstOptional)
{
$token = $tokens[$index];
- if('text' === $token[0]) {
+ if ('text' === $token[0]) {
// Text tokens
return preg_quote($token[1], self::REGEX_DELIMITER);
} else {
View
3  src/Symfony/Component/Routing/Tests/Annotation/RouteTest.php
@@ -39,7 +39,8 @@ public function getValidParameters()
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('defaults', array('_controller' => 'MyBlogBundle:Blog:index'), 'getDefaults'),
+ array('hostname_pattern', array('{locale}.example.com'), 'getHostnamePattern')
);
}
}
View
4 src/Symfony/Component/Routing/Tests/CompiledRouteTest.php
@@ -20,12 +20,12 @@ public function testAccessors()
{
$route = new Route('/{foo}', array('foo' => 'bar'), array('foo' => '\d+'), array('foo' => 'bar'));
- $compiled = new CompiledRoute($route, 'prefix', 'regex', array('tokens'), array('variables'));
+ $compiled = new CompiledRoute($route, 'prefix', 'regex', array('tokens'), array(), array(), array(), array(), array('variables'));
$this->assertEquals($route, $compiled->getRoute(), '__construct() takes a route as its first argument');
$this->assertEquals('prefix', $compiled->getStaticPrefix(), '__construct() takes a static prefix as its second argument');
$this->assertEquals('regex', $compiled->getRegex(), '__construct() takes a regexp as its third argument');
$this->assertEquals(array('tokens'), $compiled->getTokens(), '__construct() takes an array of tokens as its fourth argument');
- $this->assertEquals(array('variables'), $compiled->getVariables(), '__construct() takes an array of variables as its fifth argument');
+ $this->assertEquals(array('variables'), $compiled->getVariables(), '__construct() takes an array of variables as its ninth argument');
}
public function testgetPatterngetDefaultsgetOptionsgetRequirements()
View
5 src/Symfony/Component/Routing/Tests/Fixtures/validpattern.php
@@ -6,8 +6,9 @@
$collection->add('blog_show', new Route(
'/blog/{slug}',
array('_controller' => 'MyBlogBundle:Blog:show'),
- array(),
- array('compiler_class' => 'RouteCompiler')
+ array('_method' => 'GET'),
+ array('compiler_class' => 'RouteCompiler'),
+ '{locale}.example.com'
));
return $collection;
View
2  src/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml
@@ -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}">
+ <route id="blog_show" pattern="/blog/{slug}" hostname-pattern="{locale}.example.com">
<default key="_controller">MyBundle:Blog:show</default>
<requirement key="_method">GET</requirement>
<option key="compiler_class">RouteCompiler</option>
View
7 src/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml
@@ -1,6 +1,7 @@
blog_show:
- pattern: /blog/{slug}
- defaults: { _controller: MyBlogBundle:Blog:show }
+ pattern: /blog/{slug}
+ defaults: { _controller: MyBlogBundle:Blog:show }
+ hostname_pattern: "{locale}.example.com"
+ requirements: { 'foo': '\d+' }
options:
compiler_class: RouteCompiler
-
View
2  src/Symfony/Component/Routing/Tests/Fixtures/validresource.xml
@@ -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}">
+ <import resource="validpattern.xml" prefix="/{foo}" hostname-pattern="{locale}.example.com">
<default key="foo">foo</default>
<requirement key="foo">\d+</requirement>
<option key="foo">bar</option>
View
11 src/Symfony/Component/Routing/Tests/Fixtures/validresource.yml
@@ -1,6 +1,7 @@
blog_show:
- resource: validpattern.yml
- prefix: /{foo}
- defaults: { 'foo': 'foo' }
- requirements: { 'foo': '\d+' }
- options: { 'foo': 'bar' }
+ resource: validpattern.yml
+ prefix: /{foo}
+ defaults: { 'foo': 'foo' }
+ requirements: { 'foo': '\d+' }
+ options: { 'foo': 'bar' }
+ hostname_pattern: "{locale}.example.com"
View
1  src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
@@ -15,7 +15,6 @@
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper;
use Symfony\Component\Routing\RequestContext;
-use Symfony\Component\Routing\Matcher\Dumper\DumperCollection;
class PhpGeneratorDumperTest extends \PHPUnit_Framework_TestCase
{
View
21 src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
@@ -206,6 +206,27 @@ public function testWithAnIntegerAsADefaultValue()
$this->assertEquals('/app.php/foo', $this->getGenerator($routes)->generate('test', array('default' => 'foo')));
}
+ public function testWithHostnameDifferentFromContext()
+ {
+ $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
+
+ $this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', array('name' =>'Fabien', 'locale' => 'fr')));
+ }
+
+ public function testWithHostnameSameAsContext()
+ {
+ $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
+
+ $this->assertEquals('/app.php/Fabien', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', array('name' =>'Fabien', 'locale' => 'fr')));
+ }
+
+ public function testWithHostnameSameAsContextAndAbsolute()
+ {
+ $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
+
+ $this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', array('name' =>'Fabien', 'locale' => 'fr'), true));
+ }
+
protected function getGenerator(RouteCollection $routes, array $parameters = array())
{
$context = new RequestContext('/app.php');
View
4 src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
@@ -47,6 +47,10 @@ public function testLoadWithRoute()
$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('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
+ $this->assertEquals('GET', $route->getRequirement('_method'));
+ $this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
}
}
View
7 src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
@@ -48,6 +48,10 @@ public function testLoadWithRoute()
$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('MyBundle:Blog:show', $route->getDefault('_controller'));
+ $this->assertEquals('GET', $route->getRequirement('_method'));
+ $this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
}
@@ -59,9 +63,12 @@ public function testLoadWithImport()
$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('MyBundle:Blog:show', $routes['blog_show']->getDefault('_controller'));
$this->assertEquals('foo', $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());
}
/**
View
7 src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
@@ -88,6 +88,10 @@ public function testLoadWithPattern()
$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('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
+ $this->assertEquals('\d+', $route->getRequirement('foo'));
+ $this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
}
@@ -99,9 +103,12 @@ public function testLoadWithResource()
$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('MyBlogBundle:Blog:show', $routes['blog_show']->getDefault('_controller'));
$this->assertEquals('foo', $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());
}
/**
View
5 src/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php
@@ -42,8 +42,7 @@ public function testDump(RouteCollection $collection, $fixture, $options = array
$basePath = __DIR__.'/../../Fixtures/dumper/';
$dumper = new PhpMatcherDumper($collection, new RequestContext());
-
- file_put_contents('/tmp/' . $fixture, $dumper->dump($options));
+ file_put_contents('/tmp/'.$fixture, $dumper->dump($options));
$this->assertStringEqualsFile($basePath.$fixture, $dumper->dump($options), '->dump() correctly dumps routes as optimized PHP code.');
}
@@ -295,7 +294,7 @@ public function testGroupRoutesByHostnameRegex()
$string = $result->toString(function($route) {
if ($route instanceof DumperCollection) {
- $re = $route->get('hostnameRegex');
+ $re = $route->get('hostname_regex');
if (null !== $re) {
$re = str_replace(array("\n", " "), '', $re);
}
View
35 src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
@@ -225,4 +225,39 @@ public function testDecodeOnce()
$matcher = new UrlMatcher($coll, new RequestContext());
$this->assertEquals(array('foo' => 'bar%23', '_route' => 'foo'), $matcher->match('/foo/bar%2523'));
}
+
+ public function testWithHostname()
+ {
+ $coll = new RouteCollection();
+ $coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{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'));
+ }
+
+ public function testWithHostnameOnRouteCollection()
+ {
+ $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');
+
+ $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
+ $this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar'));
+
+ $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.net'));
+ $this->assertEquals(array('foo' => 'bar', '_route' => 'bar', 'locale' => 'en'), $matcher->match('/bar/bar'));
+ }
+
+ /**
+ * @expectedException Symfony\Component\Routing\Exception\ResourceNotFoundException
+ */
+ public function testWithOutHostnameHostnameDoesNotMatch()
+ {
+ $coll = new RouteCollection();
+ $coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com'));
+
+ $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'example.com'));
+ $matcher->match('/foo/bar');
+ }
}
View
14 src/Symfony/Component/Routing/Tests/RouteCollectionTest.php
@@ -276,4 +276,18 @@ public function testPatternDoesNotChangeWhenDefinitionOrderChanges()
$this->assertEquals($rootCollection_A, $rootCollection_B);
}
+
+ public function testSetHostnamePattern()
+ {
+ $collection = new RouteCollection();
+ $routea = new Route('/a');
+ $routeb = new Route('/b', array(), array(), array(), '{locale}.example.net');
+ $collection->add('a', $routea);
+ $collection->add('b', $routeb);
+
+ $collection->setHostnamePattern('{locale}.example.com');
+
+ $this->assertEquals('{locale}.example.com', $routea->getHostnamePattern());
+ $this->assertEquals('{locale}.example.net', $routeb->getHostnamePattern());
+ }
}
View
2  src/Symfony/Component/Routing/Tests/RouteCompilerTest.php
@@ -123,8 +123,6 @@ public function testCompileExtended($name, $arguments, $prefix, $regex, $variabl
$this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
$this->assertEquals($pathVariables, $compiled->getPathVariables(), $name.' (path variables)');
$this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
-
-
$this->assertEquals($hostnameRegex, str_replace(array("\n", ' '), '', $compiled->getHostnameRegex()), $name.' (hostname regex)');
$this->assertEquals($hostnameVariables, $compiled->getHostnameVariables(), $name.' (hostname variables)');
$this->assertEquals($hostnameTokens, $compiled->getHostnameTokens(), $name.' (hostname tokens)');
View
10 src/Symfony/Component/Routing/Tests/RouteTest.php
@@ -17,11 +17,12 @@ class RouteTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
- $route = new Route('/{foo}', array('foo' => 'bar'), array('foo' => '\d+'), array('foo' => 'bar'));
+ $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(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');
}
public function testPattern()
@@ -116,6 +117,13 @@ public function getInvalidRequirements()
);
}
+ public function testHostnamePattern()
+ {
+ $route = new Route('/');
+ $route->setHostnamePattern('{locale}.example.net');
+ $this->assertEquals('{locale}.example.net', $route->getHostnamePattern(), '->setHostnamePattern() sets the hostname pattern');
+ }
+
public function testCompile()
{
$route = new Route('/{foo}');
Something went wrong with that request. Please try again.