[Routing] Implement bug fixes and enhancements
merged branch vicb/routing_improvements (PR #3876)

Commits
-------

9307f5b [Routing] Implement bug fixes and enhancements

Discussion
----------

[Routing] Implement bug fixes and enhancements (from @Tobion)

This is mainly #3754 with some minor formatting changes.

Original PR message from @Tobion:

Here a list of what is fixed. Tests pass.

1. The `RouteCollection` states

    > it overrides existing routes with the same name defined in the instance or its children and parents.

    But this is not true for `->addCollection()` but only for `->add()`. addCollection does not remove routes with the same name in its parents (only in its children). I don't think this is on purpose.
    So I fixed it by making sure there can only be one route per name in all connected collections. This way we can also simplify `->get()` and `->remove()` and improve performance since we can stop iterating recursively when we found the first and only route with a given name.
    See `testUniqueRouteWithGivenName` that fails in the old code but works now.

2. There was an bug with `$collection->addPrefix('0');` that didn't apply the starting slash. Fixed and test case added.

3. There is an issue with `->get()` that I also think is not intended. Currently it allows to access a sub-RouteCollection by specifing $name as array integer index. But according to the PHPdoc you should only be allowed to receive a Route instance and not a RouteCollection.
    See `testGet` that has a failing test case. I fixed this behavior.

4. Then I recognized that `->addCollection` depended on the order of applying them. So

        $collection1->addCollection($collection2, '/b');
        $collection2->addCollection($collection3, '/c');
        $rootCollection->addCollection($collection1, '/a');

    had a different pattern result from

        $collection2->addCollection($collection3, '/c');
        $collection1->addCollection($collection2, '/b');
        $rootCollection->addCollection($collection1, '/a');

    Fixed and test case added. See `testPatternDoesNotChangeWhenDefinitionOrderChanges`.

5. PHP could have ended in an infinite loop when one tried to add an existing RouteCollection to the tree. Fixed by throwing an exception when this situation is detected. See tests `testCannotSelfJoinCollection` and `testCannotAddExistingCollectionToTree`.

6. I made `setParent()` private because its not useful outside the class itself. And `remove()` also removes the route from its parents. Added public `getRoot()` method.

7. The `Route` class throwed a PHP warning when trying to set an empty requirement.

8. Fixed issue #3777. See discussion there for more info. I fixed it by removing the over-optimization that was introduced in 91f4097a0905e but didn't work properly. One cannot reorder the route definitions, as is was done, because then the wrong route might me matched before the correct one. If one really wanted to do that, it would require to calculate the intersection of two regular expressions to determine if they can be grouped together (a tool that would also be useful to check whether a route is unreachable, see discussion in #3678) We can only safely optimize routes with a static prefix within a RouteCollection, not across multiple RouteCollections with different parents.  This is especially true when using variables and regular expressions requirements.
I could however apply an optimization that was missing yet: Collections with a single route were missing the static prefix optimization with `0 === strpos()`.

9. Fixed an issue where the `PhpMatcherDumper` would not apply the optimization if the root collection to be dumped has a prefix itself. For this I had to rewrite `compileRoutes`. It is also much easier to understand now. Addionally I added many comments and PHPdoc because complex recursive methods like this are still hard to grasp.
I added a test case for this (`url_matcher3.php`).

10. Fix that `Route::compile` needs to recompile a route once it is modified. Otherwise we have a wrong result. Test case added.
Added some bc breaks from the changelog into UPGRADE-2.1.md
merged branch jakzal/FilesystemMirrorCleanup (PR #3844)

Commits
-------

efad5d5 [Filesystem] Prevented infiite loop on windows while calling mirror on symlink. Added test for mirroring symlinks.

Discussion
----------

[Filesystem] Prevented infinite loop on windows while mirrorring symlinks

First check for filetype in *mirror()* method is:

    if (is_link($file)) {
        $this->symlink($file, $target);

later we see:

    } elseif (is_file($file) || ($copyOnWindows && is_link($file))) {
        $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);

The later check for links on windows (*$copyOnWindows && is_link($file)*) won't ever get called. Calling *symlink()* in *mirror()* on windows would lead to calling *mirror()* again.

Note that I didn't actually try running it on windows platform. I added a test for mirroring symlinks (non-windows test). I think it'd be good if someone added some windows specific tests to this class.

I also modified the target path:

    $target = $targetDir.'/'.str_replace($originDir.DIRECTORY_SEPARATOR, '', $file->getPathname());

It didn't use DIRECTORY_SEPARATOR and is equivalent to:

    $target = str_replace($originDir, $targetDir, $file->getPathname());

Bug fix: yes
Feature addition: no
Backwards compatibility break: no
Symfony2 tests pass: yes
Fixes the following tickets: ~
Todo: ~
merged branch hason/validator (PR #552)

Commits
-------

f9a486e [Validator] Added support for pluralization of the SizeLengthValidator
c0715f1 [FrameworkBundle], [TwigBundle] added support for form error message pluralization
7a6376e [Form] added support for error message pluralization
345981f [Validator] added support for plural messages

Discussion
----------

[Validator] Added support for plural error messages

Bug fix: no
Feature addition: yes
Backwards compatibility break: no
Symfony2 tests pass: yes
Todo: create translations for en and update others (FrameworkBundle)

[![Build Status](https://secure.travis-ci.org/hason/symfony.png?branch=validator)](http://travis-ci.org/hason/symfony)

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

by fabpot at 2011-05-14T20:41:01Z

@bschussek: What's your opinion?

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

by stof at 2011-09-04T13:14:29Z

@hason could you rebase your branch on top of master and update the PR ?

You also need to change the messages in the constraint that uses the pluralization to a pluralized format.

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

by stof at 2011-10-16T18:06:22Z

@hason ping

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

by stof at 2011-11-11T14:58:19Z

@hason ping again

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

by stof at 2011-12-12T20:39:10Z

@hason ping again. Can you update your PR ?

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

by hason at 2011-12-12T21:29:14Z

@stof I hope that I will update PR this week.

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

by bschussek at 2012-01-15T19:07:32Z

Looks good to me.

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

by canni at 2012-02-02T17:28:54Z

@hason can you update this PR and squash commits, it conflicts with current master

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

by hason at 2012-02-09T07:21:41Z

@stof, @canni Rebased.

What is the best solution for the translation of messages?

1. Change messages in the classes and all xliff files?
2. Keep messages in the classes and change all xliff files?

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

by stof at 2012-02-09T08:19:41Z

The constraints contain the en message so you will need to modify them to update the message

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

by hason at 2012-02-09T08:55:55Z

I prefer second option. The Validator component should be decoupled from the Translation component. The constraints contain the en message which is also the key for Translation component. We should create validators.en.xlf in the FrameworkBundle for en message. I think that this is better solution. What do you think?

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

by stof at 2012-04-04T02:22:02Z

@hason Please rebase your branch. It conflicts with master because of the move of the tests

@fabpot ping
merged branch richardmiller/bc_break_changes_in_upgrade (PR #3877)

Commits
-------

f23b4fa Added some bc breaks from the changelog into UPGRADE-2.1.md

Discussion
----------

Added some bc breaks from the changelog into UPGRADE-2.1.md
[Form] Fixed option support in Form component
[Form] Failing test for empty_data option BC break

This demonstrates the issue described in symfony/symfony#3354. FieldType no longer has access to the child type's data_class option, which makes it unable to create the default closure for empty_data.
[Form] Fixed typos
[Form] Moved calculation of ChoiceType options to closures
merged branch bschussek/issue3354 (PR #3789)

Commits
-------

8329087 [Form] Moved calculation of ChoiceType options to closures
5adec19 [Form] Fixed typos
cb87ccb [Form] Failing test for empty_data option BC break
b733045 [Form] Fixed option support in Form component

Discussion
----------

[Form] Fixed option support in Form component

Bug fix: yes
Feature addition: no
Backwards compatibility break: yes
Symfony2 tests pass: yes
Fixes the following tickets: #3354, #3512, #3685, #3694
Todo: -

![Travis Build Status](https://secure.travis-ci.org/bschussek/symfony.png?branch=issue3354)

This PR also introduces a new helper `DefaultOptions` for solving option graphs. It accepts default options to be defined on various layers of your class hierarchy. These options can then be merged with the options passed by the user. This is called *resolving*.

The important feature of this utility is that it lets you define *lazy options*. Lazy options are specified using closures that are evaluated when resolving and thus have access to the resolved values of other (potentially lazy) options. The class detects cyclic option dependencies and fails with an exception in this case.

For more information, check the inline documentation of the `DefaultOptions` class and the UPGRADE file.

@fabpot: Might this be worth a separate component? (in total the utility consists of five classes with two associated tests)

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

by beberlei at 2012-04-05T08:54:10Z

"The important feature of this utility is that it lets you define lazy options. Lazy options are specified using closures"

What about options that are closures? are those differentiated?

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

by bschussek at 2012-04-05T08:57:35Z

@beberlei Yes. Closures for lazy options receive a Symfony\Component\Form\Options instance as first argument. All other closures are interpreted as normal values.

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

by stof at 2012-04-05T11:09:49Z

I'm wondering if these classes should go in the Config component. My issue with it is that it would add a required dependency to the Config component and that the Config component mixes many different things in it already (the loader part, the resource part, the definition part...)

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

by sstok at 2012-04-06T13:36:36Z

Sharing the Options class would be great, and its more then one class so why not give it its own Component folder?
Filesystem is just one class, and that has its own folder.

Great job on the class bschussek :clap:

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

by bschussek at 2012-04-10T12:32:34Z

@fabpot Any input?

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

by bschussek at 2012-04-10T13:54:13Z

@fabpot Apart from the decision about the final location of DefaultOptions et al., could you merge this soon? This would make my work a bit easier since this one is a blocker.

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

by fabpot at 2012-04-10T18:08:18Z

@bschussek: Can you rebase on master? I will merge afterwards. Thanks.