Pārlūkot izejas kodu

Updating vendors

Julio Montoya 12 gadi atpakaļ
vecāks
revīzija
bae4ded345
92 mainītis faili ar 7606 papildinājumiem un 892 dzēšanām
  1. 1 0
      composer.json
  2. 146 37
      composer.lock
  3. 3 2
      main/inc/global.inc.php
  4. 3 0
      main/inc/lib/internationalization.lib.php
  5. 13 4
      main/inc/lib/main_api.lib.php
  6. 8 2
      main/inc/lib/template.lib.php
  7. 14 5
      main/newscorm/learnpath.class.php
  8. 5 4
      main/newscorm/learnpathList.class.php
  9. 11 5
      main/newscorm/lp_list.php
  10. 25 8
      src/ChamiloLMS/Controller/LearnpathController.php
  11. 1 1
      vendor/autoload.php
  12. 2 0
      vendor/composer/autoload_namespaces.php
  13. 3 3
      vendor/composer/autoload_real.php
  14. 194 609
      vendor/composer/installed.json
  15. 2 2
      vendor/gedmo/doctrine-extensions/doc/uploadable.md
  16. 8 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CHANGELOG.md
  17. 492 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php
  18. 325 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Cookie.php
  19. 215 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CookieJar.php
  20. 110 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/History.php
  21. 19 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/LICENSE
  22. 23 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/README.md
  23. 138 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Request.php
  24. 138 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Response.php
  25. 450 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ClientTest.php
  26. 191 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieJarTest.php
  27. 177 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieTest.php
  28. 101 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/HistoryTest.php
  29. 53 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/RequestTest.php
  30. 76 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ResponseTest.php
  31. 39 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/composer.json
  32. 30 0
      vendor/symfony/browser-kit/Symfony/Component/BrowserKit/phpunit.xml.dist
  33. 0 1
      vendor/symfony/console/Symfony/Component/Console/Input/ArgvInput.php
  34. 7 1
      vendor/symfony/console/Symfony/Component/Console/Input/StringInput.php
  35. 8 4
      vendor/symfony/console/Symfony/Component/Console/Tests/Input/StringInputTest.php
  36. 6 1
      vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/ContainerBuilder.php
  37. 18 8
      vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
  38. 21 0
      vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
  39. 2 2
      vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php
  40. 18 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/CHANGELOG.md
  41. 732 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php
  42. 307 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/ChoiceFormField.php
  43. 106 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FileFormField.php
  44. 122 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FormField.php
  45. 47 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/InputFormField.php
  46. 39 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/TextareaFormField.php
  47. 395 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php
  48. 216 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/FormFieldRegistry.php
  49. 19 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/LICENSE
  50. 162 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php
  51. 32 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/README.md
  52. 618 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/CrawlerTest.php
  53. 329 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php
  54. 97 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php
  55. 38 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTest.php
  56. 27 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTestCase.php
  57. 49 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php
  58. 33 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php
  59. 8 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Fixtures/windows-1250.html
  60. 691 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/FormTest.php
  61. 110 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/LinkTest.php
  62. 37 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/composer.json
  63. 30 0
      vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/phpunit.xml.dist
  64. 1 1
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Profiler/Profiler.php
  65. 8 2
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Profiler/RedisProfilerStorage.php
  66. 13 0
      vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Tests/Profiler/Mock/RedisMock.php
  67. 4 4
      vendor/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php
  68. 4 14
      vendor/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
  69. 22 2
      vendor/symfony/routing/Symfony/Component/Routing/RouteCollection.php
  70. 1 1
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_id.xml
  71. 1 1
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/namespaceprefix.xml
  72. 1 2
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml
  73. 1 1
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml
  74. 6 6
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.php
  75. 3 3
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml
  76. 4 4
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml
  77. 1 1
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.xml
  78. 2 2
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.yml
  79. 6 6
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
  80. 21 20
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
  81. 16 14
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
  82. 2 1
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php
  83. 19 12
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php
  84. 6 14
      vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
  85. 78 63
      vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCollectionTest.php
  86. 6 0
      vendor/symfony/translation/Symfony/Component/Translation/CHANGELOG.md
  87. 1 1
      vendor/symfony/translation/Symfony/Component/Translation/README.md
  88. 10 10
      vendor/symfony/translation/Symfony/Component/Translation/Tests/TranslatorTest.php
  89. 27 1
      vendor/symfony/translation/Symfony/Component/Translation/Translator.php
  90. 1 1
      vendor/symfony/validator/Symfony/Component/Validator/Validator.php
  91. 0 5
      vendor/symfony/yaml/Symfony/Component/Yaml/Inline.php
  92. 1 1
      vendor/twig/twig/doc/filters/merge.rst

+ 1 - 0
composer.json

@@ -35,6 +35,7 @@
         "symfony/validator": "dev-master",
         "symfony/translation": "dev-master",
         "symfony/dependency-injection": "dev-master",
+        "symfony/browser-kit": "2.3.*@dev",
         "dflydev/doctrine-orm-service-provider": "1.0.*@dev",
         "doctrine/migrations": "dev-master",
         "swiftmailer/swiftmailer": ">=4.1.2,<4.2-dev",

+ 146 - 37
composer.lock

@@ -1,5 +1,5 @@
 {
-    "hash": "a25ed0df7070c9362315f4f00dcb9d79",
+    "hash": "0db490911bde31defbbbcf0e1b97ea2a",
     "packages": [
         {
             "name": "dflydev/doctrine-orm-service-provider",
@@ -726,12 +726,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/l3pp4rd/DoctrineExtensions.git",
-                "reference": "v2.3.5"
+                "reference": "a9300c270f30c45eca8604d53b2f4d021526ae5f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/l3pp4rd/DoctrineExtensions/zipball/v2.3.5",
-                "reference": "v2.3.5",
+                "url": "https://api.github.com/repos/l3pp4rd/DoctrineExtensions/zipball/a9300c270f30c45eca8604d53b2f4d021526ae5f",
+                "reference": "a9300c270f30c45eca8604d53b2f4d021526ae5f",
                 "shasum": ""
             },
             "require": {
@@ -797,7 +797,7 @@
                 "tree",
                 "uploadable"
             ],
-            "time": "2013-03-05 06:32:22"
+            "time": "2013-03-07 01:32:18"
         },
         {
             "name": "knplabs/knp-menu",
@@ -1198,6 +1198,61 @@
             ],
             "time": "2012-06-17 13:55:34"
         },
+        {
+            "name": "symfony/browser-kit",
+            "version": "dev-master",
+            "target-dir": "Symfony/Component/BrowserKit",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/BrowserKit.git",
+                "reference": "63ea9f3b5ca08ddbd336cd9957c2409fcb435757"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/BrowserKit/zipball/63ea9f3b5ca08ddbd336cd9957c2409fcb435757",
+                "reference": "63ea9f3b5ca08ddbd336cd9957c2409fcb435757",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/dom-crawler": ">=2.0,<3.0"
+            },
+            "require-dev": {
+                "symfony/css-selector": ">=2.0,<3.0",
+                "symfony/process": ">=2.0,<3.0"
+            },
+            "suggest": {
+                "symfony/process": "2.2.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\BrowserKit\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony BrowserKit Component",
+            "homepage": "http://symfony.com",
+            "time": "2013-02-23 06:47:02"
+        },
         {
             "name": "symfony/config",
             "version": "dev-master",
@@ -1252,12 +1307,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Console.git",
-                "reference": "fae82dad073a491b91a2ad6cda2ef5d8b19a5152"
+                "reference": "aa28bbe2c3c4210f9e9f44df16d71d63c53adf9b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Console/zipball/fae82dad073a491b91a2ad6cda2ef5d8b19a5152",
-                "reference": "fae82dad073a491b91a2ad6cda2ef5d8b19a5152",
+                "url": "https://api.github.com/repos/symfony/Console/zipball/aa28bbe2c3c4210f9e9f44df16d71d63c53adf9b",
+                "reference": "aa28bbe2c3c4210f9e9f44df16d71d63c53adf9b",
                 "shasum": ""
             },
             "require": {
@@ -1290,7 +1345,7 @@
             ],
             "description": "Symfony Console Component",
             "homepage": "http://symfony.com",
-            "time": "2013-03-01 06:43:27"
+            "time": "2013-03-06 17:50:21"
         },
         {
             "name": "symfony/dependency-injection",
@@ -1299,12 +1354,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/DependencyInjection.git",
-                "reference": "75d281bbec94203e464a9d62a644687e15a6005a"
+                "reference": "33cc43f36c025453497c99e532e83c30e2455e41"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/75d281bbec94203e464a9d62a644687e15a6005a",
-                "reference": "75d281bbec94203e464a9d62a644687e15a6005a",
+                "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/33cc43f36c025453497c99e532e83c30e2455e41",
+                "reference": "33cc43f36c025453497c99e532e83c30e2455e41",
                 "shasum": ""
             },
             "require": {
@@ -1345,7 +1400,7 @@
             ],
             "description": "Symfony DependencyInjection Component",
             "homepage": "http://symfony.com",
-            "time": "2013-02-18 21:28:20"
+            "time": "2013-03-06 17:58:06"
         },
         {
             "name": "symfony/doctrine-bridge",
@@ -1413,6 +1468,59 @@
             "homepage": "http://symfony.com",
             "time": "2013-02-23 06:47:02"
         },
+        {
+            "name": "symfony/dom-crawler",
+            "version": "dev-master",
+            "target-dir": "Symfony/Component/DomCrawler",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/DomCrawler.git",
+                "reference": "35fe9f9c57421bf70ce9a43c614f41d295b4d984"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/DomCrawler/zipball/35fe9f9c57421bf70ce9a43c614f41d295b4d984",
+                "reference": "35fe9f9c57421bf70ce9a43c614f41d295b4d984",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "symfony/css-selector": ">=2.0,<3.0"
+            },
+            "suggest": {
+                "symfony/css-selector": "2.2.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\DomCrawler\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony DomCrawler Component",
+            "homepage": "http://symfony.com",
+            "time": "2013-03-06 19:37:18"
+        },
         {
             "name": "symfony/event-dispatcher",
             "version": "2.1.x-dev",
@@ -1571,12 +1679,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/HttpKernel.git",
-                "reference": "4d378fda500c49f41a3d09370af270ce5817d567"
+                "reference": "5e54625f1852936f59141474f31f91a1af21805d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/4d378fda500c49f41a3d09370af270ce5817d567",
-                "reference": "4d378fda500c49f41a3d09370af270ce5817d567",
+                "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/5e54625f1852936f59141474f31f91a1af21805d",
+                "reference": "5e54625f1852936f59141474f31f91a1af21805d",
                 "shasum": ""
             },
             "require": {
@@ -1624,7 +1732,7 @@
             ],
             "description": "Symfony HttpKernel Component",
             "homepage": "http://symfony.com",
-            "time": "2013-02-23 21:57:49"
+            "time": "2013-03-06 19:10:14"
         },
         {
             "name": "symfony/locale",
@@ -1796,12 +1904,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Routing.git",
-                "reference": "v2.2.0-RC3"
+                "reference": "3a97436e581bae44d0eec454a74bbb4f93593e78"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.2.0-RC3",
-                "reference": "v2.2.0-RC3",
+                "url": "https://api.github.com/repos/symfony/Routing/zipball/3a97436e581bae44d0eec454a74bbb4f93593e78",
+                "reference": "3a97436e581bae44d0eec454a74bbb4f93593e78",
                 "shasum": ""
             },
             "require": {
@@ -1845,7 +1953,7 @@
             ],
             "description": "Symfony Routing Component",
             "homepage": "http://symfony.com",
-            "time": "2013-02-11 11:24:47"
+            "time": "2013-03-06 19:31:19"
         },
         {
             "name": "symfony/security",
@@ -1914,12 +2022,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Translation.git",
-                "reference": "10164d84c407fd9ac5918172f86a4b94bf2b9c0b"
+                "reference": "890f05917b8fd603b19a0ea8111f8b1e04429029"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Translation/zipball/10164d84c407fd9ac5918172f86a4b94bf2b9c0b",
-                "reference": "10164d84c407fd9ac5918172f86a4b94bf2b9c0b",
+                "url": "https://api.github.com/repos/symfony/Translation/zipball/890f05917b8fd603b19a0ea8111f8b1e04429029",
+                "reference": "890f05917b8fd603b19a0ea8111f8b1e04429029",
                 "shasum": ""
             },
             "require": {
@@ -1960,7 +2068,7 @@
             ],
             "description": "Symfony Translation Component",
             "homepage": "http://symfony.com",
-            "time": "2013-02-11 10:13:11"
+            "time": "2013-03-06 21:28:09"
         },
         {
             "name": "symfony/twig-bridge",
@@ -2035,12 +2143,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Validator.git",
-                "reference": "a307f75f9fa64489428b870b7aec7c6469710f31"
+                "reference": "45eec4f9fad28cf637ff375b9eb30ac296f0361e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Validator/zipball/a307f75f9fa64489428b870b7aec7c6469710f31",
-                "reference": "a307f75f9fa64489428b870b7aec7c6469710f31",
+                "url": "https://api.github.com/repos/symfony/Validator/zipball/45eec4f9fad28cf637ff375b9eb30ac296f0361e",
+                "reference": "45eec4f9fad28cf637ff375b9eb30ac296f0361e",
                 "shasum": ""
             },
             "require": {
@@ -2087,7 +2195,7 @@
             ],
             "description": "Symfony Validator Component",
             "homepage": "http://symfony.com",
-            "time": "2013-03-01 10:42:20"
+            "time": "2013-03-06 17:50:21"
         },
         {
             "name": "symfony/yaml",
@@ -2096,12 +2204,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/Yaml.git",
-                "reference": "v2.1.8"
+                "reference": "783c78dcee45111f529582470b31fd682baa63c1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.1.8",
-                "reference": "v2.1.8",
+                "url": "https://api.github.com/repos/symfony/Yaml/zipball/783c78dcee45111f529582470b31fd682baa63c1",
+                "reference": "783c78dcee45111f529582470b31fd682baa63c1",
                 "shasum": ""
             },
             "require": {
@@ -2129,7 +2237,7 @@
             ],
             "description": "Symfony Yaml Component",
             "homepage": "http://symfony.com",
-            "time": "2013-01-27 16:12:43"
+            "time": "2013-03-06 17:01:03"
         },
         {
             "name": "twig/twig",
@@ -2137,12 +2245,12 @@
             "source": {
                 "type": "git",
                 "url": "https://github.com/fabpot/Twig.git",
-                "reference": "39d94faabcea7d60b90c2996d51e1b6691ab39d2"
+                "reference": "b6c2171579b4ab8c193322796ef757a185729cb8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/fabpot/Twig/zipball/39d94faabcea7d60b90c2996d51e1b6691ab39d2",
-                "reference": "39d94faabcea7d60b90c2996d51e1b6691ab39d2",
+                "url": "https://api.github.com/repos/fabpot/Twig/zipball/b6c2171579b4ab8c193322796ef757a185729cb8",
+                "reference": "b6c2171579b4ab8c193322796ef757a185729cb8",
                 "shasum": ""
             },
             "require": {
@@ -2178,7 +2286,7 @@
             "keywords": [
                 "templating"
             ],
-            "time": "2013-02-28 14:08:49"
+            "time": "2013-03-07 06:57:14"
         }
     ],
     "packages-dev": null,
@@ -2197,6 +2305,7 @@
         "symfony/validator": 20,
         "symfony/translation": 20,
         "symfony/dependency-injection": 20,
+        "symfony/browser-kit": 20,
         "dflydev/doctrine-orm-service-provider": 20,
         "doctrine/migrations": 20,
         "pagerfanta/pagerfanta": 20,

+ 3 - 2
main/inc/global.inc.php

@@ -236,6 +236,8 @@ $app['translator'] = $app->share($app->extend('translator', function($translator
 // Classic way or Controllers ways
 $app['classic_layout'] = false;
 
+$app['breadcrumb'] = array();
+
 //Form provider
 $app->register(new Silex\Provider\FormServiceProvider());
 /*
@@ -832,8 +834,7 @@ $app->get('/userportal', 'userportal.controller:indexAction');
 $app->get('/logout', 'index.controller:logoutAction');
 
 //LP controller
-$app->get('/learnpath/subscribe_users/{id}', 'learnpath.controller:indexAction')->bind('subscribe_users');
-$app->post('/learnpath/subscribe_users/{id}', 'learnpath.controller:indexAction')->bind('subscribe_users');
+$app->match('/learnpath/subscribe_users/{lpId}', 'learnpath.controller:indexAction', 'GET|POST')->bind('subscribe_users');
 
 //$app->mount('/', 'index.controller');
 return $app;

+ 3 - 0
main/inc/lib/internationalization.lib.php

@@ -129,6 +129,9 @@ function get_lang($variable, $reserved = null, $language = null)
     $translated = $app['translator']->trans($variable);
     if ($translated == $variable) {
         $translated = $app['translator']->trans("lang$variable");
+        if ($translated == "lang$variable") {
+            return $variable;
+        }
     }
     return $translated;
 

+ 13 - 4
main/inc/lib/main_api.lib.php

@@ -3158,18 +3158,29 @@ function api_get_datetime($time = null) {
  * @param int       The session ID (optional)
  * @return int      -1 on error, 0 if invisible, 1 if visible
  */
-function api_get_item_visibility($_course, $tool, $id, $session=0) {
+function api_get_item_visibility($_course, $tool, $id, $session = 0, $user_id = null, $type = null) {
     if (!is_array($_course) || count($_course) == 0 || empty($tool) || empty($id)) { return -1; }
     $tool = Database::escape_string($tool);
     $id = Database::escape_string($id);
     $session = (int) $session;
     $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
     $course_id	 = $_course['real_id'];
+    $user_condition = null;
+    if (!empty($user_id)) {
+        $user_id = intval($user_id);
+        $user_condition = " AND to_user_id = $user_id ";
+    }
+    $type_condition = null;
+    if (!empty($type)) {
+        $type = Database::escape_string($type);
+        $type_condition = " AND lastedit_type = '$type' ";
+    }
+
     $sql = "SELECT visibility FROM $TABLE_ITEMPROPERTY
     		WHERE 	c_id = $course_id AND
     				tool = '$tool' AND
     				ref = $id AND
-    				(id_session = $session OR id_session = 0)
+    				(id_session = $session OR id_session = 0) $user_condition $type_condition
     		ORDER BY id_session DESC, lastedit_date DESC";
     $res = Database::query($sql);
     if ($res === false || Database::num_rows($res) == 0) { return -1; }
@@ -3402,9 +3413,7 @@ function api_get_item_property_id($course_code, $tool, $ref) {
 }
 
 /**
- *
  * Inserts a record in the track_e_item_property table (No update)
- *
  */
 
 function api_track_item_property_update($tool, $ref, $title, $content, $progress) {

+ 8 - 2
main/inc/lib/template.lib.php

@@ -492,6 +492,14 @@ class Template
      */
     private function set_header_parameters() {
         global $interbreadcrumb;
+
+        if (isset($this->app['breadcrumb']) && !empty($this->app['breadcrumb'])) {
+            if (empty($interbreadcrumb)) {
+                $interbreadcrumb = $this->app['breadcrumb'];
+            } else {
+                $interbreadcrumb = array_merge($interbreadcrumb, $this->app['breadcrumb']);
+            }
+        }
         $_course = api_get_course_info();
         $_configuration = $this->app['configuration'];
         $this_section = $this->app['this_section'];
@@ -1278,8 +1286,6 @@ class Template
         return $return;
     }
 
-
-
     function return_breadcrumb($interbreadcrumb)
     {
         $session_id = api_get_session_id();

+ 14 - 5
main/newscorm/learnpath.class.php

@@ -116,8 +116,7 @@ class learnpath
                 $this->course_info = $course_info;
                 $course_id = $course_info['real_id'];
             } else {
-                $this->error = 'Course code does not exist in database ('.$sql.')';
-
+                $this->error = 'Course code does not exist in database';
                 return false;
             }
         }
@@ -2339,20 +2338,23 @@ class learnpath
         $lp_id = (int)$lp_id;
         $course_info = api_get_course_info($course_code);
         $tbl_learnpath = Database :: get_course_table(TABLE_LP_MAIN);
+
         // Get current prerequisite
-        $sql = "SELECT id, prerequisite, publicated_on, expired_on FROM $tbl_learnpath WHERE c_id = ".$course_info['real_id']." AND id = $lp_id";
+        $sql = "SELECT id, prerequisite, subscribe_users, publicated_on, expired_on
+                FROM $tbl_learnpath WHERE c_id = ".$course_info['real_id']." AND id = $lp_id";
         $rs = Database::query($sql);
         $now = time();
+        $session_id = api_get_session_id();
         if (Database::num_rows($rs) > 0) {
             $row = Database::fetch_array($rs, 'ASSOC');
             $prerequisite = $row['prerequisite'];
+
             $is_visible = true;
             $progress = 0;
 
             //Checking LP prerequisites
             if (!empty($prerequisite)) {
-                $progress = self::get_db_progress($prerequisite, $student_id, '%', '', false, api_get_session_id());
-                $progress = intval($progress);
+                $progress = intval(self::get_db_progress($prerequisite, $student_id, '%', '', false, $session_id));
                 if ($progress < 100) {
                     $is_visible = false;
                 }
@@ -2386,6 +2388,13 @@ class learnpath
                 }
             }
 
+            if (isset($row['subscribe_users']) && $row['subscribe_users'] == 1 ) {
+                $visibility = api_get_item_visibility($course_info, 'learnpath', $row['id'], $session_id, $student_id, 'LearnpathSubscription');
+                if ($visibility == -1) {
+                    return false;
+                }
+            }
+
             return $is_visible;
         }
 

+ 5 - 4
main/newscorm/learnpathList.class.php

@@ -30,7 +30,7 @@ class learnpathList {
      * @param	int			Optional session id (otherwise we use api_get_session_id())
      * @return	void
      */
-    function __construct($user_id, $course_code = '', $session_id = null, $order_by = null, $check_publication_dates = false, $filter_by_category = null) {
+    function __construct($user_id, $course_code = null, $session_id = null, $order_by = null, $check_publication_dates = false, $filter_by_category = null) {
         $course_info = api_get_course_info($course_code);
         $lp_table = Database::get_course_table(TABLE_LP_MAIN);
         $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
@@ -85,7 +85,7 @@ class learnpathList {
             // is done using domesticate()
             $myname = domesticate($row['name']);
             $mylink = 'newscorm/lp_controller.php?action=view&lp_id='.$row['id'].'&id_session='.$session_id;
-            $sql2="SELECT * FROM $tbl_tool WHERE c_id = $course_id AND (name='$myname' and image='scormbuilder.gif' and link LIKE '$mylink%')";
+            $sql2 = "SELECT * FROM $tbl_tool WHERE c_id = $course_id AND (name='$myname' and image='scormbuilder.gif' and link LIKE '$mylink%')";
             //error_log('New LP - learnpathList::__construct - getting visibility - '.$sql2, 0);
             $res2 = Database::query($sql2);
             if (Database::num_rows($res2) > 0) {
@@ -94,7 +94,8 @@ class learnpathList {
             } else {
                 $pub = 'i';
             }
-            // Check if visible.
+
+            // Check if visible
             $vis = api_get_item_visibility(api_get_course_info($course_code), 'learnpath', $row['id'], $session_id);
 
             if (!empty($row['created_on']) && $row['created_on'] != '0000-00-00 00:00:00') {
@@ -135,7 +136,7 @@ class learnpathList {
                 'lp_visibility'     => $vis,
                 'lp_published'	    => $pub,
                 'lp_prevent_reinit' => $row['prevent_reinit'],
-                'seriousgame_mode' => $row['seriousgame_mode'],
+                'seriousgame_mode'  => $row['seriousgame_mode'],
                 'lp_scorm_debug'    => $row['debug'],
                 'lp_display_order'  => $row['display_order'],
                 'lp_preview_image'  => stripslashes($row['preview_image']),

+ 11 - 5
main/newscorm/lp_list.php

@@ -230,9 +230,9 @@ foreach ($categories as $item) {
                 $my_title = Display::tag('font', $name, array('style' => 'color:grey'));
             }
 
-            $dsp_line = '<tr align="center" class="'.$oddclass.'">'.
-                '<td align="left" valign="top">'.Display::return_icon('learnpath.png', get_lang('LPName'), '', ICON_SIZE_SMALL).'
-                     <a href="'.$url_start_lp.'">'.$my_title.'</a>'.$session_img.$extra."</td>";
+            $dsp_line = '<tr align="center" class="'.$oddclass.'">
+                            <td align="left" valign="top">'.Display::return_icon('learnpath.png', get_lang('LPName'), '', ICON_SIZE_SMALL).'
+                            <a href="'.$url_start_lp.'">'.$my_title.'</a>'.$session_img.$extra."</td>";
 
             $dsp_desc = '';
             $dsp_export = '';
@@ -242,6 +242,13 @@ foreach ($categories as $item) {
             $dsp_default_view = '';
             $dsp_debug = '';
             $dsp_order = '';
+            $lp_auto_lunch_icon = '';
+            $copy = null;
+            $dsp_disk = null;
+            $dsp_reinit = null;
+            $subscribe_users = null;
+            $dsp_publish = null;
+            $dsp_edit_lp = null;
 
             $progress = learnpath::get_db_progress($id, api_get_user_id(), '%', '', false, api_get_session_id());
 
@@ -383,11 +390,10 @@ foreach ($categories as $item) {
                 //Subscribe users
                 $subscribe_users = null;
                 if ($details['subscribe_users'] == 1) {
-                    $subscribe_users = Display::url(Display::return_icon('add.png', get_lang('AddUsers')), api_get_path(WEB_PATH)."learnpath/subscribe_users/$id");
+                    $subscribe_users = Display::url(Display::return_icon('add.png', get_lang('AddUsers')), api_get_path(WEB_PUBLIC_PATH)."learnpath/subscribe_users/$id");
                 }
 
                 /* Auto Lunch LP code */
-                $lp_auto_lunch_icon = '';
                 if (api_get_course_setting('enable_lp_auto_launch') == 1) {
                     if ($details['autolaunch'] == 1 && $autolaunch_exists == false) {
                         $autolaunch_exists = true;

+ 25 - 8
src/ChamiloLMS/Controller/LearnpathController.php

@@ -6,19 +6,36 @@ use Symfony\Component\HttpFoundation\Response;
 
 class LearnpathController {
 
-    function indexAction(Application $app, $id) {
+    /**
+     * Index
+     *
+     * @param \Silex\Application $app
+     * @param int $lpId
+     *
+     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
+     */
+    public function indexAction(Application $app, $lpId)
+    {
         $request = $app['request'];
+        $courseCode = api_get_course_id();
+        $lp = new \learnpath($courseCode, $lpId, api_get_user_id());
 
-        $sessionId = api_get_session_id();
+        $breadcrumb = array(
+            array('url' => api_get_path(WEB_CODE_PATH).'newscorm/lp_controller.php?action=list', 'name' => get_lang('LearningPaths')),
+            array('url' => api_get_path(WEB_CODE_PATH)."newscorm/lp_controller.php?action=build&lp_id=".$lp->get_id(), 'name' => $lp->get_name()),
+            array('url' => '#', 'name' => get_lang('SubscribeUsers'))
+        );
+
+        $app['breadcrumb'] = $breadcrumb;
 
-        $lpId  = $id;
+        $sessionId = api_get_session_id();
+        $courseId = api_get_course_int_id();
 
-        if (empty($lpId)) {
-            var_dump($lpId);
-            //return $app->redirect('lp_controller.php');
+        if (empty($courseId)) {
+            $app->abort(403, 'course_not_available');
         }
 
-        $course = $app['orm.em']->getRepository('Entity\EntityCourse')->find(api_get_course_int_id());
+        $course = $app['orm.em']->getRepository('Entity\EntityCourse')->find($courseId);
 
         $subscribedUsers = $app['orm.em']->getRepository('Entity\EntityCourse')->getSubscribedStudents($course);
 
@@ -76,7 +93,7 @@ class LearnpathController {
             $data = $request->get('form');
             $destination = isset($data['destination']) ? $data['destination'] : array();
             $app['orm.em']->getRepository('Entity\EntityCItemProperty')->SubscribedUsersToItem('learnpath', $course, $sessionId, $lpId, $destination);
-            return $app->redirect($app['url_generator']->generate('subscribe_users', array('lp_id' => $lpId)));
+            return $app->redirect($app['url_generator']->generate('subscribe_users', array ('lpId' => $lpId)));
         } else {
             $app['template']->assign('form', $form->createView());
         }

+ 1 - 1
vendor/autoload.php

@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer' . '/autoload_real.php';
 
-return ComposerAutoloaderInitcdfd4dba8f133654da821bebfcae84b3::getLoader();
+return ComposerAutoloaderInit307b7e14ed20493499a65f7933fe4c9c::getLoader();

+ 2 - 0
vendor/composer/autoload_namespaces.php

@@ -19,9 +19,11 @@ return array(
     'Symfony\\Component\\HttpFoundation' => $vendorDir . '/symfony/http-foundation/',
     'Symfony\\Component\\Form\\' => $vendorDir . '/symfony/form/',
     'Symfony\\Component\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/',
+    'Symfony\\Component\\DomCrawler\\' => $vendorDir . '/symfony/dom-crawler/',
     'Symfony\\Component\\DependencyInjection\\' => $vendorDir . '/symfony/dependency-injection/',
     'Symfony\\Component\\Console\\' => $vendorDir . '/symfony/console/',
     'Symfony\\Component\\Config\\' => $vendorDir . '/symfony/config/',
+    'Symfony\\Component\\BrowserKit\\' => $vendorDir . '/symfony/browser-kit/',
     'Symfony\\Bridge\\Twig\\' => $vendorDir . '/symfony/twig-bridge/',
     'Symfony\\Bridge\\Doctrine\\' => $vendorDir . '/symfony/doctrine-bridge/',
     'Silex' => $vendorDir . '/silex/silex/src/',

+ 3 - 3
vendor/composer/autoload_real.php

@@ -2,7 +2,7 @@
 
 // autoload_real.php generated by Composer
 
-class ComposerAutoloaderInitcdfd4dba8f133654da821bebfcae84b3
+class ComposerAutoloaderInit307b7e14ed20493499a65f7933fe4c9c
 {
     private static $loader;
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInitcdfd4dba8f133654da821bebfcae84b3
             return self::$loader;
         }
 
-        spl_autoload_register(array('ComposerAutoloaderInitcdfd4dba8f133654da821bebfcae84b3', 'loadClassLoader'));
+        spl_autoload_register(array('ComposerAutoloaderInit307b7e14ed20493499a65f7933fe4c9c', 'loadClassLoader'));
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
-        spl_autoload_unregister(array('ComposerAutoloaderInitcdfd4dba8f133654da821bebfcae84b3', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInit307b7e14ed20493499a65f7933fe4c9c', 'loadClassLoader'));
 
         $vendorDir = dirname(__DIR__);
         $baseDir = dirname($vendorDir);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 194 - 609
vendor/composer/installed.json


+ 2 - 2
vendor/gedmo/doctrine-extensions/doc/uploadable.md

@@ -299,7 +299,7 @@ Entity\File:
 
 $listener->setDefaultPath('/my/app/web/upload');
 
-if (isset($_FILES['images']) && is_array($_FILES['images']) {
+if (isset($_FILES['images']) && is_array($_FILES['images'])) {
     foreach ($_FILES['images'] as $fileInfo) {
         $file = new File();
 
@@ -447,4 +447,4 @@ way:
 ``` php
 $listener->setMimeTypeGuesser(new MyCustomMimeTypeGuesser());
 
-```
+```

+ 8 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CHANGELOG.md

@@ -0,0 +1,8 @@
+CHANGELOG
+=========
+
+2.1.0
+-----
+
+ * [BC BREAK] The CookieJar internals have changed to allow cookies with the
+   same name on different sub-domains/sub-paths

+ 492 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Client.php

@@ -0,0 +1,492 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+use Symfony\Component\DomCrawler\Crawler;
+use Symfony\Component\DomCrawler\Link;
+use Symfony\Component\DomCrawler\Form;
+use Symfony\Component\Process\PhpProcess;
+
+/**
+ * Client simulates a browser.
+ *
+ * To make the actual request, you need to implement the doRequest() method.
+ *
+ * If you want to be able to run requests in their own process (insulated flag),
+ * you need to also implement the getScript() method.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+abstract class Client
+{
+    protected $history;
+    protected $cookieJar;
+    protected $server;
+    protected $request;
+    protected $response;
+    protected $crawler;
+    protected $insulated;
+    protected $redirect;
+    protected $followRedirects;
+
+    /**
+     * Constructor.
+     *
+     * @param array     $server    The server parameters (equivalent of $_SERVER)
+     * @param History   $history   A History instance to store the browser history
+     * @param CookieJar $cookieJar A CookieJar instance to store the cookies
+     *
+     * @api
+     */
+    public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null)
+    {
+        $this->setServerParameters($server);
+        $this->history = null === $history ? new History() : $history;
+        $this->cookieJar = null === $cookieJar ? new CookieJar() : $cookieJar;
+        $this->insulated = false;
+        $this->followRedirects = true;
+    }
+
+    /**
+     * Sets whether to automatically follow redirects or not.
+     *
+     * @param Boolean $followRedirect Whether to follow redirects
+     *
+     * @api
+     */
+    public function followRedirects($followRedirect = true)
+    {
+        $this->followRedirects = (Boolean) $followRedirect;
+    }
+
+    /**
+     * Sets the insulated flag.
+     *
+     * @param Boolean $insulated Whether to insulate the requests or not
+     *
+     * @throws \RuntimeException When Symfony Process Component is not installed
+     *
+     * @api
+     */
+    public function insulate($insulated = true)
+    {
+        if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) {
+            // @codeCoverageIgnoreStart
+            throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.');
+            // @codeCoverageIgnoreEnd
+        }
+
+        $this->insulated = (Boolean) $insulated;
+    }
+
+    /**
+     * Sets server parameters.
+     *
+     * @param array $server An array of server parameters
+     *
+     * @api
+     */
+    public function setServerParameters(array $server)
+    {
+        $this->server = array_merge(array(
+            'HTTP_HOST'       => 'localhost',
+            'HTTP_USER_AGENT' => 'Symfony2 BrowserKit',
+        ), $server);
+    }
+
+    /**
+     * Sets single server parameter.
+     *
+     * @param string $key   A key of the parameter
+     * @param string $value A value of the parameter
+     */
+    public function setServerParameter($key, $value)
+    {
+        $this->server[$key] = $value;
+    }
+
+    /**
+     * Gets single server parameter for specified key.
+     *
+     * @param string $key     A key of the parameter to get
+     * @param string $default A default value when key is undefined
+     *
+     * @return string A value of the parameter
+     */
+    public function getServerParameter($key, $default = '')
+    {
+        return (isset($this->server[$key])) ? $this->server[$key] : $default;
+    }
+
+    /**
+     * Returns the History instance.
+     *
+     * @return History A History instance
+     *
+     * @api
+     */
+    public function getHistory()
+    {
+        return $this->history;
+    }
+
+    /**
+     * Returns the CookieJar instance.
+     *
+     * @return CookieJar A CookieJar instance
+     *
+     * @api
+     */
+    public function getCookieJar()
+    {
+        return $this->cookieJar;
+    }
+
+    /**
+     * Returns the current Crawler instance.
+     *
+     * @return Crawler A Crawler instance
+     *
+     * @api
+     */
+    public function getCrawler()
+    {
+        return $this->crawler;
+    }
+
+    /**
+     * Returns the current Response instance.
+     *
+     * @return Response A Response instance
+     *
+     * @api
+     */
+    public function getResponse()
+    {
+        return $this->response;
+    }
+
+    /**
+     * Returns the current Request instance.
+     *
+     * @return Request A Request instance
+     *
+     * @api
+     */
+    public function getRequest()
+    {
+        return $this->request;
+    }
+
+    /**
+     * Clicks on a given link.
+     *
+     * @param Link $link A Link instance
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function click(Link $link)
+    {
+        if ($link instanceof Form) {
+            return $this->submit($link);
+        }
+
+        return $this->request($link->getMethod(), $link->getUri());
+    }
+
+    /**
+     * Submits a form.
+     *
+     * @param Form  $form   A Form instance
+     * @param array $values An array of form field values
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function submit(Form $form, array $values = array())
+    {
+        $form->setValues($values);
+
+        return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles());
+    }
+
+    /**
+     * Calls a URI.
+     *
+     * @param string  $method        The request method
+     * @param string  $uri           The URI to fetch
+     * @param array   $parameters    The Request parameters
+     * @param array   $files         The files
+     * @param array   $server        The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does)
+     * @param string  $content       The raw body data
+     * @param Boolean $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true)
+    {
+        $uri = $this->getAbsoluteUri($uri);
+
+        $server = array_merge($this->server, $server);
+        if (!$this->history->isEmpty()) {
+            $server['HTTP_REFERER'] = $this->history->current()->getUri();
+        }
+        $server['HTTP_HOST'] = parse_url($uri, PHP_URL_HOST);
+        $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME);
+
+        $request = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content);
+
+        $this->request = $this->filterRequest($request);
+
+        if (true === $changeHistory) {
+            $this->history->add($request);
+        }
+
+        if ($this->insulated) {
+            $this->response = $this->doRequestInProcess($this->request);
+        } else {
+            $this->response = $this->doRequest($this->request);
+        }
+
+        $response = $this->filterResponse($this->response);
+
+        $this->cookieJar->updateFromResponse($response);
+
+        $this->redirect = $response->getHeader('Location');
+
+        if ($this->followRedirects && $this->redirect) {
+            return $this->crawler = $this->followRedirect();
+        }
+
+        return $this->crawler = $this->createCrawlerFromContent($request->getUri(), $response->getContent(), $response->getHeader('Content-Type'));
+    }
+
+    /**
+     * Makes a request in another process.
+     *
+     * @param Request $request A Request instance
+     *
+     * @return Response A Response instance
+     *
+     * @throws \RuntimeException When processing returns exit code
+     */
+    protected function doRequestInProcess($request)
+    {
+        // We set the TMPDIR (for Macs) and TEMP (for Windows), because on these platforms the temp directory changes based on the user.
+        $process = new PhpProcess($this->getScript($request), null, array('TMPDIR' => sys_get_temp_dir(), 'TEMP' => sys_get_temp_dir()));
+        $process->run();
+
+        if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) {
+            throw new \RuntimeException('OUTPUT: '.$process->getOutput().' ERROR OUTPUT: '.$process->getErrorOutput());
+        }
+
+        return unserialize($process->getOutput());
+    }
+
+    /**
+     * Makes a request.
+     *
+     * @param Request $request A Request instance
+     *
+     * @return Response A Response instance
+     */
+    abstract protected function doRequest($request);
+
+    /**
+     * Returns the script to execute when the request must be insulated.
+     *
+     * @param Request $request A Request instance
+     *
+     * @throws \LogicException When this abstract class is not implemented
+     */
+    protected function getScript($request)
+    {
+        // @codeCoverageIgnoreStart
+        throw new \LogicException('To insulate requests, you need to override the getScript() method.');
+        // @codeCoverageIgnoreEnd
+    }
+
+    /**
+     * Filters the request.
+     *
+     * @param Request $request The request to filter
+     *
+     * @return Request
+     */
+    protected function filterRequest(Request $request)
+    {
+        return $request;
+    }
+
+    /**
+     * Filters the Response.
+     *
+     * @param Response $response The Response to filter
+     *
+     * @return Response
+     */
+    protected function filterResponse($response)
+    {
+        return $response;
+    }
+
+    /**
+     * Creates a crawler.
+     *
+     * This method returns null if the DomCrawler component is not available.
+     *
+     * @param string $uri     A uri
+     * @param string $content Content for the crawler to use
+     * @param string $type    Content type
+     *
+     * @return Crawler|null
+     */
+    protected function createCrawlerFromContent($uri, $content, $type)
+    {
+        if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
+            return null;
+        }
+
+        $crawler = new Crawler(null, $uri);
+        $crawler->addContent($content, $type);
+
+        return $crawler;
+    }
+
+    /**
+     * Goes back in the browser history.
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function back()
+    {
+        return $this->requestFromRequest($this->history->back(), false);
+    }
+
+    /**
+     * Goes forward in the browser history.
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function forward()
+    {
+        return $this->requestFromRequest($this->history->forward(), false);
+    }
+
+    /**
+     * Reloads the current browser.
+     *
+     * @return Crawler
+     *
+     * @api
+     */
+    public function reload()
+    {
+        return $this->requestFromRequest($this->history->current(), false);
+    }
+
+    /**
+     * Follow redirects?
+     *
+     * @return Crawler
+     *
+     * @throws \LogicException If request was not a redirect
+     *
+     * @api
+     */
+    public function followRedirect()
+    {
+        if (empty($this->redirect)) {
+            throw new \LogicException('The request was not redirected.');
+        }
+
+        return $this->request('get', $this->redirect);
+    }
+
+    /**
+     * Restarts the client.
+     *
+     * It flushes history and all cookies.
+     *
+     * @api
+     */
+    public function restart()
+    {
+        $this->cookieJar->clear();
+        $this->history->clear();
+    }
+
+    /**
+     * Takes a URI and converts it to absolute if it is not already absolute.
+     *
+     * @param string $uri A uri
+     *
+     * @return string An absolute uri
+     */
+    protected function getAbsoluteUri($uri)
+    {
+        // already absolute?
+        if (0 === strpos($uri, 'http')) {
+            return $uri;
+        }
+
+        if (!$this->history->isEmpty()) {
+            $currentUri = $this->history->current()->getUri();
+        } else {
+            $currentUri = sprintf('http%s://%s/',
+                isset($this->server['HTTPS']) ? 's' : '',
+                isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost'
+            );
+        }
+
+        // anchor?
+        if (!$uri || '#' == $uri[0]) {
+            return preg_replace('/#.*?$/', '', $currentUri).$uri;
+        }
+
+        if ('/' !== $uri[0]) {
+            $path = parse_url($currentUri, PHP_URL_PATH);
+
+            if ('/' !== substr($path, -1)) {
+                $path = substr($path, 0, strrpos($path, '/') + 1);
+            }
+
+            $uri = $path.$uri;
+        }
+
+        return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri;
+    }
+
+    /**
+     * Makes a request from a Request object directly.
+     *
+     * @param Request $request       A Request instance
+     * @param Boolean $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
+     *
+     * @return Crawler
+     */
+    protected function requestFromRequest(Request $request, $changeHistory = true)
+    {
+        return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory);
+    }
+}

+ 325 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Cookie.php

@@ -0,0 +1,325 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * Cookie represents an HTTP cookie.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Cookie
+{
+    /**
+     * Handles dates as defined by RFC 2616 section 3.3.1, and also some other
+     * non-standard, but common formats.
+     *
+     * @var array
+     */
+    private static $dateFormats = array(
+        'D, d M Y H:i:s T',
+        'D, d-M-y H:i:s T',
+        'D, d-M-Y H:i:s T',
+        'D, d-m-y H:i:s T',
+        'D, d-m-Y H:i:s T',
+        'D M j G:i:s Y',
+        'D M d H:i:s Y T',
+    );
+
+    protected $name;
+    protected $value;
+    protected $expires;
+    protected $path;
+    protected $domain;
+    protected $secure;
+    protected $httponly;
+    protected $rawValue;
+
+    /**
+     * Sets a cookie.
+     *
+     * @param string  $name         The cookie name
+     * @param string  $value        The value of the cookie
+     * @param string  $expires      The time the cookie expires
+     * @param string  $path         The path on the server in which the cookie will be available on
+     * @param string  $domain       The domain that the cookie is available
+     * @param Boolean $secure       Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client
+     * @param Boolean $httponly     The cookie httponly flag
+     * @param Boolean $encodedValue Whether the value is encoded or not
+     *
+     * @api
+     */
+    public function __construct($name, $value, $expires = null, $path = null, $domain = '', $secure = false, $httponly = true, $encodedValue = false)
+    {
+        if ($encodedValue) {
+            $this->value    = urldecode($value);
+            $this->rawValue = $value;
+        } else {
+            $this->value    = $value;
+            $this->rawValue = urlencode($value);
+        }
+        $this->name     = $name;
+        $this->expires  = null === $expires ? null : (integer) $expires;
+        $this->path     = empty($path) ? '/' : $path;
+        $this->domain   = $domain;
+        $this->secure   = (Boolean) $secure;
+        $this->httponly = (Boolean) $httponly;
+    }
+
+    /**
+     * Returns the HTTP representation of the Cookie.
+     *
+     * @return string The HTTP representation of the Cookie
+     *
+     * @api
+     */
+    public function __toString()
+    {
+        $cookie = sprintf('%s=%s', $this->name, $this->rawValue);
+
+        if (null !== $this->expires) {
+            $cookie .= '; expires='.substr(\DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT'))->format(self::$dateFormats[0]), 0, -5);
+        }
+
+        if ('' !== $this->domain) {
+            $cookie .= '; domain='.$this->domain;
+        }
+
+        if ('/' !== $this->path) {
+            $cookie .= '; path='.$this->path;
+        }
+
+        if ($this->secure) {
+            $cookie .= '; secure';
+        }
+
+        if ($this->httponly) {
+            $cookie .= '; httponly';
+        }
+
+        return $cookie;
+    }
+
+    /**
+     * Creates a Cookie instance from a Set-Cookie header value.
+     *
+     * @param string $cookie A Set-Cookie header value
+     * @param string $url    The base URL
+     *
+     * @return Cookie A Cookie instance
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @api
+     */
+    public static function fromString($cookie, $url = null)
+    {
+        $parts = explode(';', $cookie);
+
+        if (false === strpos($parts[0], '=')) {
+            throw new \InvalidArgumentException('The cookie string "%s" is not valid.');
+        }
+
+        list($name, $value) = explode('=', array_shift($parts), 2);
+
+        $values = array(
+            'name'     => trim($name),
+            'value'    => trim($value),
+            'expires'  => null,
+            'path'     => '/',
+            'domain'   => '',
+            'secure'   => false,
+            'httponly' => false,
+            'passedRawValue' => true,
+        );
+
+        if (null !== $url) {
+            if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host']) || !isset($urlParts['path'])) {
+                throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url));
+            }
+            $parts = array_merge($urlParts, $parts);
+
+            $values['domain'] = $parts['host'];
+            $values['path'] = substr($parts['path'], 0, strrpos($parts['path'], '/'));
+        }
+
+        foreach ($parts as $part) {
+            $part = trim($part);
+
+            if ('secure' === strtolower($part)) {
+                // Ignore the secure flag if the original URI is not given or is not HTTPS
+                if (!$url || !isset($urlParts['scheme']) || 'https' != $urlParts['scheme']) {
+                    continue;
+                }
+
+                $values['secure'] = true;
+
+                continue;
+            }
+
+            if ('httponly' === strtolower($part)) {
+                $values['httponly'] = true;
+
+                continue;
+            }
+
+            if (2 === count($elements = explode('=', $part, 2))) {
+                if ('expires' === strtolower($elements[0])) {
+                    $elements[1] = self::parseDate($elements[1]);
+                }
+
+                $values[strtolower($elements[0])] = $elements[1];
+            }
+        }
+
+        return new static(
+            $values['name'],
+            $values['value'],
+            $values['expires'],
+            $values['path'],
+            $values['domain'],
+            $values['secure'],
+            $values['httponly'],
+            $values['passedRawValue']
+        );
+    }
+
+    private static function parseDate($dateValue)
+    {
+        // trim single quotes around date if present
+        if (($length = strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length-1]) {
+            $dateValue = substr($dateValue, 1, -1);
+        }
+
+        foreach (self::$dateFormats as $dateFormat) {
+            if (false !== $date = \DateTime::createFromFormat($dateFormat, $dateValue, new \DateTimeZone('GMT'))) {
+                return $date->getTimestamp();
+            }
+        }
+
+        // attempt a fallback for unusual formatting
+        if (false !== $date = date_create($dateValue, new \DateTimeZone('GMT'))) {
+            return $date->getTimestamp();
+        }
+
+        throw new \InvalidArgumentException(sprintf('Could not parse date "%s".', $dateValue));
+    }
+
+    /**
+     * Gets the name of the cookie.
+     *
+     * @return string The cookie name
+     *
+     * @api
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the value of the cookie.
+     *
+     * @return string The cookie value
+     *
+     * @api
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Gets the raw value of the cookie.
+     *
+     * @return string The cookie value
+     *
+     * @api
+     */
+    public function getRawValue()
+    {
+        return $this->rawValue;
+    }
+
+    /**
+     * Gets the expires time of the cookie.
+     *
+     * @return string The cookie expires time
+     *
+     * @api
+     */
+    public function getExpiresTime()
+    {
+        return $this->expires;
+    }
+
+    /**
+     * Gets the path of the cookie.
+     *
+     * @return string The cookie path
+     *
+     * @api
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Gets the domain of the cookie.
+     *
+     * @return string The cookie domain
+     *
+     * @api
+     */
+    public function getDomain()
+    {
+        return $this->domain;
+    }
+
+    /**
+     * Returns the secure flag of the cookie.
+     *
+     * @return Boolean The cookie secure flag
+     *
+     * @api
+     */
+    public function isSecure()
+    {
+        return $this->secure;
+    }
+
+    /**
+     * Returns the httponly flag of the cookie.
+     *
+     * @return Boolean The cookie httponly flag
+     *
+     * @api
+     */
+    public function isHttpOnly()
+    {
+        return $this->httponly;
+    }
+
+    /**
+     * Returns true if the cookie has expired.
+     *
+     * @return Boolean true if the cookie has expired, false otherwise
+     *
+     * @api
+     */
+    public function isExpired()
+    {
+        return null !== $this->expires && 0 !== $this->expires && $this->expires < time();
+    }
+}

+ 215 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/CookieJar.php

@@ -0,0 +1,215 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * CookieJar.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class CookieJar
+{
+    protected $cookieJar = array();
+
+    /**
+     * Sets a cookie.
+     *
+     * @param Cookie $cookie A Cookie instance
+     *
+     * @api
+     */
+    public function set(Cookie $cookie)
+    {
+        $this->cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
+    }
+
+    /**
+     * Gets a cookie by name.
+     *
+     * @param string $name   The cookie name
+     * @param string $path   The cookie path
+     * @param string $domain The cookie domain
+     *
+     * @return Cookie|null A Cookie instance or null if the cookie does not exist
+     *
+     * @api
+     */
+    public function get($name, $path = '/', $domain = null)
+    {
+        $this->flushExpiredCookies();
+
+        return isset($this->cookieJar[$domain][$path][$name]) ? $this->cookieJar[$domain][$path][$name] : null;
+    }
+
+    /**
+     * Removes a cookie by name.
+     *
+     * @param string $name   The cookie name
+     * @param string $path   The cookie path
+     * @param string $domain The cookie domain
+     *
+     * @api
+     */
+    public function expire($name, $path = '/', $domain = null)
+    {
+        if (null === $path) {
+            $path = '/';
+        }
+
+        unset($this->cookieJar[$domain][$path][$name]);
+
+        if (empty($this->cookieJar[$domain][$path])) {
+            unset($this->cookieJar[$domain][$path]);
+
+            if (empty($this->cookieJar[$domain])) {
+                unset($this->cookieJar[$domain]);
+            }
+        }
+    }
+
+    /**
+     * Removes all the cookies from the jar.
+     *
+     * @api
+     */
+    public function clear()
+    {
+        $this->cookieJar = array();
+    }
+
+    /**
+     * Updates the cookie jar from a response Set-Cookie headers.
+     *
+     * @param array  $setCookies Set-Cookie headers from an HTTP response
+     * @param string $uri        The base URL
+     */
+    public function updateFromSetCookie(array $setCookies, $uri = null)
+    {
+        $cookies = array();
+
+        foreach ($setCookies as $cookie) {
+            foreach (explode(',', $cookie) as $i => $part) {
+                if (0 === $i || preg_match('/^(?P<token>\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) {
+                    $cookies[] = ltrim($part);
+                } else {
+                    $cookies[count($cookies) - 1] .= ','.$part;
+                }
+            }
+        }
+
+        foreach ($cookies as $cookie) {
+            $this->set(Cookie::fromString($cookie, $uri));
+        }
+    }
+
+    /**
+     * Updates the cookie jar from a Response object.
+     *
+     * @param Response $response A Response object
+     * @param string   $uri      The base URL
+     */
+    public function updateFromResponse(Response $response, $uri = null)
+    {
+        $this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri);
+    }
+
+    /**
+     * Returns not yet expired cookies.
+     *
+     * @return Cookie[] An array of cookies
+     */
+    public function all()
+    {
+        $this->flushExpiredCookies();
+
+        $flattenedCookies = array();
+        foreach ($this->cookieJar as $path) {
+            foreach ($path as $cookies) {
+                foreach ($cookies as $cookie) {
+                    $flattenedCookies[] = $cookie;
+                }
+            }
+        }
+
+        return $flattenedCookies;
+    }
+
+    /**
+     * Returns not yet expired cookie values for the given URI.
+     *
+     * @param string  $uri             A URI
+     * @param Boolean $returnsRawValue Returns raw value or urldecoded value
+     *
+     * @return array An array of cookie values
+     */
+    public function allValues($uri, $returnsRawValue = false)
+    {
+        $this->flushExpiredCookies();
+
+        $parts = array_replace(array('path' => '/'), parse_url($uri));
+        $cookies = array();
+        foreach ($this->cookieJar as $domain => $pathCookies) {
+            if ($domain) {
+                $domain = ltrim($domain, '.');
+                if ($domain != substr($parts['host'], -strlen($domain))) {
+                    continue;
+                }
+            }
+
+            foreach ($pathCookies as $path => $namedCookies) {
+                if ($path != substr($parts['path'], 0, strlen($path))) {
+                    continue;
+                }
+
+                foreach ($namedCookies as $cookie) {
+                    if ($cookie->isSecure() && 'https' != $parts['scheme']) {
+                        continue;
+                    }
+
+                    $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue();
+                }
+            }
+        }
+
+        return $cookies;
+    }
+
+    /**
+     * Returns not yet expired raw cookie values for the given URI.
+     *
+     * @param string $uri A URI
+     *
+     * @return array An array of cookie values
+     */
+    public function allRawValues($uri)
+    {
+        return $this->allValues($uri, true);
+    }
+
+    /**
+     * Removes all expired cookies.
+     */
+    public function flushExpiredCookies()
+    {
+        foreach ($this->cookieJar as $domain => $pathCookies) {
+            foreach ($pathCookies as $path => $namedCookies) {
+                foreach ($namedCookies as $name => $cookie) {
+                    if ($cookie->isExpired()) {
+                        unset($this->cookieJar[$domain][$path][$name]);
+                    }
+                }
+            }
+        }
+    }
+}

+ 110 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/History.php

@@ -0,0 +1,110 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * History.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class History
+{
+    protected $stack = array();
+    protected $position = -1;
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        $this->clear();
+    }
+
+    /**
+     * Clears the history.
+     */
+    public function clear()
+    {
+        $this->stack = array();
+        $this->position = -1;
+    }
+
+    /**
+     * Adds a Request to the history.
+     *
+     * @param Request $request A Request instance
+     */
+    public function add(Request $request)
+    {
+        $this->stack = array_slice($this->stack, 0, $this->position + 1);
+        $this->stack[] = clone $request;
+        $this->position = count($this->stack) - 1;
+    }
+
+    /**
+     * Returns true if the history is empty.
+     *
+     * @return Boolean true if the history is empty, false otherwise
+     */
+    public function isEmpty()
+    {
+        return count($this->stack) == 0;
+    }
+
+    /**
+     * Goes back in the history.
+     *
+     * @return Request A Request instance
+     *
+     * @throws \LogicException if the stack is already on the first page
+     */
+    public function back()
+    {
+        if ($this->position < 1) {
+            throw new \LogicException('You are already on the first page.');
+        }
+
+        return clone $this->stack[--$this->position];
+    }
+
+    /**
+     * Goes forward in the history.
+     *
+     * @return Request A Request instance
+     *
+     * @throws \LogicException if the stack is already on the last page
+     */
+    public function forward()
+    {
+        if ($this->position > count($this->stack) - 2) {
+            throw new \LogicException('You are already on the last page.');
+        }
+
+        return clone $this->stack[++$this->position];
+    }
+
+    /**
+     * Returns the current element in the history.
+     *
+     * @return Request A Request instance
+     *
+     * @throws \LogicException if the stack is empty
+     */
+    public function current()
+    {
+        if (-1 == $this->position) {
+            throw new \LogicException('The page history is empty.');
+        }
+
+        return clone $this->stack[$this->position];
+    }
+}

+ 19 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2004-2013 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 23 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/README.md

@@ -0,0 +1,23 @@
+BrowserKit Component
+====================
+
+BrowserKit simulates the behavior of a web browser.
+
+The component only provide an abstract client and does not provide any
+"default" backend for the HTTP layer.
+
+Resources
+---------
+
+For a simple implementation of a browser based on an HTTP layer, have a look
+at [Goutte](https://github.com/fabpot/Goutte).
+
+For an implementation based on HttpKernelInterface, have a look at the
+[Client](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Client.php)
+provided by the HttpKernel component.
+
+You can run the unit tests with the following command:
+
+    $ cd path/to/Symfony/Component/BrowserKit/
+    $ composer.phar install --dev
+    $ phpunit

+ 138 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Request.php

@@ -0,0 +1,138 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * Request object.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Request
+{
+    protected $uri;
+    protected $method;
+    protected $parameters;
+    protected $files;
+    protected $cookies;
+    protected $server;
+    protected $content;
+
+    /**
+     * Constructor.
+     *
+     * @param string $uri        The request URI
+     * @param string $method     The HTTP method request
+     * @param array  $parameters The request parameters
+     * @param array  $files      An array of uploaded files
+     * @param array  $cookies    An array of cookies
+     * @param array  $server     An array of server parameters
+     * @param string $content    The raw body data
+     *
+     * @api
+     */
+    public function __construct($uri, $method, array $parameters = array(), array $files = array(), array $cookies = array(), array $server = array(), $content = null)
+    {
+        $this->uri = $uri;
+        $this->method = $method;
+        $this->parameters = $parameters;
+        $this->files = $files;
+        $this->cookies = $cookies;
+        $this->server = $server;
+        $this->content = $content;
+    }
+
+    /**
+     * Gets the request URI.
+     *
+     * @return string The request URI
+     *
+     * @api
+     */
+    public function getUri()
+    {
+        return $this->uri;
+    }
+
+    /**
+     * Gets the request HTTP method.
+     *
+     * @return string The request HTTP method
+     *
+     * @api
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * Gets the request parameters.
+     *
+     * @return array The request parameters
+     *
+     * @api
+     */
+    public function getParameters()
+    {
+        return $this->parameters;
+    }
+
+    /**
+     * Gets the request server files.
+     *
+     * @return array The request files
+     *
+     * @api
+     */
+    public function getFiles()
+    {
+        return $this->files;
+    }
+
+    /**
+     * Gets the request cookies.
+     *
+     * @return array The request cookies
+     *
+     * @api
+     */
+    public function getCookies()
+    {
+        return $this->cookies;
+    }
+
+    /**
+     * Gets the request server parameters.
+     *
+     * @return array The request server parameters
+     *
+     * @api
+     */
+    public function getServer()
+    {
+        return $this->server;
+    }
+
+    /**
+     * Gets the request raw body data.
+     *
+     * @return string The request raw body data.
+     *
+     * @api
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+}

+ 138 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Response.php

@@ -0,0 +1,138 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit;
+
+/**
+ * Response object.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Response
+{
+    protected $content;
+    protected $status;
+    protected $headers;
+
+    /**
+     * Constructor.
+     *
+     * The headers array is a set of key/value pairs. If a header is present multiple times
+     * then the value is an array of all the values.
+     *
+     * @param string  $content The content of the response
+     * @param integer $status  The response status code
+     * @param array   $headers An array of headers
+     *
+     * @api
+     */
+    public function __construct($content = '', $status = 200, array $headers = array())
+    {
+        $this->content = $content;
+        $this->status  = $status;
+        $this->headers = $headers;
+    }
+
+    /**
+     * Converts the response object to string containing all headers and the response content.
+     *
+     * @return string The response with headers and content
+     */
+    public function __toString()
+    {
+        $headers = '';
+        foreach ($this->headers as $name => $value) {
+            if (is_string($value)) {
+                $headers .= $this->buildHeader($name, $value);
+            } else {
+                foreach ($value as $headerValue) {
+                    $headers .= $this->buildHeader($name, $headerValue);
+                }
+            }
+        }
+
+        return $headers."\n".$this->content;
+    }
+
+    /**
+     * Returns the build header line.
+     *
+     * @param string $name  The header name
+     * @param string $value The header value
+     *
+     * @return string The built header line
+     */
+    protected function buildHeader($name, $value)
+    {
+        return sprintf("%s: %s\n", $name, $value);
+    }
+
+    /**
+     * Gets the response content.
+     *
+     * @return string The response content
+     *
+     * @api
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    /**
+     * Gets the response status code.
+     *
+     * @return integer The response status code
+     *
+     * @api
+     */
+    public function getStatus()
+    {
+        return $this->status;
+    }
+
+    /**
+     * Gets the response headers.
+     *
+     * @return array The response headers
+     *
+     * @api
+     */
+    public function getHeaders()
+    {
+        return $this->headers;
+    }
+
+    /**
+     * Gets a response header.
+     *
+     * @param string  $header The header name
+     * @param Boolean $first  Whether to return the first value or all header values
+     *
+     * @return string|array The first header value if $first is true, an array of values otherwise
+     */
+    public function getHeader($header, $first = true)
+    {
+        foreach ($this->headers as $key => $value) {
+            if (str_replace('-', '_', strtolower($key)) == str_replace('-', '_', strtolower($header))) {
+                if ($first) {
+                    return is_array($value) ? (count($value) ? $value[0] : '') : $value;
+                }
+
+                return is_array($value) ? $value : array($value);
+            }
+        }
+
+        return $first ? null : array();
+    }
+}

+ 450 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ClientTest.php

@@ -0,0 +1,450 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\Client;
+use Symfony\Component\BrowserKit\History;
+use Symfony\Component\BrowserKit\CookieJar;
+use Symfony\Component\BrowserKit\Request;
+use Symfony\Component\BrowserKit\Response;
+
+class TestClient extends Client
+{
+    protected $nextResponse = null;
+    protected $nextScript = null;
+
+    public function setNextResponse(Response $response)
+    {
+        $this->nextResponse = $response;
+    }
+
+    public function setNextScript($script)
+    {
+        $this->nextScript = $script;
+    }
+
+    protected function doRequest($request)
+    {
+        if (null === $this->nextResponse) {
+            return new Response();
+        }
+
+        $response = $this->nextResponse;
+        $this->nextResponse = null;
+
+        return $response;
+    }
+
+    protected function getScript($request)
+    {
+        $r = new \ReflectionClass('Symfony\Component\BrowserKit\Response');
+        $path = $r->getFileName();
+
+        return <<<EOF
+<?php
+
+require_once('$path');
+
+echo serialize($this->nextScript);
+EOF;
+    }
+}
+
+class ClientTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getHistory
+     */
+    public function testGetHistory()
+    {
+        $client = new TestClient(array(), $history = new History());
+        $this->assertSame($history, $client->getHistory(), '->getHistory() returns the History');
+    }
+
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getCookieJar
+     */
+    public function testGetCookieJar()
+    {
+        $client = new TestClient(array(), null, $cookieJar = new CookieJar());
+        $this->assertSame($cookieJar, $client->getCookieJar(), '->getCookieJar() returns the CookieJar');
+    }
+
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getRequest
+     */
+    public function testGetRequest()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'http://example.com/');
+
+        $this->assertEquals('http://example.com/', $client->getRequest()->getUri(), '->getCrawler() returns the Request of the last request');
+    }
+
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getResponse
+     */
+    public function testGetResponse()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('foo'));
+        $client->request('GET', 'http://example.com/');
+
+        $this->assertEquals('foo', $client->getResponse()->getContent(), '->getCrawler() returns the Response of the last request');
+    }
+
+    public function testGetContent()
+    {
+        $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}';
+
+        $client = new TestClient();
+        $client->request('POST', 'http://example.com/jsonrpc', array(), array(), array(), $json);
+        $this->assertEquals($json, $client->getRequest()->getContent());
+    }
+
+    /**
+     * @covers Symfony\Component\BrowserKit\Client::getCrawler
+     */
+    public function testGetCrawler()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('foo'));
+        $crawler = $client->request('GET', 'http://example.com/');
+
+        $this->assertSame($crawler, $client->getCrawler(), '->getCrawler() returns the Crawler of the last request');
+    }
+
+    public function testRequestHttpHeaders()
+    {
+        $client = new TestClient();
+        $client->request('GET', '/');
+        $headers = $client->getRequest()->getServer();
+        $this->assertEquals('localhost', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com');
+        $headers = $client->getRequest()->getServer();
+        $this->assertEquals('www.example.com', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header');
+
+        $client->request('GET', 'https://www.example.com');
+        $headers = $client->getRequest()->getServer();
+        $this->assertTrue($headers['HTTPS'], '->request() sets the HTTPS header');
+    }
+
+    public function testRequestURIConversion()
+    {
+        $client = new TestClient();
+        $client->request('GET', '/foo');
+        $this->assertEquals('http://localhost/foo', $client->getRequest()->getUri(), '->request() converts the URI to an absolute one');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com');
+        $this->assertEquals('http://www.example.com', $client->getRequest()->getUri(), '->request() does not change absolute URIs');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/');
+        $client->request('GET', '/foo');
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo');
+        $client->request('GET', '#');
+        $this->assertEquals('http://www.example.com/foo#', $client->getRequest()->getUri(), '->request() uses the previous request for #');
+        $client->request('GET', '#');
+        $this->assertEquals('http://www.example.com/foo#', $client->getRequest()->getUri(), '->request() uses the previous request for #');
+        $client->request('GET', '#foo');
+        $this->assertEquals('http://www.example.com/foo#foo', $client->getRequest()->getUri(), '->request() uses the previous request for #');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/');
+        $client->request('GET', 'bar');
+        $this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs');
+
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->request('GET', 'bar');
+        $this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs');
+    }
+
+    public function testRequestReferer()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->request('GET', 'bar');
+        $server = $client->getRequest()->getServer();
+        $this->assertEquals('http://www.example.com/foo/foobar', $server['HTTP_REFERER'], '->request() sets the referer');
+    }
+
+    public function testRequestHistory()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->request('GET', 'bar');
+
+        $this->assertEquals('http://www.example.com/foo/bar', $client->getHistory()->current()->getUri(), '->request() updates the History');
+        $this->assertEquals('http://www.example.com/foo/foobar', $client->getHistory()->back()->getUri(), '->request() updates the History');
+    }
+
+    public function testRequestCookies()
+    {
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><a href="/foo">foo</a></html>', 200, array('Set-Cookie' => 'foo=bar')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $this->assertEquals(array('foo' => 'bar'), $client->getCookieJar()->allValues('http://www.example.com/foo/foobar'), '->request() updates the CookieJar');
+
+        $client->request('GET', 'bar');
+        $this->assertEquals(array('foo' => 'bar'), $client->getCookieJar()->allValues('http://www.example.com/foo/foobar'), '->request() updates the CookieJar');
+    }
+
+    public function testClick()
+    {
+        if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
+            $this->markTestSkipped('The "DomCrawler" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\CssSelector\CssSelector')) {
+            $this->markTestSkipped('The "CssSelector" component is not available');
+        }
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><a href="/foo">foo</a></html>'));
+        $crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $client->click($crawler->filter('a')->link());
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() clicks on links');
+    }
+
+    public function testClickForm()
+    {
+        if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
+            $this->markTestSkipped('The "DomCrawler" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\CssSelector\CssSelector')) {
+            $this->markTestSkipped('The "CssSelector" component is not available');
+        }
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><form action="/foo"><input type="submit" /></form></html>'));
+        $crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $client->click($crawler->filter('input')->form());
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() Form submit forms');
+    }
+
+    public function testSubmit()
+    {
+        if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
+            $this->markTestSkipped('The "DomCrawler" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\CssSelector\CssSelector')) {
+            $this->markTestSkipped('The "CssSelector" component is not available');
+        }
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('<html><form action="/foo"><input type="submit" /></form></html>'));
+        $crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $client->submit($crawler->filter('input')->form());
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->submit() submit forms');
+    }
+
+    public function testSubmitPreserveAuth()
+    {
+        if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
+            $this->markTestSkipped('The "DomCrawler" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\CssSelector\CssSelector')) {
+            $this->markTestSkipped('The "CssSelector" component is not available');
+        }
+
+        $client = new TestClient(array('PHP_AUTH_USER' => 'foo', 'PHP_AUTH_PW' => 'bar'));
+        $client->setNextResponse(new Response('<html><form action="/foo"><input type="submit" /></form></html>'));
+        $crawler = $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $server = $client->getRequest()->getServer();
+        $this->assertArrayHasKey('PHP_AUTH_USER', $server);
+        $this->assertEquals('foo', $server['PHP_AUTH_USER']);
+        $this->assertArrayHasKey('PHP_AUTH_PW', $server);
+        $this->assertEquals('bar', $server['PHP_AUTH_PW']);
+
+        $client->submit($crawler->filter('input')->form());
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->submit() submit forms');
+
+        $server = $client->getRequest()->getServer();
+        $this->assertArrayHasKey('PHP_AUTH_USER', $server);
+        $this->assertEquals('foo', $server['PHP_AUTH_USER']);
+        $this->assertArrayHasKey('PHP_AUTH_PW', $server);
+        $this->assertEquals('bar', $server['PHP_AUTH_PW']);
+    }
+
+    public function testFollowRedirect()
+    {
+        $client = new TestClient();
+        $client->followRedirects(false);
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        try {
+            $client->followRedirect();
+            $this->fail('->followRedirect() throws a \LogicException if the request was not redirected');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->followRedirect() throws a \LogicException if the request was not redirected');
+        }
+
+        $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->followRedirect();
+
+        $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any');
+
+        $client = new TestClient();
+        $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected')));
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() automatically follows redirects if followRedirects is true');
+    }
+
+    public function testFollowRedirectWithCookies()
+    {
+        $client = new TestClient();
+        $client->followRedirects(false);
+        $client->setNextResponse(new Response('', 302, array(
+            'Location'   => 'http://www.example.com/redirected',
+            'Set-Cookie' => 'foo=bar',
+        )));
+        $client->request('GET', 'http://www.example.com/');
+        $this->assertEquals(array(), $client->getRequest()->getCookies());
+        $client->followRedirect();
+        $this->assertEquals(array('foo' => 'bar'), $client->getRequest()->getCookies());
+    }
+
+    public function testBack()
+    {
+        $client = new TestClient();
+
+        $parameters = array('foo' => 'bar');
+        $files = array('myfile.foo' => 'baz');
+        $server = array('X_TEST_FOO' => 'bazbar');
+        $content = 'foobarbaz';
+
+        $client->request('GET', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content);
+        $client->request('GET', 'http://www.example.com/foo');
+        $client->back();
+
+        $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->back() goes back in the history');
+        $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->back() keeps parameters');
+        $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->back() keeps files');
+        $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->back() keeps $_SERVER');
+        $this->assertEquals($content, $client->getRequest()->getContent(), '->back() keeps content');
+    }
+
+    public function testForward()
+    {
+        $client = new TestClient();
+
+        $parameters = array('foo' => 'bar');
+        $files = array('myfile.foo' => 'baz');
+        $server = array('X_TEST_FOO' => 'bazbar');
+        $content = 'foobarbaz';
+
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->request('GET', 'http://www.example.com/foo', $parameters, $files, $server, $content);
+        $client->back();
+        $client->forward();
+
+        $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->forward() goes forward in the history');
+        $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->forward() keeps parameters');
+        $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->forward() keeps files');
+        $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->forward() keeps $_SERVER');
+        $this->assertEquals($content, $client->getRequest()->getContent(), '->forward() keeps content');
+    }
+
+    public function testReload()
+    {
+        $client = new TestClient();
+
+        $parameters = array('foo' => 'bar');
+        $files = array('myfile.foo' => 'baz');
+        $server = array('X_TEST_FOO' => 'bazbar');
+        $content = 'foobarbaz';
+
+        $client->request('GET', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content);
+        $client->reload();
+
+        $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->reload() reloads the current page');
+        $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->reload() keeps parameters');
+        $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->reload() keeps files');
+        $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->reload() keeps $_SERVER');
+        $this->assertEquals($content, $client->getRequest()->getContent(), '->reload() keeps content');
+    }
+
+    public function testRestart()
+    {
+        $client = new TestClient();
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+        $client->restart();
+
+        $this->assertTrue($client->getHistory()->isEmpty(), '->restart() clears the history');
+        $this->assertEquals(array(), $client->getCookieJar()->all(), '->restart() clears the cookies');
+    }
+
+    public function testInsulatedRequests()
+    {
+        if (!class_exists('Symfony\Component\Process\Process')) {
+            $this->markTestSkipped('The "Process" component is not available');
+        }
+
+        $client = new TestClient();
+        $client->insulate();
+        $client->setNextScript("new Symfony\Component\BrowserKit\Response('foobar')");
+        $client->request('GET', 'http://www.example.com/foo/foobar');
+
+        $this->assertEquals('foobar', $client->getResponse()->getContent(), '->insulate() process the request in a forked process');
+
+        $client->setNextScript("new Symfony\Component\BrowserKit\Response('foobar)");
+
+        try {
+            $client->request('GET', 'http://www.example.com/foo/foobar');
+            $this->fail('->request() throws a \RuntimeException if the script has an error');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('RuntimeException', $e, '->request() throws a \RuntimeException if the script has an error');
+        }
+    }
+
+    public function testGetServerParameter()
+    {
+        $client = new TestClient();
+        $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST'));
+        $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT'));
+        $this->assertEquals('testvalue', $client->getServerParameter('testkey', 'testvalue'));
+    }
+
+    public function testSetServerParameter()
+    {
+        $client = new TestClient();
+
+        $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST'));
+        $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT'));
+
+        $client->setServerParameter('HTTP_HOST', 'testhost');
+        $this->assertEquals('testhost', $client->getServerParameter('HTTP_HOST'));
+
+        $client->setServerParameter('HTTP_USER_AGENT', 'testua');
+        $this->assertEquals('testua', $client->getServerParameter('HTTP_USER_AGENT'));
+    }
+}

+ 191 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieJarTest.php

@@ -0,0 +1,191 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\CookieJar;
+use Symfony\Component\BrowserKit\Cookie;
+use Symfony\Component\BrowserKit\Response;
+
+class CookieJarTest extends \PHPUnit_Framework_TestCase
+{
+    public function testSetGet()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie = new Cookie('foo', 'bar'));
+
+        $this->assertEquals($cookie, $cookieJar->get('foo'), '->set() sets a cookie');
+
+        $this->assertNull($cookieJar->get('foobar'), '->get() returns null if the cookie does not exist');
+
+        $cookieJar->set($cookie = new Cookie('foo', 'bar', time() - 86400));
+        $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
+    }
+
+    public function testExpire()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie = new Cookie('foo', 'bar'));
+        $cookieJar->expire('foo');
+        $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
+    }
+
+    public function testAll()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar'));
+        $cookieJar->set($cookie2 = new Cookie('bar', 'foo'));
+
+        $this->assertEquals(array($cookie1, $cookie2), $cookieJar->all(), '->all() returns all cookies in the jar');
+    }
+
+    public function testClear()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar'));
+        $cookieJar->set($cookie2 = new Cookie('bar', 'foo'));
+
+        $cookieJar->clear();
+
+        $this->assertEquals(array(), $cookieJar->all(), '->clear() expires all cookies');
+    }
+
+    public function testUpdateFromResponse()
+    {
+        $response = new Response('', 200, array('Set-Cookie' => 'foo=foo'));
+
+        $cookieJar = new CookieJar();
+        $cookieJar->updateFromResponse($response);
+
+        $this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromResponse() updates cookies from a Response objects');
+    }
+
+    public function testUpdateFromSetCookie()
+    {
+        $setCookies = array('foo=foo');
+
+        $cookieJar = new CookieJar();
+        $cookieJar->set(new Cookie('bar', 'bar'));
+        $cookieJar->updateFromSetCookie($setCookies);
+
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('foo'));
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('bar'));
+        $this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromSetCookie() updates cookies from a Set-Cookie header');
+        $this->assertEquals('bar', $cookieJar->get('bar')->getValue(), '->updateFromSetCookie() keeps existing cookies');
+    }
+
+    public function testUpdateFromSetCookieWithMultipleCookies()
+    {
+        $timestamp = time() + 3600;
+        $date = gmdate('D, d M Y H:i:s \G\M\T', $timestamp);
+        $setCookies = array(sprintf('foo=foo; expires=%s; domain=.symfony.com; path=/, bar=bar; domain=.blog.symfony.com, PHPSESSID=id; expires=%s', $date, $date));
+
+        $cookieJar = new CookieJar();
+        $cookieJar->updateFromSetCookie($setCookies);
+
+        $fooCookie = $cookieJar->get('foo', '/', '.symfony.com');
+        $barCookie = $cookieJar->get('bar', '/', '.blog.symfony.com');
+        $phpCookie = $cookieJar->get('PHPSESSID');
+
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $fooCookie);
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $barCookie);
+        $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $phpCookie);
+        $this->assertEquals('foo', $fooCookie->getValue());
+        $this->assertEquals('bar', $barCookie->getValue());
+        $this->assertEquals('id', $phpCookie->getValue());
+        $this->assertEquals($timestamp, $fooCookie->getExpiresTime());
+        $this->assertNull($barCookie->getExpiresTime());
+        $this->assertEquals($timestamp, $phpCookie->getExpiresTime());
+    }
+
+    /**
+     * @dataProvider provideAllValuesValues
+     */
+    public function testAllValues($uri, $values)
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo_nothing', 'foo'));
+        $cookieJar->set($cookie2 = new Cookie('foo_expired', 'foo', time() - 86400));
+        $cookieJar->set($cookie3 = new Cookie('foo_path', 'foo', null, '/foo'));
+        $cookieJar->set($cookie4 = new Cookie('foo_domain', 'foo', null, '/', '.example.com'));
+        $cookieJar->set($cookie4 = new Cookie('foo_strict_domain', 'foo', null, '/', '.www4.example.com'));
+        $cookieJar->set($cookie5 = new Cookie('foo_secure', 'foo', null, '/', '', true));
+
+        $this->assertEquals($values, array_keys($cookieJar->allValues($uri)), '->allValues() returns the cookie for a given URI');
+    }
+
+    public function provideAllValuesValues()
+    {
+        return array(
+            array('http://www.example.com', array('foo_nothing', 'foo_domain')),
+            array('http://www.example.com/', array('foo_nothing', 'foo_domain')),
+            array('http://foo.example.com/', array('foo_nothing', 'foo_domain')),
+            array('http://foo.example1.com/', array('foo_nothing')),
+            array('https://foo.example.com/', array('foo_nothing', 'foo_secure', 'foo_domain')),
+            array('http://www.example.com/foo/bar', array('foo_nothing', 'foo_path', 'foo_domain')),
+            array('http://www4.example.com/', array('foo_nothing', 'foo_domain', 'foo_strict_domain')),
+        );
+    }
+
+    public function testEncodedValues()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true));
+
+        $this->assertEquals(array('foo' => 'bar=baz'), $cookieJar->allValues('/'));
+        $this->assertEquals(array('foo' => 'bar%3Dbaz'), $cookieJar->allRawValues('/'));
+    }
+
+    public function testCookieExpireWithSameNameButDifferentPaths()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo'));
+        $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar'));
+        $cookieJar->expire('foo', '/foo');
+
+        $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
+        $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
+        $this->assertEquals(array(), $cookieJar->allValues('http://example.com/foo'));
+        $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar'));
+    }
+
+    public function testCookieExpireWithNullPaths()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/'));
+        $cookieJar->expire('foo', null);
+
+        $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired');
+        $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
+    }
+
+    public function testCookieWithSameNameButDifferentPaths()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo'));
+        $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar'));
+
+        $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
+        $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://example.com/foo'));
+        $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar'));
+    }
+
+    public function testCookieWithSameNameButDifferentDomains()
+    {
+        $cookieJar = new CookieJar();
+        $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/', 'foo.example.com'));
+        $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/', 'bar.example.com'));
+
+        $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/')));
+        $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://foo.example.com/'));
+        $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://bar.example.com/'));
+    }
+}

+ 177 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/CookieTest.php

@@ -0,0 +1,177 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\Cookie;
+
+class CookieTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider getTestsForToFromString
+     */
+    public function testToFromString($cookie, $url = null)
+    {
+        $this->assertEquals($cookie, (string) Cookie::fromString($cookie, $url));
+    }
+
+    public function getTestsForToFromString()
+    {
+        return array(
+            array('foo=bar'),
+            array('foo=bar; path=/foo'),
+            array('foo=bar; domain=google.com'),
+            array('foo=bar; domain=example.com; secure', 'https://example.com/'),
+            array('foo=bar; httponly'),
+            array('foo=bar; domain=google.com; path=/foo; secure; httponly', 'https://google.com/'),
+            array('foo=bar=baz'),
+            array('foo=bar%3Dbaz'),
+        );
+    }
+
+    public function testFromStringIgnoreSecureFlag()
+    {
+        $this->assertFalse(Cookie::fromString('foo=bar; secure')->isSecure());
+        $this->assertFalse(Cookie::fromString('foo=bar; secure', 'http://example.com/')->isSecure());
+    }
+
+    /**
+     * @dataProvider getExpireCookieStrings
+     */
+    public function testFromStringAcceptsSeveralExpiresDateFormats($cookie)
+    {
+        $this->assertEquals(1596185377, Cookie::fromString($cookie)->getExpiresTime());
+    }
+
+    public function getExpireCookieStrings()
+    {
+        return array(
+            array('foo=bar; expires=Fri, 31-Jul-2020 08:49:37 GMT'),
+            array('foo=bar; expires=Fri, 31 Jul 2020 08:49:37 GMT'),
+            array('foo=bar; expires=Fri, 31-07-2020 08:49:37 GMT'),
+            array('foo=bar; expires=Fri, 31-07-20 08:49:37 GMT'),
+            array('foo=bar; expires=Friday, 31-Jul-20 08:49:37 GMT'),
+            array('foo=bar; expires=Fri Jul 31 08:49:37 2020'),
+            array('foo=bar; expires=\'Fri Jul 31 08:49:37 2020\''),
+            array('foo=bar; expires=Friday July 31st 2020, 08:49:37 GMT'),
+        );
+    }
+
+    public function testFromStringWithCapitalization()
+    {
+        $this->assertEquals('Foo=Bar', (string) Cookie::fromString('Foo=Bar'));
+        $this->assertEquals('foo=bar; expires=Fri, 31 Dec 2010 23:59:59 GMT', (string) Cookie::fromString('foo=bar; Expires=Fri, 31 Dec 2010 23:59:59 GMT'));
+        $this->assertEquals('foo=bar; domain=www.example.org; httponly', (string) Cookie::fromString('foo=bar; DOMAIN=www.example.org; HttpOnly'));
+    }
+
+    public function testFromStringWithUrl()
+    {
+        $this->assertEquals('foo=bar; domain=www.example.com', (string) Cookie::FromString('foo=bar', 'http://www.example.com/'));
+        $this->assertEquals('foo=bar; domain=www.example.com; path=/foo', (string) Cookie::FromString('foo=bar', 'http://www.example.com/foo/bar'));
+        $this->assertEquals('foo=bar; domain=www.example.com', (string) Cookie::FromString('foo=bar; path=/', 'http://www.example.com/foo/bar'));
+        $this->assertEquals('foo=bar; domain=www.myotherexample.com', (string) Cookie::FromString('foo=bar; domain=www.myotherexample.com', 'http://www.example.com/'));
+    }
+
+    public function testFromStringThrowsAnExceptionIfCookieIsNotValid()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+        Cookie::FromString('foo');
+    }
+
+    public function testFromStringThrowsAnExceptionIfCookieDateIsNotValid()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+        Cookie::FromString('foo=bar; expires=Flursday July 31st 2020, 08:49:37 GMT');
+    }
+
+    public function testFromStringThrowsAnExceptionIfUrlIsNotValid()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+        Cookie::FromString('foo=bar', 'foobar');
+    }
+
+    public function testGetName()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertEquals('foo', $cookie->getName(), '->getName() returns the cookie name');
+    }
+
+    public function testGetValue()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertEquals('bar', $cookie->getValue(), '->getValue() returns the cookie value');
+
+        $cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true); // raw value
+        $this->assertEquals('bar=baz', $cookie->getValue(), '->getValue() returns the urldecoded cookie value');
+    }
+
+    public function testGetRawValue()
+    {
+        $cookie = new Cookie('foo', 'bar=baz'); // decoded value
+        $this->assertEquals('bar%3Dbaz', $cookie->getRawValue(), '->getRawValue() returns the urlencoded cookie value');
+        $cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true); // raw value
+        $this->assertEquals('bar%3Dbaz', $cookie->getRawValue(), '->getRawValue() returns the non-urldecoded cookie value');
+    }
+
+    public function testGetPath()
+    {
+        $cookie = new Cookie('foo', 'bar', 0);
+        $this->assertEquals('/', $cookie->getPath(), '->getPath() returns / is no path is defined');
+
+        $cookie = new Cookie('foo', 'bar', 0, '/foo');
+        $this->assertEquals('/foo', $cookie->getPath(), '->getPath() returns the cookie path');
+    }
+
+    public function testGetDomain()
+    {
+        $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com');
+        $this->assertEquals('foo.com', $cookie->getDomain(), '->getDomain() returns the cookie domain');
+    }
+
+    public function testIsSecure()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertFalse($cookie->isSecure(), '->isSecure() returns false if not defined');
+
+        $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', true);
+        $this->assertTrue($cookie->isSecure(), '->isSecure() returns the cookie secure flag');
+    }
+
+    public function testIsHttponly()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns false if not defined');
+
+        $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', false, true);
+        $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns the cookie httponly flag');
+    }
+
+    public function testGetExpiresTime()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertNull($cookie->getExpiresTime(), '->getExpiresTime() returns the expires time');
+
+        $cookie = new Cookie('foo', 'bar', $time = time() - 86400);
+        $this->assertEquals($time, $cookie->getExpiresTime(), '->getExpiresTime() returns the expires time');
+    }
+
+    public function testIsExpired()
+    {
+        $cookie = new Cookie('foo', 'bar');
+        $this->assertFalse($cookie->isExpired(), '->isExpired() returns false when the cookie never expires (null as expires time)');
+
+        $cookie = new Cookie('foo', 'bar', time() - 86400);
+        $this->assertTrue($cookie->isExpired(), '->isExpired() returns true when the cookie is expired');
+
+        $cookie = new Cookie('foo', 'bar', 0);
+        $this->assertFalse($cookie->isExpired());
+    }
+}

+ 101 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/HistoryTest.php

@@ -0,0 +1,101 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\History;
+use Symfony\Component\BrowserKit\Request;
+
+class HistoryTest extends \PHPUnit_Framework_TestCase
+{
+    public function testAdd()
+    {
+        $history = new History();
+        $history->add(new Request('http://www.example1.com/', 'get'));
+        $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->add() adds a request to the history');
+
+        $history->add(new Request('http://www.example2.com/', 'get'));
+        $this->assertSame('http://www.example2.com/', $history->current()->getUri(), '->add() adds a request to the history');
+
+        $history->add(new Request('http://www.example3.com/', 'get'));
+        $history->back();
+        $history->add(new Request('http://www.example4.com/', 'get'));
+        $this->assertSame('http://www.example4.com/', $history->current()->getUri(), '->add() adds a request to the history');
+
+        $history->back();
+        $this->assertSame('http://www.example2.com/', $history->current()->getUri(), '->add() adds a request to the history');
+    }
+
+    public function testClearIsEmpty()
+    {
+        $history = new History();
+        $history->add(new Request('http://www.example.com/', 'get'));
+
+        $this->assertFalse($history->isEmpty(), '->isEmpty() returns false if the history is not empty');
+
+        $history->clear();
+
+        $this->assertTrue($history->isEmpty(), '->isEmpty() true if the history is empty');
+    }
+
+    public function testCurrent()
+    {
+        $history = new History();
+
+        try {
+            $history->current();
+            $this->fail('->current() throws a \LogicException if the history is empty');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->current() throws a \LogicException if the history is empty');
+        }
+
+        $history->add(new Request('http://www.example.com/', 'get'));
+
+        $this->assertSame('http://www.example.com/', $history->current()->getUri(), '->current() returns the current request in the history');
+    }
+
+    public function testBack()
+    {
+        $history = new History();
+        $history->add(new Request('http://www.example.com/', 'get'));
+
+        try {
+            $history->back();
+            $this->fail('->back() throws a \LogicException if the history is already on the first page');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->current() throws a \LogicException if the history is already on the first page');
+        }
+
+        $history->add(new Request('http://www.example1.com/', 'get'));
+        $history->back();
+
+        $this->assertSame('http://www.example.com/', $history->current()->getUri(), '->back() returns the previous request in the history');
+    }
+
+    public function testForward()
+    {
+        $history = new History();
+        $history->add(new Request('http://www.example.com/', 'get'));
+        $history->add(new Request('http://www.example1.com/', 'get'));
+
+        try {
+            $history->forward();
+            $this->fail('->forward() throws a \LogicException if the history is already on the last page');
+        } catch (\Exception $e) {
+            $this->assertInstanceof('LogicException', $e, '->forward() throws a \LogicException if the history is already on the last page');
+        }
+
+        $history->back();
+        $history->forward();
+
+        $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->forward() returns the next request in the history');
+    }
+}

+ 53 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/RequestTest.php

@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\Request;
+
+class RequestTest extends \PHPUnit_Framework_TestCase
+{
+    public function testGetUri()
+    {
+        $request = new Request('http://www.example.com/', 'get');
+        $this->assertEquals('http://www.example.com/', $request->getUri(), '->getUri() returns the URI of the request');
+    }
+
+    public function testGetMethod()
+    {
+        $request = new Request('http://www.example.com/', 'get');
+        $this->assertEquals('get', $request->getMethod(), '->getMethod() returns the method of the request');
+    }
+
+    public function testGetParameters()
+    {
+        $request = new Request('http://www.example.com/', 'get', array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $request->getParameters(), '->getParameters() returns the parameters of the request');
+    }
+
+    public function testGetFiles()
+    {
+        $request = new Request('http://www.example.com/', 'get', array(), array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $request->getFiles(), '->getFiles() returns the uploaded files of the request');
+    }
+
+    public function testGetCookies()
+    {
+        $request = new Request('http://www.example.com/', 'get', array(), array(), array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $request->getCookies(), '->getCookies() returns the cookies of the request');
+    }
+
+    public function testGetServer()
+    {
+        $request = new Request('http://www.example.com/', 'get', array(), array(), array(), array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $request->getServer(), '->getServer() returns the server parameters of the request');
+    }
+}

+ 76 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/Tests/ResponseTest.php

@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\BrowserKit\Tests;
+
+use Symfony\Component\BrowserKit\Response;
+
+class ResponseTest extends \PHPUnit_Framework_TestCase
+{
+    public function testGetUri()
+    {
+        $response = new Response('foo');
+        $this->assertEquals('foo', $response->getContent(), '->getContent() returns the content of the response');
+    }
+
+    public function testGetStatus()
+    {
+        $response = new Response('foo', 304);
+        $this->assertEquals('304', $response->getStatus(), '->getStatus() returns the status of the response');
+    }
+
+    public function testGetHeaders()
+    {
+        $response = new Response('foo', 200, array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $response->getHeaders(), '->getHeaders() returns the headers of the response');
+    }
+
+    public function testGetHeader()
+    {
+        $response = new Response('foo', 200, array(
+            'Content-Type' => 'text/html',
+            'Set-Cookie'   => array('foo=bar', 'bar=foo'),
+        ));
+
+        $this->assertEquals('text/html', $response->getHeader('Content-Type'), '->getHeader() returns a header of the response');
+        $this->assertEquals('text/html', $response->getHeader('content-type'), '->getHeader() returns a header of the response');
+        $this->assertEquals('text/html', $response->getHeader('content_type'), '->getHeader() returns a header of the response');
+        $this->assertEquals('foo=bar', $response->getHeader('Set-Cookie'), '->getHeader() returns the first header value');
+        $this->assertEquals(array('foo=bar', 'bar=foo'), $response->getHeader('Set-Cookie', false), '->getHeader() returns all header values if first is false');
+
+        $this->assertNull($response->getHeader('foo'), '->getHeader() returns null if the header is not defined');
+        $this->assertEquals(array(), $response->getHeader('foo', false), '->getHeader() returns an empty array if the header is not defined and first is set to false');
+    }
+
+    public function testMagicToString()
+    {
+        $response = new Response('foo', 304, array('foo' => 'bar'));
+
+        $this->assertEquals("foo: bar\n\nfoo", $response->__toString(), '->__toString() returns the headers and the content as a string');
+    }
+
+    public function testMagicToStringWithMultipleSetCookieHeader()
+    {
+        $headers = array(
+            'content-type' => 'text/html; charset=utf-8',
+            'set-cookie'   => array('foo=bar', 'bar=foo')
+        );
+
+        $expected = 'content-type: text/html; charset=utf-8'."\n";
+        $expected.= 'set-cookie: foo=bar'."\n";
+        $expected.= 'set-cookie: bar=foo'."\n\n";
+        $expected.= 'foo';
+
+        $response = new Response('foo', 304, $headers);
+
+        $this->assertEquals($expected, $response->__toString(), '->__toString() returns the headers and the content as a string');
+    }
+}

+ 39 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/composer.json

@@ -0,0 +1,39 @@
+{
+    "name": "symfony/browser-kit",
+    "type": "library",
+    "description": "Symfony BrowserKit Component",
+    "keywords": [],
+    "homepage": "http://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "http://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3",
+        "symfony/dom-crawler": "~2.0"
+    },
+    "require-dev": {
+        "symfony/process": "~2.0",
+        "symfony/css-selector": "~2.0"
+    },
+    "suggest": {
+        "symfony/process": "2.2.*"
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Component\\BrowserKit\\": "" }
+    },
+    "target-dir": "Symfony/Component/BrowserKit",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3-dev"
+        }
+    }
+}

+ 30 - 0
vendor/symfony/browser-kit/Symfony/Component/BrowserKit/phpunit.xml.dist

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony BrowserKit Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>

+ 0 - 1
vendor/symfony/console/Symfony/Component/Console/Input/ArgvInput.php

@@ -68,7 +68,6 @@ class ArgvInput extends Input
     protected function setTokens(array $tokens)
     {
         $this->tokens = $tokens;
-        $this->parse();
     }
 
     /**

+ 7 - 1
vendor/symfony/console/Symfony/Component/Console/Input/StringInput.php

@@ -33,13 +33,19 @@ class StringInput extends ArgvInput
      * @param string          $input      An array of parameters from the CLI (in the argv format)
      * @param InputDefinition $definition A InputDefinition instance
      *
+     * @deprecated The second argument is deprecated as it does not work (will be removed in 3.0), use 'bind' method instead
+     *
      * @api
      */
     public function __construct($input, InputDefinition $definition = null)
     {
-        parent::__construct(array(), $definition);
+        parent::__construct(array(), null);
 
         $this->setTokens($this->tokenize($input));
+
+        if (null !== $definition) {
+            $this->bind($definition);
+        }
     }
 
     /**

+ 8 - 4
vendor/symfony/console/Symfony/Component/Console/Tests/Input/StringInputTest.php

@@ -11,9 +11,9 @@
 
 namespace Symfony\Component\Console\Tests\Input;
 
-use Symfony\Component\Console\Input\StringInput;
 use Symfony\Component\Console\Input\InputDefinition;
 use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\StringInput;
 
 class StringInputTest extends \PHPUnit_Framework_TestCase
 {
@@ -35,10 +35,14 @@ class StringInputTest extends \PHPUnit_Framework_TestCase
             array(new InputOption('foo', null, InputOption::VALUE_REQUIRED))
         );
 
-        $input = new StringInput('--foo=bar', $definition);
-        $actual = $input->getOption('foo');
+        // call to bind
+        $input = new StringInput('--foo=bar');
+        $input->bind($definition);
+        $this->assertEquals('bar', $input->getOption('foo'));
 
-        $this->assertEquals('bar', $actual);
+        // definition in constructor
+        $input = new StringInput('--foo=bar', $definition);
+        $this->assertEquals('bar', $input->getOption('foo'));
     }
 
     public function getTokenizeData()

+ 6 - 1
vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/ContainerBuilder.php

@@ -431,7 +431,12 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
 
             $this->loading[$id] = true;
 
-            $service = $this->createService($definition, $id);
+            try {
+                $service = $this->createService($definition, $id);
+            } catch (\Exception $e) {
+                unset($this->loading[$id]);
+                throw $e;
+            }
 
             unset($this->loading[$id]);
 

+ 18 - 8
vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

@@ -441,19 +441,30 @@ class PhpDumper extends Dumper
         $this->referenceVariables = array();
         $this->variableCount = 0;
 
-        $return = '';
+        $return = array();
+
         if ($definition->isSynthetic()) {
-            $return = sprintf('@throws RuntimeException always since this service is expected to be injected dynamically');
+            $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
         } elseif ($class = $definition->getClass()) {
-            $return = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'Object' : $class, $class);
+            $return[] = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'object' : $class, $class);
         } elseif ($definition->getFactoryClass()) {
-            $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
+            $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
         } elseif ($definition->getFactoryService()) {
-            $return = sprintf('@return Object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod());
+            $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryService(), $definition->getFactoryMethod());
+        }
+
+        $scope = $definition->getScope();
+        if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
+            if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
+                $return[] = '';
+            }
+            $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope);
         }
 
+        $return = implode("\n     * ", $return);
+
         $doc = '';
-        if (ContainerInterface::SCOPE_PROTOTYPE !== $definition->getScope()) {
+        if (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
             $doc .= <<<EOF
 
      *
@@ -484,8 +495,7 @@ EOF;
 
 EOF;
 
-        $scope = $definition->getScope();
-        if (ContainerInterface::SCOPE_CONTAINER !== $scope && ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
+        if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
             $code .= <<<EOF
         if (!isset(\$this->scopedServices['$scope'])) {
             throw new InactiveScopeException('$id', '$scope');

+ 21 - 0
vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

@@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 use Symfony\Component\Config\Resource\FileResource;
@@ -116,6 +117,26 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
         $this->assertTrue($builder->get('bar') === $builder->get('bar'), '->get() always returns the same instance if the service is shared');
     }
 
+    /**
+     * @covers                   \Symfony\Component\DependencyInjection\ContainerBuilder::get
+     * @expectedException        \Symfony\Component\DependencyInjection\Exception\RuntimeException
+     * @expectedExceptionMessage You have requested a synthetic service ("foo"). The DIC does not know how to construct this service.
+     */
+    public function testGetUnsetLoadingServiceWhenCreateServiceThrowsAnException()
+    {
+        $builder = new ContainerBuilder();
+        $builder->register('foo', 'stdClass')->setSynthetic(true);
+
+        // we expect a RuntimeException here as foo is synthetic
+        try {
+            $builder->get('foo');
+        } catch (RuntimeException $e) {
+        }
+
+        // we must also have the same RuntimeException here
+        $builder->get('foo');
+    }
+
     /**
      * @covers Symfony\Component\DependencyInjection\ContainerBuilder::getServiceIds
      */

+ 2 - 2
vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php

@@ -102,7 +102,7 @@ class ProjectServiceContainer extends Container
      * This service is shared.
      * This method always returns the same instance of the service.
      *
-     * @return Object A %baz_class% instance.
+     * @return object A %baz_class% instance.
      */
     protected function getFoo_BazService()
     {
@@ -116,7 +116,7 @@ class ProjectServiceContainer extends Container
     /**
      * Gets the 'foo_bar' service.
      *
-     * @return Object A %foo_class% instance.
+     * @return object A %foo_class% instance.
      */
     protected function getFooBarService()
     {

+ 18 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/CHANGELOG.md

@@ -0,0 +1,18 @@
+CHANGELOG
+=========
+
+2.2.0
+-----
+
+ * added a way to set raw path to the file in FileFormField - necessary for
+   simulating HTTP requests
+
+2.1.0
+-----
+
+ * added support for the HTTP PATCH method
+ * refactored the Form class internals to support multi-dimensional fields
+   (the public API is backward compatible)
+ * added a way to get parsing errors for Crawler::addHtmlContent() and
+   Crawler::addXmlContent() via libxml functions
+ * added support for submitting a form without a submit button

+ 732 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Crawler.php

@@ -0,0 +1,732 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+use Symfony\Component\CssSelector\CssSelector;
+
+/**
+ * Crawler eases navigation of a list of \DOMNode objects.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Crawler extends \SplObjectStorage
+{
+    /**
+     * @var string The current URI or the base href value
+     */
+    protected $uri;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed  $node A Node to use as the base for the crawling
+     * @param string $uri  The current URI or the base href value
+     *
+     * @api
+     */
+    public function __construct($node = null, $uri = null)
+    {
+        $this->uri = $uri;
+
+        $this->add($node);
+    }
+
+    /**
+     * Removes all the nodes.
+     *
+     * @api
+     */
+    public function clear()
+    {
+        $this->removeAll($this);
+    }
+
+    /**
+     * Adds a node to the current list of nodes.
+     *
+     * This method uses the appropriate specialized add*() method based
+     * on the type of the argument.
+     *
+     * @param null|\DOMNodeList|array|\DOMNode $node A node
+     *
+     * @api
+     */
+    public function add($node)
+    {
+        if ($node instanceof \DOMNodeList) {
+            $this->addNodeList($node);
+        } elseif (is_array($node)) {
+            $this->addNodes($node);
+        } elseif (is_string($node)) {
+            $this->addContent($node);
+        } elseif (is_object($node)) {
+            $this->addNode($node);
+        }
+    }
+
+    /**
+     * Adds HTML/XML content.
+     *
+     * @param string      $content A string to parse as HTML/XML
+     * @param null|string $type    The content type of the string
+     *
+     * @return null|void
+     */
+    public function addContent($content, $type = null)
+    {
+        if (empty($type)) {
+            $type = 'text/html';
+        }
+
+        // DOM only for HTML/XML content
+        if (!preg_match('/(x|ht)ml/i', $type, $matches)) {
+            return null;
+        }
+
+        $charset = 'ISO-8859-1';
+        if (false !== $pos = strpos($type, 'charset=')) {
+            $charset = substr($type, $pos + 8);
+            if (false !== $pos = strpos($charset, ';')) {
+                $charset = substr($charset, 0, $pos);
+            }
+        }
+
+        if ('x' === $matches[1]) {
+            $this->addXmlContent($content, $charset);
+        } else {
+            $this->addHtmlContent($content, $charset);
+        }
+    }
+
+    /**
+     * Adds an HTML content to the list of nodes.
+     *
+     * The libxml errors are disabled when the content is parsed.
+     *
+     * If you want to get parsing errors, be sure to enable
+     * internal errors via libxml_use_internal_errors(true)
+     * and then, get the errors via libxml_get_errors(). Be
+     * sure to clear errors with libxml_clear_errors() afterward.
+     *
+     * @param string $content The HTML content
+     * @param string $charset The charset
+     *
+     * @api
+     */
+    public function addHtmlContent($content, $charset = 'UTF-8')
+    {
+        $current = libxml_use_internal_errors(true);
+        $disableEntities = libxml_disable_entity_loader(true);
+
+        $dom = new \DOMDocument('1.0', $charset);
+        $dom->validateOnParse = true;
+
+        if (function_exists('mb_convert_encoding') && in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) {
+            $content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset);
+        }
+
+        @$dom->loadHTML($content);
+
+        libxml_use_internal_errors($current);
+        libxml_disable_entity_loader($disableEntities);
+
+        $this->addDocument($dom);
+
+        $base = $this->filterXPath('descendant-or-self::base')->extract(array('href'));
+
+        $baseHref = current($base);
+        if (count($base) && !empty($baseHref)) {
+            $this->uri = $baseHref;
+        }
+    }
+
+    /**
+     * Adds an XML content to the list of nodes.
+     *
+     * The libxml errors are disabled when the content is parsed.
+     *
+     * If you want to get parsing errors, be sure to enable
+     * internal errors via libxml_use_internal_errors(true)
+     * and then, get the errors via libxml_get_errors(). Be
+     * sure to clear errors with libxml_clear_errors() afterward.
+     *
+     * @param string $content The XML content
+     * @param string $charset The charset
+     *
+     * @api
+     */
+    public function addXmlContent($content, $charset = 'UTF-8')
+    {
+        $current = libxml_use_internal_errors(true);
+        $disableEntities = libxml_disable_entity_loader(true);
+
+        $dom = new \DOMDocument('1.0', $charset);
+        $dom->validateOnParse = true;
+
+        // remove the default namespace to make XPath expressions simpler
+        @$dom->loadXML(str_replace('xmlns', 'ns', $content), LIBXML_NONET);
+
+        libxml_use_internal_errors($current);
+        libxml_disable_entity_loader($disableEntities);
+
+        $this->addDocument($dom);
+    }
+
+    /**
+     * Adds a \DOMDocument to the list of nodes.
+     *
+     * @param \DOMDocument $dom A \DOMDocument instance
+     *
+     * @api
+     */
+    public function addDocument(\DOMDocument $dom)
+    {
+        if ($dom->documentElement) {
+            $this->addNode($dom->documentElement);
+        }
+    }
+
+    /**
+     * Adds a \DOMNodeList to the list of nodes.
+     *
+     * @param \DOMNodeList $nodes A \DOMNodeList instance
+     *
+     * @api
+     */
+    public function addNodeList(\DOMNodeList $nodes)
+    {
+        foreach ($nodes as $node) {
+            $this->addNode($node);
+        }
+    }
+
+    /**
+     * Adds an array of \DOMNode instances to the list of nodes.
+     *
+     * @param \DOMNode[] $nodes An array of \DOMNode instances
+     *
+     * @api
+     */
+    public function addNodes(array $nodes)
+    {
+        foreach ($nodes as $node) {
+            $this->add($node);
+        }
+    }
+
+    /**
+     * Adds a \DOMNode instance to the list of nodes.
+     *
+     * @param \DOMNode $node A \DOMNode instance
+     *
+     * @api
+     */
+    public function addNode(\DOMNode $node)
+    {
+        if ($node instanceof \DOMDocument) {
+            $this->attach($node->documentElement);
+        } else {
+            $this->attach($node);
+        }
+    }
+
+    /**
+     * Returns a node given its position in the node list.
+     *
+     * @param integer $position The position
+     *
+     * @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist.
+     *
+     * @api
+     */
+    public function eq($position)
+    {
+        foreach ($this as $i => $node) {
+            if ($i == $position) {
+                return new static($node, $this->uri);
+            }
+        }
+
+        return new static(null, $this->uri);
+    }
+
+    /**
+     * Calls an anonymous function on each node of the list.
+     *
+     * The anonymous function receives the position and the node as arguments.
+     *
+     * Example:
+     *
+     *     $crawler->filter('h1')->each(function ($node, $i)
+     *     {
+     *       return $node->nodeValue;
+     *     });
+     *
+     * @param \Closure $closure An anonymous function
+     *
+     * @return array An array of values returned by the anonymous function
+     *
+     * @api
+     */
+    public function each(\Closure $closure)
+    {
+        $data = array();
+        foreach ($this as $i => $node) {
+            $data[] = $closure($node, $i);
+        }
+
+        return $data;
+    }
+
+    /**
+     * Reduces the list of nodes by calling an anonymous function.
+     *
+     * To remove a node from the list, the anonymous function must return false.
+     *
+     * @param \Closure $closure An anonymous function
+     *
+     * @return Crawler A Crawler instance with the selected nodes.
+     *
+     * @api
+     */
+    public function reduce(\Closure $closure)
+    {
+        $nodes = array();
+        foreach ($this as $i => $node) {
+            if (false !== $closure($node, $i)) {
+                $nodes[] = $node;
+            }
+        }
+
+        return new static($nodes, $this->uri);
+    }
+
+    /**
+     * Returns the first node of the current selection
+     *
+     * @return Crawler A Crawler instance with the first selected node
+     *
+     * @api
+     */
+    public function first()
+    {
+        return $this->eq(0);
+    }
+
+    /**
+     * Returns the last node of the current selection
+     *
+     * @return Crawler A Crawler instance with the last selected node
+     *
+     * @api
+     */
+    public function last()
+    {
+        return $this->eq(count($this) - 1);
+    }
+
+    /**
+     * Returns the siblings nodes of the current selection
+     *
+     * @return Crawler A Crawler instance with the sibling nodes
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function siblings()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return new static($this->sibling($this->getNode(0)->parentNode->firstChild), $this->uri);
+    }
+
+    /**
+     * Returns the next siblings nodes of the current selection
+     *
+     * @return Crawler A Crawler instance with the next sibling nodes
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function nextAll()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return new static($this->sibling($this->getNode(0)), $this->uri);
+    }
+
+    /**
+     * Returns the previous sibling nodes of the current selection
+     *
+     * @return Crawler A Crawler instance with the previous sibling nodes
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @api
+     */
+    public function previousAll()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return new static($this->sibling($this->getNode(0), 'previousSibling'), $this->uri);
+    }
+
+    /**
+     * Returns the parents nodes of the current selection
+     *
+     * @return Crawler A Crawler instance with the parents nodes of the current selection
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function parents()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $node = $this->getNode(0);
+        $nodes = array();
+
+        while ($node = $node->parentNode) {
+            if (1 === $node->nodeType && '_root' !== $node->nodeName) {
+                $nodes[] = $node;
+            }
+        }
+
+        return new static($nodes, $this->uri);
+    }
+
+    /**
+     * Returns the children nodes of the current selection
+     *
+     * @return Crawler A Crawler instance with the children nodes
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function children()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return new static($this->sibling($this->getNode(0)->firstChild), $this->uri);
+    }
+
+    /**
+     * Returns the attribute value of the first node of the list.
+     *
+     * @param string $attribute The attribute name
+     *
+     * @return string The attribute value
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function attr($attribute)
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return $this->getNode(0)->getAttribute($attribute);
+    }
+
+    /**
+     * Returns the node value of the first node of the list.
+     *
+     * @return string The node value
+     *
+     * @throws \InvalidArgumentException When current node is empty
+     *
+     * @api
+     */
+    public function text()
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        return $this->getNode(0)->nodeValue;
+    }
+
+    /**
+     * Extracts information from the list of nodes.
+     *
+     * You can extract attributes or/and the node value (_text).
+     *
+     * Example:
+     *
+     * $crawler->filter('h1 a')->extract(array('_text', 'href'));
+     *
+     * @param array $attributes An array of attributes
+     *
+     * @return array An array of extracted values
+     *
+     * @api
+     */
+    public function extract($attributes)
+    {
+        $attributes = (array) $attributes;
+
+        $data = array();
+        foreach ($this as $node) {
+            $elements = array();
+            foreach ($attributes as $attribute) {
+                if ('_text' === $attribute) {
+                    $elements[] = $node->nodeValue;
+                } else {
+                    $elements[] = $node->getAttribute($attribute);
+                }
+            }
+
+            $data[] = count($attributes) > 1 ? $elements : $elements[0];
+        }
+
+        return $data;
+    }
+
+    /**
+     * Filters the list of nodes with an XPath expression.
+     *
+     * @param string $xpath An XPath expression
+     *
+     * @return Crawler A new instance of Crawler with the filtered list of nodes
+     *
+     * @api
+     */
+    public function filterXPath($xpath)
+    {
+        $document = new \DOMDocument('1.0', 'UTF-8');
+        $root = $document->appendChild($document->createElement('_root'));
+        foreach ($this as $node) {
+            $root->appendChild($document->importNode($node, true));
+        }
+
+        $domxpath = new \DOMXPath($document);
+
+        return new static($domxpath->query($xpath), $this->uri);
+    }
+
+    /**
+     * Filters the list of nodes with a CSS selector.
+     *
+     * This method only works if you have installed the CssSelector Symfony Component.
+     *
+     * @param string $selector A CSS selector
+     *
+     * @return Crawler A new instance of Crawler with the filtered list of nodes
+     *
+     * @throws \RuntimeException if the CssSelector Component is not available
+     *
+     * @api
+     */
+    public function filter($selector)
+    {
+        if (!class_exists('Symfony\\Component\\CssSelector\\CssSelector')) {
+            // @codeCoverageIgnoreStart
+            throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector is not installed (you can use filterXPath instead).');
+            // @codeCoverageIgnoreEnd
+        }
+
+        return $this->filterXPath(CssSelector::toXPath($selector));
+    }
+
+    /**
+     * Selects links by name or alt value for clickable images.
+     *
+     * @param string $value The link text
+     *
+     * @return Crawler A new instance of Crawler with the filtered list of nodes
+     *
+     * @api
+     */
+    public function selectLink($value)
+    {
+        $xpath  = sprintf('//a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s)] ', static::xpathLiteral(' '.$value.' ')).
+                            sprintf('| //a/img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]/ancestor::a', static::xpathLiteral(' '.$value.' '));
+
+        return $this->filterXPath($xpath);
+    }
+
+    /**
+     * Selects a button by name or alt value for images.
+     *
+     * @param string $value The button text
+     *
+     * @return Crawler A new instance of Crawler with the filtered list of nodes
+     *
+     * @api
+     */
+    public function selectButton($value)
+    {
+        $xpath = sprintf('//input[((@type="submit" or @type="button") and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', static::xpathLiteral(' '.$value.' ')).
+                         sprintf('or (@type="image" and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id="%s" or @name="%s"] ', static::xpathLiteral(' '.$value.' '), $value, $value).
+                         sprintf('| //button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id="%s" or @name="%s"]', static::xpathLiteral(' '.$value.' '), $value, $value);
+
+        return $this->filterXPath($xpath);
+    }
+
+    /**
+     * Returns a Link object for the first node in the list.
+     *
+     * @param string $method The method for the link (get by default)
+     *
+     * @return Link A Link instance
+     *
+     * @throws \InvalidArgumentException If the current node list is empty
+     *
+     * @api
+     */
+    public function link($method = 'get')
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $node = $this->getNode(0);
+
+        return new Link($node, $this->uri, $method);
+    }
+
+    /**
+     * Returns an array of Link objects for the nodes in the list.
+     *
+     * @return Link[] An array of Link instances
+     *
+     * @api
+     */
+    public function links()
+    {
+        $links = array();
+        foreach ($this as $node) {
+            $links[] = new Link($node, $this->uri, 'get');
+        }
+
+        return $links;
+    }
+
+    /**
+     * Returns a Form object for the first node in the list.
+     *
+     * @param array  $values An array of values for the form fields
+     * @param string $method The method for the form
+     *
+     * @return Form A Form instance
+     *
+     * @throws \InvalidArgumentException If the current node list is empty
+     *
+     * @api
+     */
+    public function form(array $values = null, $method = null)
+    {
+        if (!count($this)) {
+            throw new \InvalidArgumentException('The current node list is empty.');
+        }
+
+        $form = new Form($this->getNode(0), $this->uri, $method);
+
+        if (null !== $values) {
+            $form->setValues($values);
+        }
+
+        return $form;
+    }
+
+    /**
+     * Converts string for XPath expressions.
+     *
+     * Escaped characters are: quotes (") and apostrophe (').
+     *
+     *  Examples:
+     *  <code>
+     *     echo Crawler::xpathLiteral('foo " bar');
+     *     //prints 'foo " bar'
+     *
+     *     echo Crawler::xpathLiteral("foo ' bar");
+     *     //prints "foo ' bar"
+     *
+     *     echo Crawler::xpathLiteral('a\'b"c');
+     *     //prints concat('a', "'", 'b"c')
+     *  </code>
+     *
+     * @param string $s String to be escaped
+     *
+     * @return string Converted string
+     *
+     */
+    public static function xpathLiteral($s)
+    {
+        if (false === strpos($s, "'")) {
+            return sprintf("'%s'", $s);
+        }
+
+        if (false === strpos($s, '"')) {
+            return sprintf('"%s"', $s);
+        }
+
+        $string = $s;
+        $parts = array();
+        while (true) {
+            if (false !== $pos = strpos($string, "'")) {
+                $parts[] = sprintf("'%s'", substr($string, 0, $pos));
+                $parts[] = "\"'\"";
+                $string = substr($string, $pos + 1);
+            } else {
+                $parts[] = "'$string'";
+                break;
+            }
+        }
+
+        return sprintf("concat(%s)", implode($parts, ', '));
+    }
+
+    protected function getNode($position)
+    {
+        foreach ($this as $i => $node) {
+            if ($i == $position) {
+                return $node;
+            }
+        // @codeCoverageIgnoreStart
+        }
+
+        return null;
+        // @codeCoverageIgnoreEnd
+    }
+
+    protected function sibling($node, $siblingDir = 'nextSibling')
+    {
+        $nodes = array();
+
+        do {
+            if ($node !== $this->getNode(0) && $node->nodeType === 1) {
+                $nodes[] = $node;
+            }
+        } while ($node = $node->$siblingDir);
+
+        return $nodes;
+    }
+}

+ 307 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/ChoiceFormField.php

@@ -0,0 +1,307 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * ChoiceFormField represents a choice form field.
+ *
+ * It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class ChoiceFormField extends FormField
+{
+    /**
+     * @var string
+     */
+    private $type;
+    /**
+     * @var Boolean
+     */
+    private $multiple;
+    /**
+     * @var array
+     */
+    private $options;
+
+    /**
+     * Returns true if the field should be included in the submitted values.
+     *
+     * @return Boolean true if the field should be included in the submitted values, false otherwise
+     */
+    public function hasValue()
+    {
+        // don't send a value for unchecked checkboxes
+        if (in_array($this->type, array('checkbox', 'radio')) && null === $this->value) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the current selected option is disabled
+     *
+     * @return Boolean
+     */
+    public function isDisabled()
+    {
+        foreach ($this->options as $option) {
+            if ($option['value'] == $this->value && $option['disabled']) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     *
+     * @api
+     */
+    public function select($value)
+    {
+        $this->setValue($value);
+    }
+
+    /**
+     * Ticks a checkbox.
+     *
+     * @throws \LogicException When the type provided is not correct
+     *
+     * @api
+     */
+    public function tick()
+    {
+        if ('checkbox' !== $this->type) {
+            throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
+        }
+
+        $this->setValue(true);
+    }
+
+    /**
+     * Ticks a checkbox.
+     *
+     * @throws \LogicException When the type provided is not correct
+     *
+     * @api
+     */
+    public function untick()
+    {
+        if ('checkbox' !== $this->type) {
+            throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
+        }
+
+        $this->setValue(false);
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     *
+     * @throws \InvalidArgumentException When value type provided is not correct
+     */
+    public function setValue($value)
+    {
+        if ('checkbox' == $this->type && false === $value) {
+            // uncheck
+            $this->value = null;
+        } elseif ('checkbox' == $this->type && true === $value) {
+            // check
+            $this->value = $this->options[0]['value'];
+        } else {
+            if (is_array($value)) {
+                if (!$this->multiple) {
+                    throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name));
+                }
+
+                foreach ($value as $v) {
+                    if (!$this->containsOption($v, $this->options)) {
+                        throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues())));
+                    }
+                }
+            } elseif (!$this->containsOption($value, $this->options)) {
+                throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues())));
+            }
+
+            if ($this->multiple) {
+                $value = (array) $value;
+            }
+
+            if (is_array($value)) {
+                $this->value = $value;
+            } else {
+                parent::setValue($value);
+            }
+        }
+    }
+
+    /**
+     * Adds a choice to the current ones.
+     *
+     * This method should only be used internally.
+     *
+     * @param \DOMNode $node A \DOMNode
+     *
+     * @throws \LogicException When choice provided is not multiple nor radio
+     */
+    public function addChoice(\DOMNode $node)
+    {
+        if (!$this->multiple && 'radio' != $this->type) {
+            throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name));
+        }
+
+        $option = $this->buildOptionValue($node);
+        $this->options[] = $option;
+
+        if ($node->getAttribute('checked')) {
+            $this->value = $option['value'];
+        }
+    }
+
+    /**
+     * Returns the type of the choice field (radio, select, or checkbox).
+     *
+     * @return string The type
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Returns true if the field accepts multiple values.
+     *
+     * @return Boolean true if the field accepts multiple values, false otherwise
+     */
+    public function isMultiple()
+    {
+        return $this->multiple;
+    }
+
+    /**
+     * Initializes the form field.
+     *
+     * @throws \LogicException When node type is incorrect
+     */
+    protected function initialize()
+    {
+        if ('input' != $this->node->nodeName && 'select' != $this->node->nodeName) {
+            throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName));
+        }
+
+        if ('input' == $this->node->nodeName && 'checkbox' != $this->node->getAttribute('type') && 'radio' != $this->node->getAttribute('type')) {
+            throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type')));
+        }
+
+        $this->value = null;
+        $this->options = array();
+        $this->multiple = false;
+
+        if ('input' == $this->node->nodeName) {
+            $this->type = $this->node->getAttribute('type');
+            $optionValue = $this->buildOptionValue($this->node);
+            $this->options[] = $optionValue;
+
+            if ($this->node->getAttribute('checked')) {
+                $this->value = $optionValue['value'];
+            }
+        } else {
+            $this->type = 'select';
+            if ($this->node->hasAttribute('multiple')) {
+                $this->multiple = true;
+                $this->value = array();
+                $this->name = str_replace('[]', '', $this->name);
+            }
+
+            $found = false;
+            foreach ($this->xpath->query('descendant::option', $this->node) as $option) {
+                $this->options[] = $this->buildOptionValue($option);
+
+                if ($option->getAttribute('selected')) {
+                    $found = true;
+                    if ($this->multiple) {
+                        $this->value[] = $option->getAttribute('value');
+                    } else {
+                        $this->value = $option->getAttribute('value');
+                    }
+                }
+            }
+
+            // if no option is selected and if it is a simple select box, take the first option as the value
+            $option = $this->xpath->query('descendant::option', $this->node)->item(0);
+            if (!$found && !$this->multiple && $option instanceof \DOMElement) {
+                $this->value = $option->getAttribute('value');
+            }
+        }
+    }
+
+    /**
+     * Returns option value with associated disabled flag
+     *
+     * @param \DOMNode $node
+     *
+     * @return array
+     */
+    private function buildOptionValue($node)
+    {
+        $option = array();
+
+        $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : '1';
+        $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue;
+        $option['disabled'] = ($node->hasAttribute('disabled') && $node->getAttribute('disabled') == 'disabled');
+
+        return $option;
+    }
+
+    /**
+     * Checks whether given vale is in the existing options
+     *
+     * @param string $optionValue
+     * @param array  $options
+     *
+     * @return bool
+     */
+    public function containsOption($optionValue, $options)
+    {
+        foreach ($options as $option) {
+            if ($option['value'] == $optionValue) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns list of available field options
+     *
+     * @return array
+     */
+    public function availableOptionValues()
+    {
+        $values = array();
+
+        foreach ($this->options as $option) {
+            $values[] = $option['value'];
+        }
+
+        return $values;
+    }
+}

+ 106 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FileFormField.php

@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * FileFormField represents a file form field (an HTML file input tag).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class FileFormField extends FormField
+{
+    /**
+     * Sets the PHP error code associated with the field.
+     *
+     * @param integer $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION)
+     *
+     * @throws \InvalidArgumentException When error code doesn't exist
+     */
+    public function setErrorCode($error)
+    {
+        $codes = array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION);
+        if (!in_array($error, $codes)) {
+            throw new \InvalidArgumentException(sprintf('The error code %s is not valid.', $error));
+        }
+
+        $this->value = array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0);
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     *
+     * @api
+     */
+    public function upload($value)
+    {
+        $this->setValue($value);
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     */
+    public function setValue($value)
+    {
+        if (null !== $value && is_readable($value)) {
+            $error = UPLOAD_ERR_OK;
+            $size = filesize($value);
+            $name = basename($value);
+
+            // copy to a tmp location
+            $tmp = tempnam(sys_get_temp_dir(), 'upload');
+            unlink($tmp);
+            copy($value, $tmp);
+            $value = $tmp;
+        } else {
+            $error = UPLOAD_ERR_NO_FILE;
+            $size = 0;
+            $name = '';
+            $value = '';
+        }
+
+        $this->value = array('name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size);
+    }
+
+    /**
+     * Sets path to the file as string for simulating HTTP request
+     *
+     * @param string $path The path to the file
+     */
+    public function setFilePath($path)
+    {
+        parent::setValue($path);
+    }
+
+    /**
+     * Initializes the form field.
+     *
+     * @throws \LogicException When node type is incorrect
+     */
+    protected function initialize()
+    {
+        if ('input' != $this->node->nodeName) {
+            throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName));
+        }
+
+        if ('file' != $this->node->getAttribute('type')) {
+            throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is %s).', $this->node->getAttribute('type')));
+        }
+
+        $this->setValue(null);
+    }
+}

+ 122 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/FormField.php

@@ -0,0 +1,122 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * FormField is the abstract class for all form fields.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class FormField
+{
+    /**
+     * @var \DOMNode
+     */
+    protected $node;
+    /**
+     * @var string
+     */
+    protected $name;
+    /**
+     * @var string
+     */
+    protected $value;
+    /**
+     * @var \DOMDocument
+     */
+    protected $document;
+    /**
+     * @var \DOMXPath
+     */
+    protected $xpath;
+    /**
+     * @var Boolean
+     */
+    protected $disabled;
+
+    /**
+     * Constructor.
+     *
+     * @param \DOMNode $node The node associated with this field
+     */
+    public function __construct(\DOMNode $node)
+    {
+        $this->node = $node;
+        $this->name = $node->getAttribute('name');
+
+        $this->document = new \DOMDocument('1.0', 'UTF-8');
+        $this->node = $this->document->importNode($this->node, true);
+
+        $root = $this->document->appendChild($this->document->createElement('_root'));
+        $root->appendChild($this->node);
+        $this->xpath = new \DOMXPath($this->document);
+
+        $this->initialize();
+    }
+
+    /**
+     * Returns the name of the field.
+     *
+     * @return string The name of the field
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the value of the field.
+     *
+     * @return string|array The value of the field
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Sets the value of the field.
+     *
+     * @param string $value The value of the field
+     *
+     * @api
+     */
+    public function setValue($value)
+    {
+        $this->value = (string) $value;
+    }
+
+    /**
+     * Returns true if the field should be included in the submitted values.
+     *
+     * @return Boolean true if the field should be included in the submitted values, false otherwise
+     */
+    public function hasValue()
+    {
+        return true;
+    }
+
+    /**
+     * Check if the current field is disabled
+     *
+     * @return Boolean
+     */
+    public function isDisabled()
+    {
+        return $this->node->hasAttribute('disabled');
+    }
+
+    /**
+     * Initializes the form field.
+     */
+    abstract protected function initialize();
+}

+ 47 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/InputFormField.php

@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * InputFormField represents an input form field (an HTML input tag).
+ *
+ * For inputs with type of file, checkbox, or radio, there are other more
+ * specialized classes (cf. FileFormField and ChoiceFormField).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class InputFormField extends FormField
+{
+    /**
+     * Initializes the form field.
+     *
+     * @throws \LogicException When node type is incorrect
+     */
+    protected function initialize()
+    {
+        if ('input' != $this->node->nodeName && 'button' != $this->node->nodeName) {
+            throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName));
+        }
+
+        if ('checkbox' == $this->node->getAttribute('type')) {
+            throw new \LogicException('Checkboxes should be instances of ChoiceFormField.');
+        }
+
+        if ('file' == $this->node->getAttribute('type')) {
+            throw new \LogicException('File inputs should be instances of FileFormField.');
+        }
+
+        $this->value = $this->node->getAttribute('value');
+    }
+}

+ 39 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Field/TextareaFormField.php

@@ -0,0 +1,39 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Field;
+
+/**
+ * TextareaFormField represents a textarea form field (an HTML textarea tag).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class TextareaFormField extends FormField
+{
+    /**
+     * Initializes the form field.
+     *
+     * @throws \LogicException When node type is incorrect
+     */
+    protected function initialize()
+    {
+        if ('textarea' != $this->node->nodeName) {
+            throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName));
+        }
+
+        $this->value = null;
+        foreach ($this->node->childNodes as $node) {
+            $this->value .= $this->document->saveXML($node);
+        }
+    }
+}

+ 395 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Form.php

@@ -0,0 +1,395 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+use Symfony\Component\DomCrawler\Field\FormField;
+
+/**
+ * Form represents an HTML form.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Form extends Link implements \ArrayAccess
+{
+    /**
+     * @var \DOMNode
+     */
+    private $button;
+
+    /**
+     * @var Field\FormField[]
+     */
+    private $fields;
+
+    /**
+     * Constructor.
+     *
+     * @param \DOMNode $node       A \DOMNode instance
+     * @param string   $currentUri The URI of the page where the form is embedded
+     * @param string   $method     The method to use for the link (if null, it defaults to the method defined by the form)
+     *
+     * @throws \LogicException if the node is not a button inside a form tag
+     *
+     * @api
+     */
+    public function __construct(\DOMNode $node, $currentUri, $method = null)
+    {
+        parent::__construct($node, $currentUri, $method);
+
+        $this->initialize();
+    }
+
+    /**
+     * Gets the form node associated with this form.
+     *
+     * @return \DOMNode A \DOMNode instance
+     */
+    public function getFormNode()
+    {
+        return $this->node;
+    }
+
+    /**
+     * Sets the value of the fields.
+     *
+     * @param array $values An array of field values
+     *
+     * @return Form
+     *
+     * @api
+     */
+    public function setValues(array $values)
+    {
+        foreach ($values as $name => $value) {
+            $this->fields->set($name, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Gets the field values.
+     *
+     * The returned array does not include file fields (@see getFiles).
+     *
+     * @return array An array of field values.
+     *
+     * @api
+     */
+    public function getValues()
+    {
+        $values = array();
+        foreach ($this->fields->all() as $name => $field) {
+            if ($field->isDisabled()) {
+                continue;
+            }
+
+            if (!$field instanceof Field\FileFormField && $field->hasValue()) {
+                $values[$name] = $field->getValue();
+            }
+        }
+
+        return $values;
+    }
+
+    /**
+     * Gets the file field values.
+     *
+     * @return array An array of file field values.
+     *
+     * @api
+     */
+    public function getFiles()
+    {
+        if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) {
+            return array();
+        }
+
+        $files = array();
+
+        foreach ($this->fields->all() as $name => $field) {
+            if ($field->isDisabled()) {
+                continue;
+            }
+
+            if ($field instanceof Field\FileFormField) {
+                $files[$name] = $field->getValue();
+            }
+        }
+
+        return $files;
+    }
+
+    /**
+     * Gets the field values as PHP.
+     *
+     * This method converts fields with the array notation
+     * (like foo[bar] to arrays) like PHP does.
+     *
+     * @return array An array of field values.
+     *
+     * @api
+     */
+    public function getPhpValues()
+    {
+        $qs = http_build_query($this->getValues(), '', '&');
+        parse_str($qs, $values);
+
+        return $values;
+    }
+
+    /**
+     * Gets the file field values as PHP.
+     *
+     * This method converts fields with the array notation
+     * (like foo[bar] to arrays) like PHP does.
+     *
+     * @return array An array of field values.
+     *
+     * @api
+     */
+    public function getPhpFiles()
+    {
+        $qs = http_build_query($this->getFiles(), '', '&');
+        parse_str($qs, $values);
+
+        return $values;
+    }
+
+    /**
+     * Gets the URI of the form.
+     *
+     * The returned URI is not the same as the form "action" attribute.
+     * This method merges the value if the method is GET to mimics
+     * browser behavior.
+     *
+     * @return string The URI
+     *
+     * @api
+     */
+    public function getUri()
+    {
+        $uri = parent::getUri();
+
+        if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH')) && $queryString = http_build_query($this->getValues(), null, '&')) {
+            $sep = false === strpos($uri, '?') ? '?' : '&';
+            $uri .= $sep.$queryString;
+        }
+
+        return $uri;
+    }
+
+    protected function getRawUri()
+    {
+        return $this->node->getAttribute('action');
+    }
+
+    /**
+     * Gets the form method.
+     *
+     * If no method is defined in the form, GET is returned.
+     *
+     * @return string The method
+     *
+     * @api
+     */
+    public function getMethod()
+    {
+        if (null !== $this->method) {
+            return $this->method;
+        }
+
+        return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET';
+    }
+
+    /**
+     * Returns true if the named field exists.
+     *
+     * @param string $name The field name
+     *
+     * @return Boolean true if the field exists, false otherwise
+     *
+     * @api
+     */
+    public function has($name)
+    {
+        return $this->fields->has($name);
+    }
+
+    /**
+     * Removes a field from the form.
+     *
+     * @param string $name The field name
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     *
+     * @api
+     */
+    public function remove($name)
+    {
+        $this->fields->remove($name);
+    }
+
+    /**
+     * Gets a named field.
+     *
+     * @param string $name The field name
+     *
+     * @return FormField The field instance
+     *
+     * @throws \InvalidArgumentException When field is not present in this form
+     *
+     * @api
+     */
+    public function get($name)
+    {
+        return $this->fields->get($name);
+    }
+
+    /**
+     * Sets a named field.
+     *
+     * @param FormField $field The field
+     *
+     * @api
+     */
+    public function set(FormField $field)
+    {
+        $this->fields->add($field);
+    }
+
+    /**
+     * Gets all fields.
+     *
+     * @return FormField[] An array of fields
+     *
+     * @api
+     */
+    public function all()
+    {
+        return $this->fields->all();
+    }
+
+    /**
+     * Returns true if the named field exists.
+     *
+     * @param string $name The field name
+     *
+     * @return Boolean true if the field exists, false otherwise
+     */
+    public function offsetExists($name)
+    {
+        return $this->has($name);
+    }
+
+    /**
+     * Gets the value of a field.
+     *
+     * @param string $name The field name
+     *
+     * @return FormField The associated Field instance
+     *
+     * @throws \InvalidArgumentException if the field does not exist
+     */
+    public function offsetGet($name)
+    {
+        return $this->fields->get($name);
+    }
+
+    /**
+     * Sets the value of a field.
+     *
+     * @param string       $name  The field name
+     * @param string|array $value The value of the field
+     *
+     * @throws \InvalidArgumentException if the field does not exist
+     */
+    public function offsetSet($name, $value)
+    {
+        $this->fields->set($name, $value);
+    }
+
+    /**
+     * Removes a field from the form.
+     *
+     * @param string $name The field name
+     */
+    public function offsetUnset($name)
+    {
+        $this->fields->remove($name);
+    }
+
+    /**
+     * Sets current \DOMNode instance.
+     *
+     * @param \DOMNode $node A \DOMNode instance
+     *
+     * @throws \LogicException If given node is not a button or input or does not have a form ancestor
+     */
+    protected function setNode(\DOMNode $node)
+    {
+        $this->button = $node;
+        if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image')))) {
+            do {
+                // use the ancestor form element
+                if (null === $node = $node->parentNode) {
+                    throw new \LogicException('The selected node does not have a form ancestor.');
+                }
+            } while ('form' != $node->nodeName);
+        } elseif ('form' != $node->nodeName) {
+            throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
+        }
+
+        $this->node = $node;
+    }
+
+    private function initialize()
+    {
+        $this->fields = new FormFieldRegistry();
+
+        $document = new \DOMDocument('1.0', 'UTF-8');
+        $node = $document->importNode($this->node, true);
+        $button = $document->importNode($this->button, true);
+        $root = $document->appendChild($document->createElement('_root'));
+        $root->appendChild($node);
+        $root->appendChild($button);
+        $xpath = new \DOMXPath($document);
+
+        foreach ($xpath->query('descendant::input | descendant::button | descendant::textarea | descendant::select', $root) as $node) {
+            if (!$node->hasAttribute('name') || !$node->getAttribute('name')) {
+                continue;
+            }
+
+            $nodeName = $node->nodeName;
+
+            if ($node === $button) {
+                $this->set(new Field\InputFormField($node));
+            } elseif ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type')) {
+                $this->set(new Field\ChoiceFormField($node));
+            } elseif ('input' == $nodeName && 'radio' == $node->getAttribute('type')) {
+                if ($this->has($node->getAttribute('name'))) {
+                    $this->get($node->getAttribute('name'))->addChoice($node);
+                } else {
+                    $this->set(new Field\ChoiceFormField($node));
+                }
+            } elseif ('input' == $nodeName && 'file' == $node->getAttribute('type')) {
+                $this->set(new Field\FileFormField($node));
+            } elseif ('input' == $nodeName && !in_array($node->getAttribute('type'), array('submit', 'button', 'image'))) {
+                $this->set(new Field\InputFormField($node));
+            } elseif ('textarea' == $nodeName) {
+                $this->set(new Field\TextareaFormField($node));
+            }
+        }
+    }
+}

+ 216 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/FormFieldRegistry.php

@@ -0,0 +1,216 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+use Symfony\Component\DomCrawler\Field\FormField;
+
+/**
+ * This is an internal class that must not be used directly.
+ */
+class FormFieldRegistry
+{
+    private $fields = array();
+
+    private $base;
+
+    /**
+     * Adds a field to the registry.
+     *
+     * @param FormField $field The field
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     */
+    public function add(FormField $field)
+    {
+        $segments = $this->getSegments($field->getName());
+
+        $target =& $this->fields;
+        while ($segments) {
+            if (!is_array($target)) {
+                $target = array();
+            }
+            $path = array_shift($segments);
+            if ('' === $path) {
+                $target =& $target[];
+            } else {
+                $target =& $target[$path];
+            }
+        }
+        $target = $field;
+    }
+
+    /**
+     * Removes a field and its children from the registry.
+     *
+     * @param string $name The fully qualified name of the base field
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     */
+    public function remove($name)
+    {
+        $segments = $this->getSegments($name);
+        $target =& $this->fields;
+        while (count($segments) > 1) {
+            $path = array_shift($segments);
+            if (!array_key_exists($path, $target)) {
+                return;
+            }
+            $target =& $target[$path];
+        }
+        unset($target[array_shift($segments)]);
+    }
+
+    /**
+     * Returns the value of the field and its children.
+     *
+     * @param string $name The fully qualified name of the field
+     *
+     * @return mixed The value of the field
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     * @throws \InvalidArgumentException if the field does not exist
+     */
+    public function &get($name)
+    {
+        $segments = $this->getSegments($name);
+        $target =& $this->fields;
+        while ($segments) {
+            $path = array_shift($segments);
+            if (!array_key_exists($path, $target)) {
+                throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path));
+            }
+            $target =& $target[$path];
+        }
+
+        return $target;
+    }
+
+    /**
+     * Tests whether the form has the given field.
+     *
+     * @param string $name The fully qualified name of the field
+     *
+     * @return Boolean Whether the form has the given field
+     */
+    public function has($name)
+    {
+        try {
+            $this->get($name);
+
+            return true;
+        } catch (\InvalidArgumentException $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Set the value of a field and its children.
+     *
+     * @param string $name  The fully qualified name of the field
+     * @param mixed  $value The value
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     * @throws \InvalidArgumentException if the field does not exist
+     */
+    public function set($name, $value)
+    {
+        $target =& $this->get($name);
+        if (!is_array($value) || $target instanceof Field\ChoiceFormField) {
+            $target->setValue($value);
+        } else {
+            $fields = self::create($name, $value);
+            foreach ($fields->all() as $k => $v) {
+                $this->set($k, $v);
+            }
+        }
+    }
+
+    /**
+     * Returns the list of field with their value.
+     *
+     * @return array The list of fields as array((string) Fully qualified name => (mixed) value)
+     */
+    public function all()
+    {
+        return $this->walk($this->fields, $this->base);
+    }
+
+    /**
+     * Creates an instance of the class.
+     *
+     * This function is made private because it allows overriding the $base and
+     * the $values properties without any type checking.
+     *
+     * @param string $base   The fully qualified name of the base field
+     * @param array  $values The values of the fields
+     *
+     * @return FormFieldRegistry
+     */
+    private static function create($base, array $values)
+    {
+        $registry = new static();
+        $registry->base = $base;
+        $registry->fields = $values;
+
+        return $registry;
+    }
+
+    /**
+     * Transforms a PHP array in a list of fully qualified name / value.
+     *
+     * @param array  $array  The PHP array
+     * @param string $base   The name of the base field
+     * @param array  $output The initial values
+     *
+     * @return array The list of fields as array((string) Fully qualified name => (mixed) value)
+     */
+    private function walk(array $array, $base = '', array &$output = array())
+    {
+        foreach ($array as $k => $v) {
+            $path = empty($base) ? $k : sprintf("%s[%s]", $base, $k);
+            if (is_array($v)) {
+                $this->walk($v, $path, $output);
+            } else {
+                $output[$path] = $v;
+            }
+        }
+
+        return $output;
+    }
+
+    /**
+     * Splits a field name into segments as a web browser would do.
+     *
+     * <code>
+     *     getSegments('base[foo][3][]') = array('base', 'foo, '3', '');
+     * </code>
+     *
+     * @param string $name The name of the field
+     *
+     * @return array The list of segments
+     *
+     * @throws \InvalidArgumentException when the name is malformed
+     */
+    private function getSegments($name)
+    {
+        if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) {
+            $segments = array($m['base']);
+            while (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $m['extra'], $m)) {
+                $segments[] = $m['segment'];
+            }
+
+            return $segments;
+        }
+
+        throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name));
+    }
+}

+ 19 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2004-2013 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 162 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Link.php

@@ -0,0 +1,162 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler;
+
+/**
+ * Link represents an HTML link (an HTML a tag).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Link
+{
+    /**
+     * @var \DOMNode A \DOMNode instance
+     */
+    protected $node;
+    /**
+     * @var string The method to use for the link
+     */
+    protected $method;
+    /**
+     * @var string The URI of the page where the link is embedded (or the base href)
+     */
+    protected $currentUri;
+
+    /**
+     * Constructor.
+     *
+     * @param \DOMNode $node       A \DOMNode instance
+     * @param string   $currentUri The URI of the page where the link is embedded (or the base href)
+     * @param string   $method     The method to use for the link (get by default)
+     *
+     * @throws \InvalidArgumentException if the node is not a link
+     *
+     * @api
+     */
+    public function __construct(\DOMNode $node, $currentUri, $method = 'GET')
+    {
+        if (!in_array(strtolower(substr($currentUri, 0, 4)), array('http', 'file'))) {
+            throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri));
+        }
+
+        $this->setNode($node);
+        $this->method = $method ? strtoupper($method) : null;
+        $this->currentUri = $currentUri;
+    }
+
+    /**
+     * Gets the node associated with this link.
+     *
+     * @return \DOMNode A \DOMNode instance
+     */
+    public function getNode()
+    {
+        return $this->node;
+    }
+
+    /**
+     * Gets the method associated with this link.
+     *
+     * @return string The method
+     *
+     * @api
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * Gets the URI associated with this link.
+     *
+     * @return string The URI
+     *
+     * @api
+     */
+    public function getUri()
+    {
+        $uri = trim($this->getRawUri());
+
+        // absolute URL?
+        if (null !== parse_url($uri, PHP_URL_SCHEME)) {
+            return $uri;
+        }
+
+        // empty URI
+        if (!$uri) {
+            return $this->currentUri;
+        }
+
+        // only an anchor
+        if ('#' === $uri[0]) {
+            $baseUri = $this->currentUri;
+            if (false !== $pos = strpos($baseUri, '#')) {
+                $baseUri = substr($baseUri, 0, $pos);
+            }
+
+            return $baseUri.$uri;
+        }
+
+        // only a query string
+        if ('?' === $uri[0]) {
+            $baseUri = $this->currentUri;
+
+            // remove the query string from the current uri
+            if (false !== $pos = strpos($baseUri, '?')) {
+                $baseUri = substr($baseUri, 0, $pos);
+            }
+
+            return $baseUri.$uri;
+        }
+
+        // absolute URL with relative schema
+        if (0 === strpos($uri, '//')) {
+            return preg_replace('#^([^/]*)//.*$#', '$1', $this->currentUri).$uri;
+        }
+
+        // absolute path
+        if ('/' === $uri[0]) {
+            return preg_replace('#^(.*?//[^/]+)(?:\/.*)?$#', '$1', $this->currentUri).$uri;
+        }
+
+        // relative path
+        return substr($this->currentUri, 0, strrpos($this->currentUri, '/') + 1).$uri;
+    }
+
+    /**
+     * Returns raw uri data.
+     *
+     * @return string
+     */
+    protected function getRawUri()
+    {
+        return $this->node->getAttribute('href');
+    }
+
+    /**
+     * Sets current \DOMNode instance.
+     *
+     * @param \DOMNode $node A \DOMNode instance
+     *
+     * @throws \LogicException If given node is not an anchor
+     */
+    protected function setNode(\DOMNode $node)
+    {
+        if ('a' != $node->nodeName) {
+            throw new \LogicException(sprintf('Unable to click on a "%s" tag.', $node->nodeName));
+        }
+
+        $this->node = $node;
+    }
+}

+ 32 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/README.md

@@ -0,0 +1,32 @@
+DomCrawler Component
+====================
+
+DomCrawler eases DOM navigation for HTML and XML documents.
+
+If you are familiar with jQuery, DomCrawler is a PHP equivalent:
+
+    use Symfony\Component\DomCrawler\Crawler;
+
+    $crawler = new Crawler();
+    $crawler->addContent('<html><body><p>Hello World!</p></body></html>');
+
+    print $crawler->filterXPath('descendant-or-self::body/p')->text();
+
+If you are also using the CssSelector component, you can use CSS Selectors
+instead of XPath expressions:
+
+    use Symfony\Component\DomCrawler\Crawler;
+
+    $crawler = new Crawler();
+    $crawler->addContent('<html><body><p>Hello World!</p></body></html>');
+
+    print $crawler->filter('body > p')->text();
+
+Resources
+---------
+
+You can run the unit tests with the following command:
+
+    $ cd path/to/Symfony/Component/DomCrawler/
+    $ composer.phar install --dev
+    $ phpunit

+ 618 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/CrawlerTest.php

@@ -0,0 +1,618 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests;
+
+use Symfony\Component\DomCrawler\Crawler;
+
+class CrawlerTest extends \PHPUnit_Framework_TestCase
+{
+    public function testConstructor()
+    {
+        $crawler = new Crawler();
+        $this->assertCount(0, $crawler, '__construct() returns an empty crawler');
+
+        $crawler = new Crawler(new \DOMNode());
+        $this->assertCount(1, $crawler, '__construct() takes a node as a first argument');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::add
+     */
+    public function testAdd()
+    {
+        $crawler = new Crawler();
+        $crawler->add($this->createDomDocument());
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMDocument');
+
+        $crawler = new Crawler();
+        $crawler->add($this->createNodeList());
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList');
+
+        foreach ($this->createNodeList() as $node) {
+            $list[] = $node;
+        }
+        $crawler = new Crawler();
+        $crawler->add($list);
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from an array of nodes');
+
+        $crawler = new Crawler();
+        $crawler->add($this->createNodeList()->item(0));
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from an \DOMNode');
+
+        $crawler = new Crawler();
+        $crawler->add('<html><body>Foo</body></html>');
+        $this->assertEquals('Foo', $crawler->filterXPath('//body')->text(), '->add() adds nodes from a string');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContent()
+    {
+        $crawler = new Crawler();
+        $crawler->addHtmlContent('<html><div class="foo"></html>', 'UTF-8');
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addHtmlContent() adds nodes from an HTML string');
+
+        $crawler->addHtmlContent('<html><head><base href="http://symfony.com"></head><a href="/contact"></a></html>', 'UTF-8');
+
+        $this->assertEquals('http://symfony.com', $crawler->filterXPath('//base')->attr('href'), '->addHtmlContent() adds nodes from an HTML string');
+        $this->assertEquals('http://symfony.com/contact', $crawler->filterXPath('//a')->link()->getUri(), '->addHtmlContent() adds nodes from an HTML string');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentCharset()
+    {
+        $crawler = new Crawler();
+        $crawler->addHtmlContent('<html><div class="foo">Tiếng Việt</html>', 'UTF-8');
+
+        $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text());
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentInvalidBaseTag()
+    {
+        $crawler = new Crawler(null, 'http://symfony.com');
+
+        $crawler->addHtmlContent('<html><head><base target="_top"></head><a href="/contact"></a></html>', 'UTF-8');
+
+        $this->assertEquals('http://symfony.com/contact', current($crawler->filterXPath('//a')->links())->getUri(), '->addHtmlContent() correctly handles a non-existent base tag href attribute');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentUnsupportedCharset()
+    {
+        $crawler = new Crawler();
+        $crawler->addHtmlContent(file_get_contents(__DIR__.'/Fixtures/windows-1250.html'), 'Windows-1250');
+
+        $this->assertEquals('Žťčýů', $crawler->filterXPath('//p')->text());
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addHtmlContent
+     */
+    public function testAddHtmlContentWithErrors()
+    {
+        libxml_use_internal_errors(true);
+
+        $crawler = new Crawler();
+        $crawler->addHtmlContent(<<<EOF
+<!DOCTYPE html>
+<html>
+    <head>
+    </head>
+    <body>
+        <nav><a href="#"><a href="#"></nav>
+    </body>
+</html>
+EOF
+        , 'UTF-8');
+
+        $errors = libxml_get_errors();
+        $this->assertCount(1, $errors);
+        $this->assertEquals("Tag nav invalid\n", $errors[0]->message);
+
+        libxml_clear_errors();
+        libxml_use_internal_errors(false);
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent
+     */
+    public function testAddXmlContent()
+    {
+        $crawler = new Crawler();
+        $crawler->addXmlContent('<html><div class="foo"></div></html>', 'UTF-8');
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addXmlContent() adds nodes from an XML string');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent
+     */
+    public function testAddXmlContentCharset()
+    {
+        $crawler = new Crawler();
+        $crawler->addXmlContent('<html><div class="foo">Tiếng Việt</div></html>', 'UTF-8');
+
+        $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text());
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addXmlContent
+     */
+    public function testAddXmlContentWithErrors()
+    {
+        libxml_use_internal_errors(true);
+
+        $crawler = new Crawler();
+        $crawler->addXmlContent(<<<EOF
+<!DOCTYPE html>
+<html>
+    <head>
+    </head>
+    <body>
+        <nav><a href="#"><a href="#"></nav>
+    </body>
+</html>
+EOF
+        , 'UTF-8');
+
+        $this->assertTrue(count(libxml_get_errors()) > 1);
+
+        libxml_clear_errors();
+        libxml_use_internal_errors(false);
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addContent
+     */
+    public function testAddContent()
+    {
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></html>', 'text/html; charset=UTF-8');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></html>', 'text/html; charset=UTF-8; dir=RTL');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string with extended content type');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></html>');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() uses text/html as the default type');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></div></html>', 'text/xml; charset=UTF-8');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string');
+
+        $crawler = new Crawler();
+        $crawler->addContent('<html><div class="foo"></div></html>', 'text/xml');
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string');
+
+        $crawler = new Crawler();
+        $crawler->addContent('foo bar', 'text/plain');
+        $this->assertCount(0, $crawler, '->addContent() does nothing if the type is not (x|ht)ml');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addDocument
+     */
+    public function testAddDocument()
+    {
+        $crawler = new Crawler();
+        $crawler->addDocument($this->createDomDocument());
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addDocument() adds nodes from a \DOMDocument');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addNodeList
+     */
+    public function testAddNodeList()
+    {
+        $crawler = new Crawler();
+        $crawler->addNodeList($this->createNodeList());
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodeList() adds nodes from a \DOMNodeList');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addNodes
+     */
+    public function testAddNodes()
+    {
+        foreach ($this->createNodeList() as $node) {
+            $list[] = $node;
+        }
+
+        $crawler = new Crawler();
+        $crawler->addNodes($list);
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodes() adds nodes from an array of nodes');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::addNode
+     */
+    public function testAddNode()
+    {
+        $crawler = new Crawler();
+        $crawler->addNode($this->createNodeList()->item(0));
+
+        $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNode() adds nodes from an \DOMNode');
+    }
+
+    public function testClear()
+    {
+        $crawler = new Crawler(new \DOMNode());
+        $crawler->clear();
+        $this->assertCount(0, $crawler, '->clear() removes all the nodes from the crawler');
+    }
+
+    public function testEq()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li');
+        $this->assertNotSame($crawler, $crawler->eq(0), '->eq() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->eq() returns a new instance of a crawler');
+
+        $this->assertEquals('Two', $crawler->eq(1)->text(), '->eq() returns the nth node of the list');
+        $this->assertCount(0, $crawler->eq(100), '->eq() returns an empty crawler if the nth node does not exist');
+    }
+
+    public function testEach()
+    {
+        $data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each(function ($node, $i) {
+            return $i.'-'.$node->nodeValue;
+        });
+
+        $this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list');
+    }
+
+    public function testReduce()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
+        $nodes = $crawler->reduce(function ($node, $i) {
+            return $i == 1 ? false : true;
+        });
+        $this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler');
+
+        $this->assertCount(2, $nodes, '->reduce() filters the nodes in the list');
+    }
+
+    public function testAttr()
+    {
+        $this->assertEquals('first', $this->createTestCrawler()->filterXPath('//li')->attr('class'), '->attr() returns the attribute of the first element of the node list');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->attr('class');
+            $this->fail('->attr() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->attr() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testText()
+    {
+        $this->assertEquals('One', $this->createTestCrawler()->filterXPath('//li')->text(), '->text() returns the node value of the first element of the node list');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->text();
+            $this->fail('->text() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->text() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testExtract()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
+
+        $this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list');
+        $this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list');
+
+        $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::filterXPath
+     */
+    public function testFilterXPath()
+    {
+        $crawler = $this->createTestCrawler();
+        $this->assertNotSame($crawler, $crawler->filterXPath('//li'), '->filterXPath() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filterXPath() returns a new instance of a crawler');
+
+        $crawler = $this->createTestCrawler()->filterXPath('//ul');
+
+        $this->assertCount(6, $crawler->filterXPath('//li'), '->filterXPath() filters the node list with the XPath expression');
+    }
+
+    /**
+     * @covers Symfony\Component\DomCrawler\Crawler::filter
+     */
+    public function testFilter()
+    {
+        if (!class_exists('Symfony\Component\CssSelector\CssSelector')) {
+            $this->markTestSkipped('The "CssSelector" component is not available');
+        }
+
+        $crawler = $this->createTestCrawler();
+        $this->assertNotSame($crawler, $crawler->filter('li'), '->filter() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filter() returns a new instance of a crawler');
+
+        $crawler = $this->createTestCrawler()->filter('ul');
+
+        $this->assertCount(6, $crawler->filter('li'), '->filter() filters the node list with the CSS selector');
+    }
+
+    public function testSelectLink()
+    {
+        $crawler = $this->createTestCrawler();
+        $this->assertNotSame($crawler, $crawler->selectLink('Foo'), '->selectLink() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectLink() returns a new instance of a crawler');
+
+        $this->assertCount(1, $crawler->selectLink('Fabien\'s Foo'), '->selectLink() selects links by the node values');
+        $this->assertCount(1, $crawler->selectLink('Fabien\'s Bar'), '->selectLink() selects links by the alt attribute of a clickable image');
+
+        $this->assertCount(2, $crawler->selectLink('Fabien"s Foo'), '->selectLink() selects links by the node values');
+        $this->assertCount(2, $crawler->selectLink('Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image');
+
+        $this->assertCount(1, $crawler->selectLink('\' Fabien"s Foo'), '->selectLink() selects links by the node values');
+        $this->assertCount(1, $crawler->selectLink('\' Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image');
+
+        $this->assertCount(4, $crawler->selectLink('Foo'), '->selectLink() selects links by the node values');
+        $this->assertCount(4, $crawler->selectLink('Bar'), '->selectLink() selects links by the node values');
+    }
+
+    public function testSelectButton()
+    {
+        $crawler = $this->createTestCrawler();
+        $this->assertNotSame($crawler, $crawler->selectButton('FooValue'), '->selectButton() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectButton() returns a new instance of a crawler');
+
+        $this->assertEquals(1, $crawler->selectButton('FooValue')->count(), '->selectButton() selects buttons');
+        $this->assertEquals(1, $crawler->selectButton('FooName')->count(), '->selectButton() selects buttons');
+        $this->assertEquals(1, $crawler->selectButton('FooId')->count(), '->selectButton() selects buttons');
+
+        $this->assertEquals(1, $crawler->selectButton('BarValue')->count(), '->selectButton() selects buttons');
+        $this->assertEquals(1, $crawler->selectButton('BarName')->count(), '->selectButton() selects buttons');
+        $this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons');
+    }
+
+    public function testLink()
+    {
+        $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $crawler->link(), '->link() returns a Link instance');
+
+        $this->assertEquals('POST', $crawler->link('post')->getMethod(), '->link() takes a method as its argument');
+
+        $crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('GetLink');
+        $this->assertEquals('http://example.com/bar?get=param', $crawler->link()->getUri(), '->link() returns a Link instance');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->link();
+            $this->fail('->link() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->link() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testLinks()
+    {
+        $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
+        $this->assertInternalType('array', $crawler->links(), '->links() returns an array');
+
+        $this->assertCount(4, $crawler->links(), '->links() returns an array');
+        $links = $crawler->links();
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $links[0], '->links() returns an array of Link instances');
+
+        $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty');
+    }
+
+    public function testForm()
+    {
+        $crawler = $this->createTestCrawler('http://example.com/bar/')->selectButton('FooValue');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance');
+
+        $this->assertEquals(array('FooName' => 'FooBar'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument');
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->form();
+            $this->fail('->form() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->form() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testLast()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
+        $this->assertNotSame($crawler, $crawler->last(), '->last() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->last() returns a new instance of a crawler');
+
+        $this->assertEquals('Three', $crawler->last()->text());
+    }
+
+    public function testFirst()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li');
+        $this->assertNotSame($crawler, $crawler->first(), '->first() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->first() returns a new instance of a crawler');
+
+        $this->assertEquals('One', $crawler->first()->text());
+    }
+
+    public function testSiblings()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1);
+        $this->assertNotSame($crawler, $crawler->siblings(), '->siblings() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->siblings() returns a new instance of a crawler');
+
+        $nodes = $crawler->siblings();
+        $this->assertEquals(2, $nodes->count());
+        $this->assertEquals('One', $nodes->eq(0)->text());
+        $this->assertEquals('Three', $nodes->eq(1)->text());
+
+        $nodes = $this->createTestCrawler()->filterXPath('//li')->eq(0)->siblings();
+        $this->assertEquals(2, $nodes->count());
+        $this->assertEquals('Two', $nodes->eq(0)->text());
+        $this->assertEquals('Three', $nodes->eq(1)->text());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->siblings();
+            $this->fail('->siblings() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->siblings() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testNextAll()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1);
+        $this->assertNotSame($crawler, $crawler->nextAll(), '->nextAll() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->nextAll() returns a new instance of a crawler');
+
+        $nodes = $crawler->nextAll();
+        $this->assertEquals(1, $nodes->count());
+        $this->assertEquals('Three', $nodes->eq(0)->text());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->nextAll();
+            $this->fail('->nextAll() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->nextAll() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testPreviousAll()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(2);
+        $this->assertNotSame($crawler, $crawler->previousAll(), '->previousAll() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->previousAll() returns a new instance of a crawler');
+
+        $nodes = $crawler->previousAll();
+        $this->assertEquals(2, $nodes->count());
+        $this->assertEquals('Two', $nodes->eq(0)->text());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->previousAll();
+            $this->fail('->previousAll() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->previousAll() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testChildren()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//ul');
+        $this->assertNotSame($crawler, $crawler->children(), '->children() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->children() returns a new instance of a crawler');
+
+        $nodes = $crawler->children();
+        $this->assertEquals(3, $nodes->count());
+        $this->assertEquals('One', $nodes->eq(0)->text());
+        $this->assertEquals('Two', $nodes->eq(1)->text());
+        $this->assertEquals('Three', $nodes->eq(2)->text());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->children();
+            $this->fail('->children() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->children() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function testParents()
+    {
+        $crawler = $this->createTestCrawler()->filterXPath('//li[1]');
+        $this->assertNotSame($crawler, $crawler->parents(), '->parents() returns a new instance of a crawler');
+        $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->parents() returns a new instance of a crawler');
+
+        $nodes = $crawler->parents();
+        $this->assertEquals(3, $nodes->count());
+
+        $nodes = $this->createTestCrawler()->filterXPath('//html')->parents();
+        $this->assertEquals(0, $nodes->count());
+
+        try {
+            $this->createTestCrawler()->filterXPath('//ol')->parents();
+            $this->fail('->parents() throws an \InvalidArgumentException if the node list is empty');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->parents() throws an \InvalidArgumentException if the node list is empty');
+        }
+    }
+
+    public function createTestCrawler($uri = null)
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+            <html>
+                <body>
+                    <a href="foo">Foo</a>
+                    <a href="/foo">   Fabien\'s Foo   </a>
+                    <a href="/foo">Fabien"s Foo</a>
+                    <a href="/foo">\' Fabien"s Foo</a>
+
+                    <a href="/bar"><img alt="Bar"/></a>
+                    <a href="/bar"><img alt="   Fabien\'s Bar   "/></a>
+                    <a href="/bar"><img alt="Fabien&quot;s Bar"/></a>
+                    <a href="/bar"><img alt="\' Fabien&quot;s Bar"/></a>
+
+                    <a href="?get=param">GetLink</a>
+
+                    <form action="foo">
+                        <input type="submit" value="FooValue" name="FooName" id="FooId" />
+                        <input type="button" value="BarValue" name="BarName" id="BarId" />
+                        <button value="ButtonValue" name="ButtonName" id="ButtonId" />
+                    </form>
+
+                    <ul class="first">
+                        <li class="first">One</li>
+                        <li>Two</li>
+                        <li>Three</li>
+                    </ul>
+                    <ul>
+                        <li>One Bis</li>
+                        <li>Two Bis</li>
+                        <li>Three Bis</li>
+                    </ul>
+                </body>
+            </html>
+        ');
+
+        return new Crawler($dom, $uri);
+    }
+
+    protected function createDomDocument()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadXML('<html><div class="foo"></div></html>');
+
+        return $dom;
+    }
+
+    protected function createNodeList()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadXML('<html><div class="foo"></div></html>');
+        $domxpath = new \DOMXPath($dom);
+
+        return $domxpath->query('//div');
+    }
+}

+ 329 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/ChoiceFormFieldTest.php

@@ -0,0 +1,329 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\ChoiceFormField;
+
+class ChoiceFormFieldTest extends FormFieldTestCase
+{
+    public function testInitialize()
+    {
+        $node = $this->createNode('textarea', '');
+        try {
+            $field = new ChoiceFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not an input or a select');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input or a select');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'text'));
+        try {
+            $field = new ChoiceFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio');
+        }
+    }
+
+    public function testGetType()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('radio', $field->getType(), '->getType() returns radio for radio buttons');
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('checkbox', $field->getType(), '->getType() returns radio for a checkbox');
+
+        $node = $this->createNode('select', '');
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('select', $field->getType(), '->getType() returns radio for a select');
+    }
+
+    public function testIsMultiple()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons');
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for checkboxes');
+
+        $node = $this->createNode('select', '');
+        $field = new ChoiceFormField($node);
+
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for selects without the multiple attribute');
+
+        $node = $this->createNode('select', '', array('multiple' => 'multiple'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertTrue($field->isMultiple(), '->isMultiple() returns true for selects with the multiple attribute');
+    }
+
+    public function testSelects()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+
+        $this->assertTrue($field->hasValue(), '->hasValue() returns true for selects');
+        $this->assertEquals('foo', $field->getValue(), '->getValue() returns the first option if none are selected');
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false when no multiple attribute is defined');
+
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => true));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('bar', $field->getValue(), '->getValue() returns the selected option');
+
+        $field->setValue('foo');
+        $this->assertEquals('foo', $field->getValue(), '->setValue() changes the selected option');
+
+        try {
+            $field->setValue('foobar');
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the selected options');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the selected options');
+        }
+
+        try {
+            $field->setValue(array('foobar'));
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is an array');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is an array');
+        }
+    }
+
+    public function testMultipleSelects()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals(array(), $field->getValue(), '->setValue() returns an empty array if multiple is true and no option is selected');
+
+        $field->setValue('foo');
+        $this->assertEquals(array('foo'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
+
+        $field->setValue('bar');
+        $this->assertEquals(array('bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
+
+        $field->setValue(array('foo', 'bar'));
+        $this->assertEquals(array('foo', 'bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
+
+        $node = $this->createSelectNode(array('foo' => true, 'bar' => true), array('multiple' => 'multiple'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals(array('foo', 'bar'), $field->getValue(), '->getValue() returns the selected options');
+
+        try {
+            $field->setValue(array('foobar'));
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the options');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the options');
+        }
+    }
+
+    public function testRadioButtons()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar'));
+        $field->addChoice($node);
+
+        $this->assertFalse($field->hasValue(), '->hasValue() returns false when no radio button is selected');
+        $this->assertNull($field->getValue(), '->getValue() returns null if no radio button is selected');
+        $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons');
+
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => 'checked'));
+        $field->addChoice($node);
+
+        $this->assertTrue($field->hasValue(), '->hasValue() returns true when a radio button is selected');
+        $this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
+
+        $field->setValue('foo');
+        $this->assertEquals('foo', $field->getValue(), '->setValue() changes the selected radio button');
+
+        try {
+            $field->setValue('foobar');
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the radio button values');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the radio button values');
+        }
+    }
+
+    public function testRadioButtonIsDisabled()
+    {
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo', 'disabled' => 'disabled'));
+        $field = new ChoiceFormField($node);
+        $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar'));
+        $field->addChoice($node);
+
+        $field->select('foo');
+        $this->assertEquals('foo', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
+        $this->assertTrue($field->isDisabled());
+
+        $field->select('bar');
+        $this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
+        $this->assertFalse($field->isDisabled());
+    }
+
+    public function testCheckboxes()
+    {
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertFalse($field->hasValue(), '->hasValue() returns false when the checkbox is not checked');
+        $this->assertNull($field->getValue(), '->getValue() returns null if the checkbox is not checked');
+        $this->assertFalse($field->isMultiple(), '->hasValue() returns false for checkboxes');
+        try {
+            $field->addChoice(new \DOMNode());
+            $this->fail('->addChoice() throws a \LogicException for checkboxes');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException for checkboxes');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertTrue($field->hasValue(), '->hasValue() returns true when the checkbox is checked');
+        $this->assertEquals('1', $field->getValue(), '->getValue() returns 1 if the checkbox is checked and has no value attribute');
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $this->assertEquals('foo', $field->getValue(), '->getValue() returns the value attribute if the checkbox is checked');
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo'));
+        $field = new ChoiceFormField($node);
+
+        $field->setValue(false);
+        $this->assertNull($field->getValue(), '->setValue() unchecks the checkbox is value is false');
+
+        $field->setValue(true);
+        $this->assertEquals('foo', $field->getValue(), '->setValue() checks the checkbox is value is true');
+
+        try {
+            $field->setValue('bar');
+            $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one from the value attribute');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one from the value attribute');
+        }
+    }
+
+    public function testTick()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+
+        try {
+            $field->tick();
+            $this->fail('->tick() throws a \LogicException for select boxes');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->tick() throws a \LogicException for select boxes');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name'));
+        $field = new ChoiceFormField($node);
+        $field->tick();
+        $this->assertEquals(1, $field->getValue(), '->tick() ticks checkboxes');
+    }
+
+    public function testUntick()
+    {
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+
+        try {
+            $field->untick();
+            $this->fail('->untick() throws a \LogicException for select boxes');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->untick() throws a \LogicException for select boxes');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked'));
+        $field = new ChoiceFormField($node);
+        $field->untick();
+        $this->assertNull($field->getValue(), '->untick() unticks checkboxes');
+    }
+
+    public function testSelect()
+    {
+        $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked'));
+        $field = new ChoiceFormField($node);
+        $field->select(true);
+        $this->assertEquals(1, $field->getValue(), '->select() changes the value of the field');
+        $field->select(false);
+        $this->assertNull($field->getValue(), '->select() changes the value of the field');
+
+        $node = $this->createSelectNode(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+        $field->select('foo');
+        $this->assertEquals('foo', $field->getValue(), '->select() changes the selected option');
+    }
+
+    public function testOptionWithNoValue()
+    {
+        $node = $this->createSelectNodeWithEmptyOption(array('foo' => false, 'bar' => false));
+        $field = new ChoiceFormField($node);
+        $field->select('foo');
+        $this->assertEquals('foo', $field->getValue(), '->select() changes the selected option');
+    }
+
+    protected function createSelectNode($options, $attributes = array())
+    {
+        $document = new \DOMDocument();
+        $node = $document->createElement('select');
+
+        foreach ($attributes as $name => $value) {
+            $node->setAttribute($name, $value);
+        }
+        $node->setAttribute('name', 'name');
+
+        foreach ($options as $value => $selected) {
+            $option = $document->createElement('option', $value);
+            $option->setAttribute('value', $value);
+            if ($selected) {
+                $option->setAttribute('selected', 'selected');
+            }
+            $node->appendChild($option);
+        }
+
+        return $node;
+    }
+
+    protected function createSelectNodeWithEmptyOption($options, $attributes = array())
+    {
+        $document = new \DOMDocument();
+        $node = $document->createElement('select');
+
+        foreach ($attributes as $name => $value) {
+            $node->setAttribute($name, $value);
+        }
+        $node->setAttribute('name', 'name');
+
+        foreach ($options as $value => $selected) {
+            $option = $document->createElement('option', $value);
+            if ($selected) {
+                $option->setAttribute('selected', 'selected');
+            }
+            $node->appendChild($option);
+        }
+
+        return $node;
+    }
+}

+ 97 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FileFormFieldTest.php

@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\FileFormField;
+
+class FileFormFieldTest extends FormFieldTestCase
+{
+    public function testInitialize()
+    {
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        $field = new FileFormField($node);
+
+        $this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), '->initialize() sets the value of the field to no file uploaded');
+
+        $node = $this->createNode('textarea', '');
+        try {
+            $field = new FileFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not an input field');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input field');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'text'));
+        try {
+            $field = new FileFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not a file input field');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a file input field');
+        }
+    }
+
+    /**
+     * @dataProvider getSetValueMethods
+     */
+    public function testSetValue($method)
+    {
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        $field = new FileFormField($node);
+
+        $field->$method(null);
+        $this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), "->$method() clears the uploaded file if the value is null");
+
+        $field->$method(__FILE__);
+        $value = $field->getValue();
+
+        $this->assertEquals(basename(__FILE__), $value['name'], "->$method() sets the name of the file field");
+        $this->assertEquals('', $value['type'], "->$method() sets the type of the file field");
+        $this->assertInternalType('string', $value['tmp_name'], "->$method() sets the tmp_name of the file field");
+        $this->assertEquals(0, $value['error'], "->$method() sets the error of the file field");
+        $this->assertEquals(filesize(__FILE__), $value['size'], "->$method() sets the size of the file field");
+    }
+
+    public function getSetValueMethods()
+    {
+        return array(
+            array('setValue'),
+            array('upload'),
+        );
+    }
+
+    public function testSetErrorCode()
+    {
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        $field = new FileFormField($node);
+
+        $field->setErrorCode(UPLOAD_ERR_FORM_SIZE);
+        $value = $field->getValue();
+        $this->assertEquals(UPLOAD_ERR_FORM_SIZE, $value['error'], '->setErrorCode() sets the file input field error code');
+
+        try {
+            $field->setErrorCode('foobar');
+            $this->fail('->setErrorCode() throws a \InvalidArgumentException if the error code is not valid');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->setErrorCode() throws a \InvalidArgumentException if the error code is not valid');
+        }
+    }
+
+    public function testSetRawFilePath()
+    {
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        $field = new FileFormField($node);
+        $field->setFilePath(__FILE__);
+
+        $this->assertEquals(__FILE__, $field->getValue());
+    }
+
+}

+ 38 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTest.php

@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\InputFormField;
+
+class FormFieldTest extends FormFieldTestCase
+{
+    public function testGetName()
+    {
+        $node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
+        $field = new InputFormField($node);
+
+        $this->assertEquals('name', $field->getName(), '->getName() returns the name of the field');
+    }
+
+    public function testGetSetHasValue()
+    {
+        $node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
+        $field = new InputFormField($node);
+
+        $this->assertEquals('value', $field->getValue(), '->getValue() returns the value of the field');
+
+        $field->setValue('foo');
+        $this->assertEquals('foo', $field->getValue(), '->setValue() sets the value of the field');
+
+        $this->assertTrue($field->hasValue(), '->hasValue() always returns true');
+    }
+}

+ 27 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/FormFieldTestCase.php

@@ -0,0 +1,27 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+class FormFieldTestCase extends \PHPUnit_Framework_TestCase
+{
+    protected function createNode($tag, $value, $attributes = array())
+    {
+        $document = new \DOMDocument();
+        $node = $document->createElement($tag, $value);
+
+        foreach ($attributes as $name => $value) {
+            $node->setAttribute($name, $value);
+        }
+
+        return $node;
+    }
+}

+ 49 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/InputFormFieldTest.php

@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\InputFormField;
+
+class InputFormFieldTest extends FormFieldTestCase
+{
+    public function testInitialize()
+    {
+        $node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
+        $field = new InputFormField($node);
+
+        $this->assertEquals('value', $field->getValue(), '->initialize() sets the value of the field to the value attribute value');
+
+        $node = $this->createNode('textarea', '');
+        try {
+            $field = new InputFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not an input');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'checkbox'));
+        try {
+            $field = new InputFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is a checkbox');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is a checkbox');
+        }
+
+        $node = $this->createNode('input', '', array('type' => 'file'));
+        try {
+            $field = new InputFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is a file');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is a file');
+        }
+    }
+}

+ 33 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Field/TextareaFormFieldTest.php

@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests\Field;
+
+use Symfony\Component\DomCrawler\Field\TextareaFormField;
+
+class TextareaFormFieldTest extends FormFieldTestCase
+{
+    public function testInitialize()
+    {
+        $node = $this->createNode('textarea', 'foo bar');
+        $field = new TextareaFormField($node);
+
+        $this->assertEquals('foo bar', $field->getValue(), '->initialize() sets the value of the field to the textarea node value');
+
+        $node = $this->createNode('input', '');
+        try {
+            $field = new TextareaFormField($node);
+            $this->fail('->initialize() throws a \LogicException if the node is not a textarea');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a textarea');
+        }
+    }
+}

+ 8 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/Fixtures/windows-1250.html

@@ -0,0 +1,8 @@
+<html>
+    <head>
+        <meta http-equiv="content-type" content="text/html;charset=windows-1250">
+    </head>
+    <body>
+        <p>Ž�èýù</p>
+    </body>
+</html>

+ 691 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/FormTest.php

@@ -0,0 +1,691 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests;
+
+use Symfony\Component\DomCrawler\Form;
+use Symfony\Component\DomCrawler\FormFieldRegistry;
+use Symfony\Component\DomCrawler\Field;
+
+class FormTest extends \PHPUnit_Framework_TestCase
+{
+    public static function setUpBeforeClass()
+    {
+        // Ensure that the private helper class FormFieldRegistry is loaded
+        class_exists('Symfony\\Component\\DomCrawler\\Form');
+    }
+
+    public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+            <html>
+                <input type="submit" />
+                <form>
+                    <input type="foo" />
+                </form>
+                <button />
+            </html>
+        ');
+
+        $nodes = $dom->getElementsByTagName('input');
+
+        try {
+            $form = new Form($nodes->item(0), 'http://example.com');
+            $this->fail('__construct() throws a \\LogicException if the node has no form ancestor');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor');
+        }
+
+        try {
+            $form = new Form($nodes->item(1), 'http://example.com');
+            $this->fail('__construct() throws a \\LogicException if the input type is not submit, button, or image');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '__construct() throws a \\LogicException if the input type is not submit, button, or image');
+        }
+
+        $nodes = $dom->getElementsByTagName('button');
+
+        try {
+            $form = new Form($nodes->item(0), 'http://example.com');
+            $this->fail('__construct() throws a \\LogicException if the node has no form ancestor');
+        } catch (\LogicException $e) {
+            $this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor');
+        }
+    }
+
+    public function testMultiValuedFields()
+    {
+        $form = $this->createForm('<form>
+            <input type="text" name="foo[4]" value="foo" disabled="disabled" />
+            <input type="text" name="foo" value="foo" disabled="disabled" />
+            <input type="text" name="foo[2]" value="foo" disabled="disabled" />
+            <input type="text" name="foo[]" value="foo" disabled="disabled" />
+            <input type="text" name="bar[foo][]" value="foo" disabled="disabled" />
+            <input type="text" name="bar[foo][foobar]" value="foo" disabled="disabled" />
+            <input type="submit" />
+        </form>
+        ');
+
+        $this->assertEquals(
+            array_keys($form->all()),
+            array('foo[2]', 'foo[3]', 'bar[foo][0]', 'bar[foo][foobar]')
+        );
+
+        $this->assertEquals($form->get('foo[2]')->getValue(), 'foo');
+        $this->assertEquals($form->get('foo[3]')->getValue(), 'foo');
+        $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'foo');
+        $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foo');
+
+        $form['foo[2]'] = 'bar';
+        $form['foo[3]'] = 'bar';
+
+        $this->assertEquals($form->get('foo[2]')->getValue(), 'bar');
+        $this->assertEquals($form->get('foo[3]')->getValue(), 'bar');
+
+        $form['bar'] = array('foo' => array('0' => 'bar', 'foobar' => 'foobar'));
+
+        $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'bar');
+        $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foobar');
+
+    }
+
+    /**
+     * @dataProvider provideInitializeValues
+     */
+    public function testConstructor($message, $form, $values)
+    {
+        $form = $this->createForm('<form>'.$form.'</form>');
+        $this->assertEquals(
+            $values,
+            array_map(function ($field) {
+                    $class = get_class($field);
+
+                    return array(substr($class, strrpos($class, '\\') + 1), $field->getValue());
+                },
+                $form->all()
+            ),
+            '->getDefaultValues() '.$message
+        );
+    }
+
+    public function provideInitializeValues()
+    {
+        return array(
+            array(
+                'does not take into account input fields without a name attribute',
+                '<input type="text" value="foo" />
+                 <input type="submit" />',
+                array(),
+            ),
+            array(
+                'does not take into account input fields with an empty name attribute value',
+                '<input type="text" name="" value="foo" />
+                 <input type="submit" />',
+                array(),
+            ),
+            array(
+                'takes into account disabled input fields',
+                '<input type="text" name="foo" value="foo" disabled="disabled" />
+                 <input type="submit" />',
+                array('foo' => array('InputFormField', 'foo')),
+            ),
+            array(
+                'appends the submitted button value',
+                '<input type="submit" name="bar" value="bar" />',
+                array('bar' => array('InputFormField', 'bar')),
+            ),
+            array(
+                'appends the submitted button value for Button element',
+                '<button type="submit" name="bar" value="bar">Bar</button>',
+                array('bar' => array('InputFormField', 'bar')),
+            ),
+            array(
+                'appends the submitted button value but not other submit buttons',
+                '<input type="submit" name="bar" value="bar" />
+                 <input type="submit" name="foobar" value="foobar" />',
+                 array('foobar' => array('InputFormField', 'foobar')),
+            ),
+            array(
+                'returns textareas',
+                '<textarea name="foo">foo</textarea>
+                 <input type="submit" />',
+                 array('foo' => array('TextareaFormField', 'foo')),
+            ),
+            array(
+                'returns inputs',
+                '<input type="text" name="foo" value="foo" />
+                 <input type="submit" />',
+                 array('foo' => array('InputFormField', 'foo')),
+            ),
+            array(
+                'returns checkboxes',
+                '<input type="checkbox" name="foo" value="foo" checked="checked" />
+                 <input type="submit" />',
+                 array('foo' => array('ChoiceFormField', 'foo')),
+            ),
+            array(
+                'returns not-checked checkboxes',
+                '<input type="checkbox" name="foo" value="foo" />
+                 <input type="submit" />',
+                 array('foo' => array('ChoiceFormField', false)),
+            ),
+            array(
+                'returns radio buttons',
+                '<input type="radio" name="foo" value="foo" />
+                 <input type="radio" name="foo" value="bar" checked="bar" />
+                 <input type="submit" />',
+                 array('foo' => array('ChoiceFormField', 'bar')),
+            ),
+            array(
+                'returns file inputs',
+                '<input type="file" name="foo" />
+                 <input type="submit" />',
+                 array('foo' => array('FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))),
+            ),
+        );
+    }
+
+    public function testGetFormNode()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><form><input type="submit" /></form></html>');
+
+        $form = new Form($dom->getElementsByTagName('input')->item(0), 'http://example.com');
+
+        $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form');
+    }
+
+    public function testGetMethod()
+    {
+        $form = $this->createForm('<form><input type="submit" /></form>');
+        $this->assertEquals('GET', $form->getMethod(), '->getMethod() returns get if no method is defined');
+
+        $form = $this->createForm('<form method="post"><input type="submit" /></form>');
+        $this->assertEquals('POST', $form->getMethod(), '->getMethod() returns the method attribute value of the form');
+
+        $form = $this->createForm('<form method="post"><input type="submit" /></form>', 'put');
+        $this->assertEquals('PUT', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided');
+
+        $form = $this->createForm('<form method="post"><input type="submit" /></form>', 'delete');
+        $this->assertEquals('DELETE', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided');
+
+        $form = $this->createForm('<form method="post"><input type="submit" /></form>', 'patch');
+        $this->assertEquals('PATCH', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided');
+    }
+
+    public function testGetSetValue()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>');
+
+        $this->assertEquals('foo', $form['foo']->getValue(), '->offsetGet() returns the value of a form field');
+
+        $form['foo'] = 'bar';
+
+        $this->assertEquals('bar', $form['foo']->getValue(), '->offsetSet() changes the value of a form field');
+
+        try {
+            $form['foobar'] = 'bar';
+            $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist');
+        }
+
+        try {
+            $form['foobar'];
+            $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist');
+        }
+    }
+
+    public function testOffsetUnset()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>');
+        unset($form['foo']);
+        $this->assertFalse(isset($form['foo']), '->offsetUnset() removes a field');
+    }
+
+    public function testOffsetExists()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>');
+
+        $this->assertTrue(isset($form['foo']), '->offsetExists() return true if the field exists');
+        $this->assertFalse(isset($form['bar']), '->offsetExists() return false if the field does not exist');
+    }
+
+    public function testGetValues()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('foo[bar]' => 'foo', 'bar' => 'bar'), $form->getValues(), '->getValues() returns all form field values');
+
+        $form = $this->createForm('<form><input type="checkbox" name="foo" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include not-checked checkboxes');
+
+        $form = $this->createForm('<form><input type="file" name="foo" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include file input fields');
+
+        $form = $this->createForm('<form><input type="text" name="foo" value="foo" disabled="disabled" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include disabled fields');
+    }
+
+    public function testSetValues()
+    {
+        $form = $this->createForm('<form><input type="checkbox" name="foo" value="foo" checked="checked" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $form->setValues(array('foo' => false, 'bar' => 'foo'));
+        $this->assertEquals(array('bar' => 'foo'), $form->getValues(), '->setValues() sets the values of fields');
+    }
+
+    public function testMultiselectSetValues()
+    {
+        $form = $this->createForm('<form><select multiple="multiple" name="multi"><option value="foo">foo</option><option value="bar">bar</option></select><input type="submit" /></form>');
+        $form->setValues(array('multi' => array("foo", "bar")));
+        $this->assertEquals(array('multi' => array('foo', 'bar')), $form->getValues(), '->setValue() sets the values of select');
+    }
+
+    public function testGetPhpValues()
+    {
+        $form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays');
+    }
+
+    public function testGetFiles()
+    {
+        $form = $this->createForm('<form><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array(), $form->getFiles(), '->getFiles() returns an empty array if method is get');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for POST');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'put');
+        $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PUT');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'delete');
+        $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for DELETE');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>', 'patch');
+        $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PATCH');
+
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" disabled="disabled" /><input type="submit" /></form>');
+        $this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include disabled file fields');
+    }
+
+    public function testGetPhpFiles()
+    {
+        $form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays');
+    }
+
+    /**
+     * @dataProvider provideGetUriValues
+     */
+    public function testGetUri($message, $form, $values, $uri, $method = null)
+    {
+        $form = $this->createForm($form, $method);
+        $form->setValues($values);
+
+        $this->assertEquals('http://example.com'.$uri, $form->getUri(), '->getUri() '.$message);
+    }
+
+    public function testGetBaseUri()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<form method="post" action="foo.php"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+
+        $nodes = $dom->getElementsByTagName('input');
+        $form = new Form($nodes->item($nodes->length - 1), 'http://www.foo.com/');
+        $this->assertEquals('http://www.foo.com/foo.php', $form->getUri());
+    }
+
+    public function testGetUriWithAnchor()
+    {
+        $form = $this->createForm('<form action="#foo"><input type="submit" /></form>', null, 'http://example.com/id/123');
+
+        $this->assertEquals('http://example.com/id/123#foo', $form->getUri());
+    }
+
+    public function testGetUriActionAbsolute()
+    {
+        $formHtml='<form id="login_form" action="https://login.foo.com/login.php?login_attempt=1" method="POST"><input type="text" name="foo" value="foo" /><input type="submit" /></form>';
+
+        $form = $this->createForm($formHtml);
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+
+        $form = $this->createForm($formHtml, null, 'https://login.foo.com');
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+
+        $form = $this->createForm($formHtml, null, 'https://login.foo.com/bar/');
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+
+        // The action URI haven't the same domain Host have an another domain as Host
+        $form = $this->createForm($formHtml, null, 'https://www.foo.com');
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+
+        $form = $this->createForm($formHtml, null, 'https://www.foo.com/bar/');
+        $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form');
+    }
+
+    public function testGetUriAbsolute()
+    {
+        $form = $this->createForm('<form action="foo"><input type="submit" /></form>', null, 'http://localhost/foo/');
+        $this->assertEquals('http://localhost/foo/foo', $form->getUri(), '->getUri() returns absolute URIs');
+
+        $form = $this->createForm('<form action="/foo"><input type="submit" /></form>', null, 'http://localhost/foo/');
+        $this->assertEquals('http://localhost/foo', $form->getUri(), '->getUri() returns absolute URIs');
+    }
+
+    public function testGetUriWithOnlyQueryString()
+    {
+        $form = $this->createForm('<form action="?get=param"><input type="submit" /></form>', null, 'http://localhost/foo/bar');
+        $this->assertEquals('http://localhost/foo/bar?get=param', $form->getUri(), '->getUri() returns absolute URIs only if the host has been defined in the constructor');
+    }
+
+    public function testGetUriWithoutAction()
+    {
+        $form = $this->createForm('<form><input type="submit" /></form>', null, 'http://localhost/foo/bar');
+        $this->assertEquals('http://localhost/foo/bar', $form->getUri(), '->getUri() returns path if no action defined');
+    }
+
+    public function provideGetUriValues()
+    {
+        return array(
+            array(
+                'returns the URI of the form',
+                '<form action="/foo"><input type="submit" /></form>',
+                array(),
+                '/foo'
+            ),
+            array(
+                'appends the form values if the method is get',
+                '<form action="/foo"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo?foo=foo'
+            ),
+            array(
+                'appends the form values and merges the submitted values',
+                '<form action="/foo"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array('foo' => 'bar'),
+                '/foo?foo=bar'
+            ),
+            array(
+                'does not append values if the method is post',
+                '<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo'
+            ),
+            array(
+                'does not append values if the method is patch',
+                '<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo',
+                'PUT'
+            ),
+            array(
+                'does not append values if the method is delete',
+                '<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo',
+                'DELETE'
+            ),
+            array(
+                'does not append values if the method is put',
+                '<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo',
+                'PATCH'
+            ),
+            array(
+                'appends the form values to an existing query string',
+                '<form action="/foo?bar=bar"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/foo?bar=bar&foo=foo'
+            ),
+            array(
+                'returns an empty URI if the action is empty',
+                '<form><input type="submit" /></form>',
+                array(),
+                '/',
+            ),
+            array(
+                'appends the form values even if the action is empty',
+                '<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/?foo=foo',
+            ),
+            array(
+                'chooses the path if the action attribute value is a sharp (#)',
+                '<form action="#" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
+                array(),
+                '/#',
+            ),
+        );
+    }
+
+    public function testHas()
+    {
+        $form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+
+        $this->assertFalse($form->has('foo'), '->has() returns false if a field is not in the form');
+        $this->assertTrue($form->has('bar'), '->has() returns true if a field is in the form');
+    }
+
+    public function testRemove()
+    {
+        $form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+        $form->remove('bar');
+        $this->assertFalse($form->has('bar'), '->remove() removes a field');
+    }
+
+    public function testGet()
+    {
+        $form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+
+        $this->assertEquals('Symfony\\Component\\DomCrawler\\Field\\InputFormField', get_class($form->get('bar')), '->get() returns the field object associated with the given name');
+
+        try {
+            $form->get('foo');
+            $this->fail('->get() throws an \InvalidArgumentException if the field does not exist');
+        } catch (\InvalidArgumentException $e) {
+            $this->assertTrue(true, '->get() throws an \InvalidArgumentException if the field does not exist');
+        }
+    }
+
+    public function testAll()
+    {
+        $form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
+
+        $fields = $form->all();
+        $this->assertEquals(1, count($fields), '->all() return an array of form field objects');
+        $this->assertEquals('Symfony\\Component\\DomCrawler\\Field\\InputFormField', get_class($fields['bar']), '->all() return an array of form field objects');
+    }
+
+    public function testSubmitWithoutAFormButton()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('
+            <html>
+                <form>
+                    <input type="foo" />
+                </form>
+            </html>
+        ');
+
+        $nodes = $dom->getElementsByTagName('form');
+        $form = new Form($nodes->item(0), 'http://example.com');
+        $this->assertSame($nodes->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistryAddThrowAnExceptionWhenTheNameIsMalformed()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($this->getFormFieldMock('[foo]'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistryRemoveThrowAnExceptionWhenTheNameIsMalformed()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->remove('[foo]');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistryGetThrowAnExceptionWhenTheNameIsMalformed()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->get('[foo]');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistryGetThrowAnExceptionWhenTheFieldDoesNotExist()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->get('foo');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistrySetThrowAnExceptionWhenTheNameIsMalformed()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->set('[foo]', null);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testFormFieldRegistrySetThrowAnExceptionWhenTheFieldDoesNotExist()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->set('foo', null);
+    }
+
+    public function testFormFieldRegistryHasReturnsTrueWhenTheFQNExists()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($this->getFormFieldMock('foo[bar]'));
+
+        $this->assertTrue($registry->has('foo'));
+        $this->assertTrue($registry->has('foo[bar]'));
+        $this->assertFalse($registry->has('bar'));
+        $this->assertFalse($registry->has('foo[foo]'));
+    }
+
+    public function testFormRegistryFieldsCanBeRemoved()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($this->getFormFieldMock('foo'));
+        $registry->remove('foo');
+        $this->assertFalse($registry->has('foo'));
+    }
+
+    public function testFormRegistrySupportsMultivaluedFields()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($this->getFormFieldMock('foo[]'));
+        $registry->add($this->getFormFieldMock('foo[]'));
+        $registry->add($this->getFormFieldMock('bar[5]'));
+        $registry->add($this->getFormFieldMock('bar[]'));
+        $registry->add($this->getFormFieldMock('bar[baz]'));
+
+        $this->assertEquals(
+            array('foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'),
+            array_keys($registry->all())
+        );
+    }
+
+    public function testFormRegistrySetValues()
+    {
+        $registry = new FormFieldRegistry();
+        $registry->add($f2 = $this->getFormFieldMock('foo[2]'));
+        $registry->add($f3 = $this->getFormFieldMock('foo[3]'));
+        $registry->add($fbb = $this->getFormFieldMock('foo[bar][baz]'));
+
+        $f2
+            ->expects($this->exactly(2))
+            ->method('setValue')
+            ->with(2)
+        ;
+
+        $f3
+            ->expects($this->exactly(2))
+            ->method('setValue')
+            ->with(3)
+        ;
+
+        $fbb
+            ->expects($this->exactly(2))
+            ->method('setValue')
+            ->with('fbb')
+        ;
+
+        $registry->set('foo[2]', 2);
+        $registry->set('foo[3]', 3);
+        $registry->set('foo[bar][baz]', 'fbb');
+
+        $registry->set('foo', array(
+            2     => 2,
+            3     => 3,
+            'bar' => array(
+                'baz' => 'fbb'
+             )
+        ));
+    }
+
+    protected function getFormFieldMock($name, $value = null)
+    {
+        $field = $this
+            ->getMockBuilder('Symfony\\Component\\DomCrawler\\Field\\FormField')
+            ->setMethods(array('getName', 'getValue', 'setValue', 'initialize'))
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+
+        $field
+            ->expects($this->any())
+            ->method('getName')
+            ->will($this->returnValue($name))
+        ;
+
+        $field
+            ->expects($this->any())
+            ->method('getValue')
+            ->will($this->returnValue($value))
+        ;
+
+        return $field;
+    }
+
+    protected function createForm($form, $method = null, $currentUri = null)
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html>'.$form.'</html>');
+
+        $nodes = $dom->getElementsByTagName('input');
+        $xPath = new \DOMXPath($dom);
+        $nodes = $xPath->query('//input | //button');
+
+        if (null === $currentUri) {
+            $currentUri = 'http://example.com/';
+        }
+
+        return new Form($nodes->item($nodes->length - 1), $currentUri, $method);
+    }
+}

+ 110 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/Tests/LinkTest.php

@@ -0,0 +1,110 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DomCrawler\Tests;
+
+use Symfony\Component\DomCrawler\Link;
+
+class LinkTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException \LogicException
+     */
+    public function testConstructorWithANonATag()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><div><div></html>');
+
+        new Link($dom->getElementsByTagName('div')->item(0), 'http://www.example.com/');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testConstructorWithAnInvalidCurrentUri()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><a href="/foo">foo</a></html>');
+
+        new Link($dom->getElementsByTagName('a')->item(0), 'example.com');
+    }
+
+    public function testGetNode()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><a href="/foo">foo</a></html>');
+
+        $node = $dom->getElementsByTagName('a')->item(0);
+        $link = new Link($node, 'http://example.com/');
+
+        $this->assertEquals($node, $link->getNode(), '->getNode() returns the node associated with the link');
+    }
+
+    public function testGetMethod()
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML('<html><a href="/foo">foo</a></html>');
+
+        $node = $dom->getElementsByTagName('a')->item(0);
+        $link = new Link($node, 'http://example.com/');
+
+        $this->assertEquals('GET', $link->getMethod(), '->getMethod() returns the method of the link');
+
+        $link = new Link($node, 'http://example.com/', 'post');
+        $this->assertEquals('POST', $link->getMethod(), '->getMethod() returns the method of the link');
+    }
+
+    /**
+     * @dataProvider getGetUriTests
+     */
+    public function testGetUri($url, $currentUri, $expected)
+    {
+        $dom = new \DOMDocument();
+        $dom->loadHTML(sprintf('<html><a href="%s">foo</a></html>', $url));
+        $link = new Link($dom->getElementsByTagName('a')->item(0), $currentUri);
+
+        $this->assertEquals($expected, $link->getUri());
+    }
+
+    public function getGetUriTests()
+    {
+        return array(
+            array('/foo', 'http://localhost/bar/foo/', 'http://localhost/foo'),
+            array('/foo', 'http://localhost/bar/foo', 'http://localhost/foo'),
+            array('
+            /foo', 'http://localhost/bar/foo/', 'http://localhost/foo'),
+            array('/foo
+            ', 'http://localhost/bar/foo', 'http://localhost/foo'),
+
+            array('foo', 'http://localhost/bar/foo/', 'http://localhost/bar/foo/foo'),
+            array('foo', 'http://localhost/bar/foo', 'http://localhost/bar/foo'),
+
+            array('', 'http://localhost/bar/', 'http://localhost/bar/'),
+            array('#', 'http://localhost/bar/', 'http://localhost/bar/#'),
+            array('#bar', 'http://localhost/bar/#foo', 'http://localhost/bar/#bar'),
+            array('?a=b', 'http://localhost/bar/', 'http://localhost/bar/?a=b'),
+
+            array('http://login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'),
+            array('https://login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'),
+            array('mailto:foo@bar.com', 'http://localhost/foo', 'mailto:foo@bar.com'),
+
+            // tests schema relative URL (issue #7169)
+            array('//login.foo.com/foo', 'http://localhost/bar/', 'http://login.foo.com/foo'),
+            array('//login.foo.com/foo', 'https://localhost/bar/', 'https://login.foo.com/foo'),
+
+            array('?foo=2', 'http://localhost?foo=1', 'http://localhost?foo=2'),
+            array('?foo=2', 'http://localhost/?foo=1', 'http://localhost/?foo=2'),
+            array('?foo=2', 'http://localhost/bar?foo=1', 'http://localhost/bar?foo=2'),
+            array('?foo=2', 'http://localhost/bar/?foo=1', 'http://localhost/bar/?foo=2'),
+            array('?bar=2', 'http://localhost?foo=1', 'http://localhost?bar=2'),
+        );
+    }
+}

+ 37 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/composer.json

@@ -0,0 +1,37 @@
+{
+    "name": "symfony/dom-crawler",
+    "type": "library",
+    "description": "Symfony DomCrawler Component",
+    "keywords": [],
+    "homepage": "http://symfony.com",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "http://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.3"
+    },
+    "require-dev": {
+        "symfony/css-selector": "~2.0"
+    },
+    "suggest": {
+        "symfony/css-selector": "2.2.*"
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Component\\DomCrawler\\": "" }
+    },
+    "target-dir": "Symfony/Component/DomCrawler",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.3-dev"
+        }
+    }
+}

+ 30 - 0
vendor/symfony/dom-crawler/Symfony/Component/DomCrawler/phpunit.xml.dist

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="vendor/autoload.php"
+>
+    <testsuites>
+        <testsuite name="Symfony DomCrawler Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./Resources</directory>
+                <directory>./Tests</directory>
+                <directory>./vendor</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>

+ 1 - 1
vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Profiler/Profiler.php

@@ -165,7 +165,7 @@ class Profiler
             return;
         }
 
-        $profile = new Profile(uniqid());
+        $profile = new Profile(sha1(uniqid(mt_rand(), true)));
         $profile->setTime(time());
         $profile->setUrl($request->getUri());
         $profile->setIp($request->getClientIp());

+ 8 - 2
vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Profiler/RedisProfilerStorage.php

@@ -193,12 +193,13 @@ class RedisProfilerStorage implements ProfilerStorageInterface
     protected function getRedis()
     {
         if (null === $this->redis) {
-            if (!preg_match('#^redis://(?(?=\[.*\])\[(.*)\]|(.*)):(.*)$#', $this->dsn, $matches)) {
-                throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Redis with an invalid dsn "%s". The expected format is "redis://[host]:port".', $this->dsn));
+            if (!preg_match('#^redis://(?(?=\[.*\])\[(.*)\]|(.*)):(\d+)(/(\d+))?$#', $this->dsn, $matches)) {
+                throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Redis with an invalid dsn "%s". The expected format is "redis://[host]:port[/db-number]".', $this->dsn));
             }
 
             $host = $matches[1] ?: $matches[2];
             $port = $matches[3];
+            $dbnum = !empty($matches[5]) ? intval($matches[5]) : -1;
 
             if (!extension_loaded('redis')) {
                 throw new \RuntimeException('RedisProfilerStorage requires that the redis extension is loaded.');
@@ -206,6 +207,11 @@ class RedisProfilerStorage implements ProfilerStorageInterface
 
             $redis = new Redis;
             $redis->connect($host, $port);
+            
+            // if a valid dbnumber is given select the redis index
+            if (-1 < $dbnum) {
+                $redis->select($dbnum);
+            }
 
             $redis->setOption(self::REDIS_OPT_PREFIX, self::TOKEN_PREFIX);
 

+ 13 - 0
vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Tests/Profiler/Mock/RedisMock.php

@@ -238,4 +238,17 @@ class RedisMock
 
         return true;
     }
+    
+    public function select($dbnum)
+    {
+        if (!$this->connected) {
+            return false;
+        }
+        
+        if (0 > $dbnum) {
+            return false;
+        }
+        
+        return true;   
+    }
 }

+ 4 - 4
vendor/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php

@@ -126,8 +126,8 @@ class XmlFileLoader extends FileLoader
             $node->removeAttribute('pattern');
         }
 
-        $schemes = array_filter(explode(' ', $node->getAttribute('schemes')));
-        $methods = array_filter(explode(' ', $node->getAttribute('methods')));
+        $schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY);
+        $methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY);
 
         list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
 
@@ -154,8 +154,8 @@ class XmlFileLoader extends FileLoader
         $type = $node->getAttribute('type');
         $prefix = $node->getAttribute('prefix');
         $host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
-        $schemes = $node->hasAttribute('schemes') ? array_filter(explode(' ', $node->getAttribute('schemes'))) : null;
-        $methods = $node->hasAttribute('methods') ? array_filter(explode(' ', $node->getAttribute('methods'))) : null;
+        $schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
+        $methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
 
         list($defaults, $requirements, $options) = $this->parseConfigs($node, $path);
 

+ 4 - 14
vendor/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd

@@ -17,16 +17,6 @@
 
   <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" />
@@ -49,8 +39,8 @@
     <xsd:attribute name="path" type="xsd:string" />
     <xsd:attribute name="pattern" type="xsd:string" />
     <xsd:attribute name="host" type="xsd:string" />
-    <xsd:attribute name="schemes" type="stringlist" />
-    <xsd:attribute name="methods" type="stringlist" />
+    <xsd:attribute name="schemes" type="xsd:string" />
+    <xsd:attribute name="methods" type="xsd:string" />
   </xsd:complexType>
 
   <xsd:complexType name="import">
@@ -60,8 +50,8 @@
     <xsd:attribute name="type" type="xsd:string" />
     <xsd:attribute name="prefix" type="xsd:string" />
     <xsd:attribute name="host" type="xsd:string" />
-    <xsd:attribute name="schemes" type="stringlist" />
-    <xsd:attribute name="methods" type="stringlist" />
+    <xsd:attribute name="schemes" type="xsd:string" />
+    <xsd:attribute name="methods" type="xsd:string" />
   </xsd:complexType>
 
   <xsd:complexType name="element">

+ 22 - 2
vendor/symfony/routing/Symfony/Component/Routing/RouteCollection.php

@@ -65,6 +65,9 @@ class RouteCollection implements \IteratorAggregate, \Countable
      */
     public function getParent()
     {
+        trigger_error('getParent() is deprecated since version 2.2 and will be removed in 2.3. There is no substitution ' .
+            'because RouteCollection is not tree structure anymore.', E_USER_DEPRECATED);
+
         return $this->parent;
     }
 
@@ -77,6 +80,9 @@ class RouteCollection implements \IteratorAggregate, \Countable
      */
     public function getRoot()
     {
+        trigger_error('getRoot() is deprecated since version 2.2 and will be removed in 2.3. There is no substitution ' .
+            'because RouteCollection is not tree structure anymore.', E_USER_DEPRECATED);
+
         $parent = $this;
         while ($parent->getParent()) {
             $parent = $parent->getParent();
@@ -157,7 +163,10 @@ class RouteCollection implements \IteratorAggregate, \Countable
     public function remove($name)
     {
         // just for BC
-        $root = $this->getRoot();
+        $root = $this;
+        while ($root->parent) {
+            $root = $root->parent;
+        }
 
         foreach ((array) $name as $n) {
             unset($root->routes[$n]);
@@ -184,6 +193,8 @@ class RouteCollection implements \IteratorAggregate, \Countable
         // this is to keep BC
         $numargs = func_num_args();
         if ($numargs > 1) {
+            trigger_error('addCollection() should only be used with a single parameter. The params $prefix, $defaults, $requirements and $options ' .
+                'are deprecated since version 2.2 and will be removed in 2.3. Use addPrefix() and addOptions() instead.', E_USER_DEPRECATED);
             $collection->addPrefix($this->prefix . func_get_arg(1));
             if ($numargs > 2) {
                 $collection->addDefaults(func_get_arg(2));
@@ -232,7 +243,13 @@ class RouteCollection implements \IteratorAggregate, \Countable
         $this->prefix = '/' . $prefix . $this->prefix;
 
         // this is to keep BC
-        $options = func_num_args() > 3 ? func_get_arg(3) : array();
+        if (func_num_args() > 3) {
+            trigger_error('The fourth parameter ($options) of addPrefix() is deprecated since version 2.2 and will be removed in 2.3. ' .
+                'Use addOptions() instead.', E_USER_DEPRECATED);
+            $options = func_get_arg(3);
+        } else {
+            $options = array();
+        }
 
         foreach ($this->routes as $route) {
             $route->setPath('/' . $prefix . $route->getPath());
@@ -251,6 +268,9 @@ class RouteCollection implements \IteratorAggregate, \Countable
      */
     public function getPrefix()
     {
+        trigger_error('getPrefix() is deprecated since version 2.2 and will be removed in 2.3. The method suggests that ' .
+            'all routes in the collection would have this prefix, which is not necessarily true.', E_USER_DEPRECATED);
+
         return $this->prefix;
     }
 

+ 1 - 1
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/missing_id.xml

@@ -4,5 +4,5 @@
     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 pattern="/test"></route>
+    <route path="/test"></route>
 </routes>

+ 1 - 1
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/namespaceprefix.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">
 
-    <r:route id="blog_show" pattern="/blog/{slug}" host="{_locale}.example.com">
+    <r:route id="blog_show" path="/blog/{slug}" host="{_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>

+ 1 - 2
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml

@@ -4,9 +4,8 @@
     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" path="/blog/{slug}">
         <default key="_controller">MyBundle:Blog:show</default>
         <requirement key="_method">GET</requirement>
-        <option key="segment_separators">/</option>
     <!-- </route> -->
 </routes>

+ 1 - 1
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.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" path="/blog/{slug}">
         <default key="_controller">MyBundle:Blog:show</default>
         <requirement key="_method">GET</requirement>
         <option key="compiler_class">RouteCompiler</option>

+ 6 - 6
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.php

@@ -6,18 +6,18 @@ $collection = new RouteCollection();
 $collection->add('blog_show', new Route(
     '/blog/{slug}',
     array('_controller' => 'MyBlogBundle:Blog:show'),
-    array('_method' => 'GET', 'locale' => '\w+', '_scheme' => 'https'),
+    array('locale' => '\w+'),
     array('compiler_class' => 'RouteCompiler'),
-    '{locale}.example.com'
+    '{locale}.example.com',
+    array('https'),
+    array('GET','POST','put','OpTiOnS')
 ));
 $collection->add('blog_show_legacy', new Route(
     '/blog/{slug}',
     array('_controller' => 'MyBlogBundle:Blog:show'),
-    array('locale' => '\w+'),
+    array('_method' => 'GET|POST|put|OpTiOnS', '_scheme' => 'https', 'locale' => '\w+',),
     array('compiler_class' => 'RouteCompiler'),
-    '{locale}.example.com',
-    array('https'),
-    array('GET')
+    '{locale}.example.com'
 ));
 
 return $collection;

+ 3 - 3
vendor/symfony/routing/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" path="/blog/{slug}" host="{locale}.example.com" methods="GET" schemes="https">
+    <route id="blog_show" path="/blog/{slug}" host="{locale}.example.com" methods="GET|POST  put,OpTiOnS" schemes="hTTps">
         <default key="_controller">MyBundle:Blog:show</default>
         <requirement key="locale">\w+</requirement>
         <option key="compiler_class">RouteCompiler</option>
@@ -12,8 +12,8 @@
 
     <route id="blog_show_legacy" pattern="/blog/{slug}" host="{locale}.example.com">
         <default key="_controller">MyBundle:Blog:show</default>
-        <requirement key="_method">GET</requirement>
-        <requirement key="_scheme">https</requirement>
+        <requirement key="_method">GET|POST|put|OpTiOnS</requirement>
+        <requirement key="_scheme">hTTps</requirement>
         <requirement key="locale">\w+</requirement>
         <option key="compiler_class">RouteCompiler</option>
     </route>

+ 4 - 4
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml

@@ -1,17 +1,17 @@
 blog_show:
     path:         /blog/{slug}
-    defaults:     { _controller: MyBlogBundle:Blog:show }
+    defaults:     { _controller: "MyBundle:Blog:show" }
     host:         "{locale}.example.com"
     requirements: { 'locale': '\w+' }
-    methods:      ['GET']
+    methods:      ['GET','POST','put','OpTiOnS']
     schemes:      ['https']
     options:
         compiler_class: RouteCompiler
 
 blog_show_legacy:
     pattern:      /blog/{slug}
-    defaults:     { _controller: MyBlogBundle:Blog:show }
+    defaults:     { _controller: "MyBundle:Blog:show" }
     host:         "{locale}.example.com"
-    requirements: { '_method': 'GET', 'locale': '\w+', _scheme: 'https' }
+    requirements: { '_method': 'GET|POST|put|OpTiOnS', _scheme: https, 'locale': '\w+' }
     options:
         compiler_class: RouteCompiler

+ 1 - 1
vendor/symfony/routing/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}" host="{locale}.example.com">
+    <import resource="validpattern.xml" prefix="/{foo}" host="">
         <default key="foo">123</default>
         <requirement key="foo">\d+</requirement>
         <option key="foo">bar</option>

+ 2 - 2
vendor/symfony/routing/Symfony/Component/Routing/Tests/Fixtures/validresource.yml

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

+ 6 - 6
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php

@@ -44,12 +44,12 @@ class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
         $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
 
         foreach ($routes as $route) {
-            $this->assertEquals('/blog/{slug}', $route->getPath());
-            $this->assertEquals('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
-            $this->assertEquals('GET', $route->getRequirement('_method'));
-            $this->assertEquals('https', $route->getRequirement('_scheme'));
-            $this->assertEquals('{locale}.example.com', $route->getHost());
-            $this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
+            $this->assertSame('/blog/{slug}', $route->getPath());
+            $this->assertSame('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
+            $this->assertSame('{locale}.example.com', $route->getHost());
+            $this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
+            $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods());
+            $this->assertEquals(array('https'), $route->getSchemes());
         }
     }
 }

+ 21 - 20
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php

@@ -43,14 +43,16 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
 
         $this->assertCount(2, $routes, 'Two routes are loaded');
         $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
-        $route = $routes['blog_show'];
-        $this->assertEquals('/blog/{slug}', $route->getPath());
-        $this->assertEquals('MyBundle:Blog:show', $route->getDefault('_controller'));
-        $this->assertEquals('GET', $route->getRequirement('_method'));
-        $this->assertEquals('https', $route->getRequirement('_scheme'));
-        $this->assertEquals('\w+', $route->getRequirement('locale'));
-        $this->assertEquals('{locale}.example.com', $route->getHost());
-        $this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
+
+        foreach ($routes as $route) {
+            $this->assertSame('/blog/{slug}', $route->getPath());
+            $this->assertSame('{locale}.example.com', $route->getHost());
+            $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller'));
+            $this->assertSame('\w+', $route->getRequirement('locale'));
+            $this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
+            $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods());
+            $this->assertEquals(array('https'), $route->getSchemes());
+        }
     }
 
     public function testLoadWithNamespacePrefix()
@@ -61,12 +63,12 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
         $this->assertCount(1, $routeCollection->all(), 'One route is loaded');
 
         $route = $routeCollection->get('blog_show');
-        $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->getHost());
-        $this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
+        $this->assertSame('/blog/{slug}', $route->getPath());
+        $this->assertSame('{_locale}.example.com', $route->getHost());
+        $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller'));
+        $this->assertSame('\w+', $route->getRequirement('slug'));
+        $this->assertSame('en|fr|de', $route->getRequirement('_locale'));
+        $this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
     }
 
     public function testLoadWithImport()
@@ -79,12 +81,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
         $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
 
         foreach ($routes as $route) {
-            $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']->getHost());
+            $this->assertSame('/{foo}/blog/{slug}', $route->getPath());
+            $this->assertSame('123', $route->getDefault('foo'));
+            $this->assertSame('\d+', $route->getRequirement('foo'));
+            $this->assertSame('bar', $route->getOption('foo'));
+            $this->assertSame('', $route->getHost());
         }
     }
 

+ 16 - 14
vendor/symfony/routing/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php

@@ -73,7 +73,7 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
         $this->assertSame('/true', $route->getPath());
     }
 
-    public function testLoadWithPattern()
+    public function testLoadWithRoute()
     {
         $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
         $routeCollection = $loader->load('validpattern.yml');
@@ -83,13 +83,13 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
         $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
 
         foreach ($routes as $route) {
-            $this->assertEquals('/blog/{slug}', $route->getPath());
-            $this->assertEquals('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
-            $this->assertEquals('GET', $route->getRequirement('_method'));
-            $this->assertEquals('https', $route->getRequirement('_scheme'));
-            $this->assertEquals('\w+', $route->getRequirement('locale'));
-            $this->assertEquals('{locale}.example.com', $route->getHost());
-            $this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
+            $this->assertSame('/blog/{slug}', $route->getPath());
+            $this->assertSame('{locale}.example.com', $route->getHost());
+            $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller'));
+            $this->assertSame('\w+', $route->getRequirement('locale'));
+            $this->assertSame('RouteCompiler', $route->getOption('compiler_class'));
+            $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods());
+            $this->assertEquals(array('https'), $route->getSchemes());
         }
     }
 
@@ -101,11 +101,13 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
 
         $this->assertCount(2, $routes, 'Two routes are loaded');
         $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
-        $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']->getHost());
+
+        foreach ($routes as $route) {
+            $this->assertSame('/{foo}/blog/{slug}', $route->getPath());
+            $this->assertSame('123', $route->getDefault('foo'));
+            $this->assertSame('\d+', $route->getRequirement('foo'));
+            $this->assertSame('bar', $route->getOption('foo'));
+            $this->assertSame('', $route->getHost());
+        }
     }
 }

+ 2 - 1
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php

@@ -150,7 +150,8 @@ class ApacheMatcherDumperTest extends \PHPUnit_Framework_TestCase
         $route3 = new Route('/route3', array(), array(), array(), 'b.example.com');
         $collection2->add('route3', $route3);
 
-        $collection1->addCollection($collection2, '/c2');
+        $collection2->addPrefix('/c2');
+        $collection1->addCollection($collection2);
 
         $route4 = new Route('/route4', array(), array(), array(), 'a.example.com');
         $collection1->add('route4', $route4);

+ 19 - 12
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php

@@ -118,14 +118,17 @@ class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
         $collection1->add('overridden', new Route('/overridden1'));
         $collection1->add('foo1', new Route('/{foo}'));
         $collection1->add('bar1', new Route('/{bar}'));
+        $collection1->addPrefix('/b\'b');
         $collection2 = new RouteCollection();
-        $collection2->addCollection($collection1, '/b\'b');
+        $collection2->addCollection($collection1);
         $collection2->add('overridden', new Route('/{var}', array(), array('var' => '.*')));
         $collection1 = new RouteCollection();
         $collection1->add('foo2', new Route('/{foo1}'));
         $collection1->add('bar2', new Route('/{bar1}'));
-        $collection2->addCollection($collection1, '/b\'b');
-        $collection->addCollection($collection2, '/a');
+        $collection1->addPrefix('/b\'b');
+        $collection2->addCollection($collection1);
+        $collection2->addPrefix('/a');
+        $collection->addCollection($collection2);
 
         // overridden through addCollection() and multiple sub-collections with no own prefix
         $collection1 = new RouteCollection();
@@ -137,15 +140,16 @@ class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
         $collection3->add('hey', new Route('/hey/'));
         $collection2->addCollection($collection3);
         $collection1->addCollection($collection2);
-        $collection->addCollection($collection1, '/multi');
+        $collection1->addPrefix('/multi');
+        $collection->addCollection($collection1);
 
         // "dynamic" prefix
         $collection1 = new RouteCollection();
         $collection1->add('foo3', new Route('/{foo}'));
         $collection1->add('bar3', new Route('/{bar}'));
-        $collection2 = new RouteCollection();
-        $collection2->addCollection($collection1, '/b');
-        $collection->addCollection($collection2, '/{_locale}');
+        $collection1->addPrefix('/b');
+        $collection1->addPrefix('{_locale}');
+        $collection->addCollection($collection1);
 
         // route between collections
         $collection->add('ababa', new Route('/ababa'));
@@ -153,7 +157,8 @@ class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
         // collection with static prefix but only one route
         $collection1 = new RouteCollection();
         $collection1->add('foo4', new Route('/{foo}'));
-        $collection->addCollection($collection1, '/aba');
+        $collection1->addPrefix('/aba');
+        $collection->addCollection($collection1);
 
         // prefix and host
 
@@ -215,10 +220,12 @@ class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
         $collection2->add('b', new Route('/{var}'));
         $collection3 = new RouteCollection();
         $collection3->add('c', new Route('/{var}'));
-
-        $collection2->addCollection($collection3, '/c');
-        $collection1->addCollection($collection2, '/b');
-        $collection->addCollection($collection1, '/a');
+        $collection3->addPrefix('/c');
+        $collection2->addCollection($collection3);
+        $collection2->addPrefix('/b');
+        $collection1->addCollection($collection2);
+        $collection1->addPrefix('/a');
+        $collection->addCollection($collection1);
 
         /* test case 2 */
 

+ 6 - 14
vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php

@@ -130,14 +130,10 @@ class UrlMatcherTest extends \PHPUnit_Framework_TestCase
 
     public function testMatchWithPrefixes()
     {
-        $collection1 = new RouteCollection();
-        $collection1->add('foo', new Route('/{foo}'));
-
-        $collection2 = new RouteCollection();
-        $collection2->addCollection($collection1, '/b');
-
         $collection = new RouteCollection();
-        $collection->addCollection($collection2, '/a');
+        $collection->add('foo', new Route('/{foo}'));
+        $collection->addPrefix('/b');
+        $collection->addPrefix('/a');
 
         $matcher = new UrlMatcher($collection, new RequestContext());
         $this->assertEquals(array('_route' => 'foo', 'foo' => 'foo'), $matcher->match('/a/b/foo'));
@@ -145,14 +141,10 @@ class UrlMatcherTest extends \PHPUnit_Framework_TestCase
 
     public function testMatchWithDynamicPrefix()
     {
-        $collection1 = new RouteCollection();
-        $collection1->add('foo', new Route('/{foo}'));
-
-        $collection2 = new RouteCollection();
-        $collection2->addCollection($collection1, '/b');
-
         $collection = new RouteCollection();
-        $collection->addCollection($collection2, '/{_locale}');
+        $collection->add('foo', new Route('/{foo}'));
+        $collection->addPrefix('/b');
+        $collection->addPrefix('/{_locale}');
 
         $matcher = new UrlMatcher($collection, new RequestContext());
         $this->assertEquals(array('_locale' => 'fr', '_route' => 'foo', 'foo' => 'foo'), $matcher->match('/fr/b/foo'));

+ 78 - 63
vendor/symfony/routing/Symfony/Component/Routing/Tests/RouteCollectionTest.php

@@ -54,16 +54,19 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('/foo2', $collection->get('foo')->getPath());
     }
 
-    public function testIteratorWithOverriddenRoutes()
+    public function testIterator()
     {
         $collection = new RouteCollection();
         $collection->add('foo', new Route('/foo'));
 
         $collection1 = new RouteCollection();
-        $collection1->add('foo', new Route('/foo1'));
+        $collection1->add('bar', $bar = new Route('/bar'));
+        $collection1->add('foo', $foo = new Route('/foo-new'));
         $collection->addCollection($collection1);
+        $collection->add('last', $last = new Route('/last'));
 
-        $this->assertEquals('/foo1', $this->getFirstNamedRoute($collection, 'foo')->getPath());
+        $this->assertInstanceOf('\ArrayIterator', $collection->getIterator());
+        $this->assertSame(array('bar' => $bar, 'foo' => $foo, 'last' => $last), $collection->getIterator()->getArrayCopy());
     }
 
     public function testCount()
@@ -72,58 +75,71 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
         $collection->add('foo', new Route('/foo'));
 
         $collection1 = new RouteCollection();
-        $collection1->add('foo1', new Route('/foo1'));
+        $collection1->add('bar', new Route('/bar'));
         $collection->addCollection($collection1);
 
         $this->assertCount(2, $collection);
     }
 
-    protected function getFirstNamedRoute(RouteCollection $routeCollection, $name)
+    public function testAddCollection()
     {
-        foreach ($routeCollection as $key => $route) {
-            if ($route instanceof RouteCollection) {
-                return $this->getFirstNamedRoute($route, $name);
-            }
-
-            if ($name === $key) {
-                return $route;
-            }
-        }
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('bar', $bar = new Route('/bar'));
+        $collection1->add('foo', $foo = new Route('/foo-new'));
+
+        $collection2 = new RouteCollection();
+        $collection2->add('grandchild', $grandchild = new Route('/grandchild'));
+
+        $collection1->addCollection($collection2);
+        $collection->addCollection($collection1);
+        $collection->add('last', $last = new Route('/last'));
+
+        $this->assertSame(array('bar' => $bar, 'foo' => $foo, 'grandchild' => $grandchild, 'last' => $last), $collection->all(), 
+            '->addCollection() imports routes of another collection, overrides if necessary and adds them at the end');
     }
 
-    public function testAddCollection()
+    public function testAddCollectionWithResources()
     {
         if (!class_exists('Symfony\Component\Config\Resource\FileResource')) {
             $this->markTestSkipped('The "Config" component is not available');
         }
 
         $collection = new RouteCollection();
-        $collection->add('foo', $foo = new Route('/foo'));
+        $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml'));
         $collection1 = new RouteCollection();
-        $collection1->add('foo', $foo1 = new Route('/foo1'));
-        $collection1->add('bar', $bar1 = new Route('/bar1'));
+        $collection1->addResource($foo1 = new FileResource(__DIR__.'/Fixtures/foo1.xml'));
         $collection->addCollection($collection1);
-        $this->assertEquals(array('foo' => $foo1, 'bar' => $bar1), $collection->all(), '->addCollection() adds routes from another collection');
+        $this->assertEquals(array($foo, $foo1), $collection->getResources(), '->addCollection() merges resources');
+    }
 
+    public function testAddDefaultsAndRequirementsAndOptions()
+    {
         $collection = new RouteCollection();
-        $collection->add('foo', $foo = new Route('/foo'));
+        $collection->add('foo', new Route('/{placeholder}'));
         $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')->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(
-            array('foo' => 'bar', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'),
-            $collection->get('foo')->getOptions(), '->addCollection() can add an option to all merged routes'
+        $collection1->add('bar', new Route('/{placeholder}',
+            array('_controller' => 'fixed', 'placeholder' => 'default'), array('placeholder' => '.+'), array('option' => 'value'))
         );
-
-        $collection = new RouteCollection();
-        $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml'));
-        $collection1 = new RouteCollection();
-        $collection1->addResource($foo1 = new FileResource(__DIR__.'/Fixtures/foo1.xml'));
         $collection->addCollection($collection1);
-        $this->assertEquals(array($foo, $foo1), $collection->getResources(), '->addCollection() merges resources');
+
+        $collection->addDefaults(array('placeholder' => 'new-default'));
+        $this->assertEquals(array('placeholder' => 'new-default'), $collection->get('foo')->getDefaults(), '->addDefaults() adds defaults to all routes');
+        $this->assertEquals(array('_controller' => 'fixed', 'placeholder' => 'new-default'), $collection->get('bar')->getDefaults(),
+            '->addDefaults() adds defaults to all routes and overwrites existing ones');
+
+        $collection->addRequirements(array('placeholder' => '\d+'));
+        $this->assertEquals(array('placeholder' => '\d+'), $collection->get('foo')->getRequirements(), '->addRequirements() adds requirements to all routes');
+        $this->assertEquals(array('placeholder' => '\d+'), $collection->get('bar')->getRequirements(),
+            '->addRequirements() adds requirements to all routes and overwrites existing ones');
+
+        $collection->addOptions(array('option' => 'new-value'));
+        $this->assertEquals(
+            array('option' => 'new-value', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'),
+            $collection->get('bar')->getOptions(), '->addOptions() adds options to all routes and overwrites existing ones'
+        );
     }
 
     public function testAddPrefix()
@@ -133,30 +149,20 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
         $collection2 = new RouteCollection();
         $collection2->add('bar', $bar = new Route('/bar'));
         $collection->addCollection($collection2);
-        $this->assertSame('', $collection->getPrefix(), '->getPrefix() is initialized with ""');
         $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->assertSame('/foo', $collection->get('foo')->getPattern(), '->addPrefix() trims the prefix and a single slash has no effect');
+        $collection->addPrefix('/{admin}', array('admin' => 'admin'), array('admin' => '\d+'));
         $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');
         $this->assertEquals(array('admin' => '\d+'), $collection->get('bar')->getRequirements(), '->addPrefix() adds requirements to all routes');
-        $this->assertEquals(
-            array('foo' => 'bar', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'),
-            $collection->get('foo')->getOptions(), '->addPrefix() adds an option to all routes'
-        );
-        $this->assertEquals(
-            array('foo' => 'bar', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'),
-            $collection->get('bar')->getOptions(), '->addPrefix() adds an option to all routes'
-        );
         $collection->addPrefix('0');
-        $this->assertEquals('/0/{admin}', $collection->getPrefix(), '->addPrefix() ensures a prefix must start with a slash and must not end with a slash');
+        $this->assertEquals('/0/{admin}/foo', $collection->get('foo')->getPattern(), '->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')->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');
+        $this->assertSame('/ /0/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() can handle spaces if desired');
+        $this->assertSame('/ /0/{admin}/bar', $collection->get('bar')->getPath(), 'the route pattern of an added collection is in synch with the added prefix');
     }
 
     public function testAddPrefixOverridesDefaultsAndRequirements()
@@ -170,19 +176,6 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('https', $collection->get('bar')->getRequirement('_scheme'), '->addPrefix() overrides existing requirements');
     }
 
-    public function testAddCollectionOverridesDefaultsAndRequirements()
-    {
-        $imported = new RouteCollection();
-        $imported->add('foo', $foo = new Route('/foo'));
-        $imported->add('bar', $bar = new Route('/bar', array(), array('_scheme' => 'http')));
-
-        $collection = new RouteCollection();
-        $collection->addCollection($imported, null, array(), array('_scheme' => 'https'));
-
-        $this->assertEquals('https', $collection->get('foo')->getRequirement('_scheme'), '->addCollection() overrides existing requirements');
-        $this->assertEquals('https', $collection->get('bar')->getRequirement('_scheme'), '->addCollection() overrides existing requirements');
-    }
-
     public function testResource()
     {
         if (!class_exists('Symfony\Component\Config\Resource\FileResource')) {
@@ -191,7 +184,11 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
 
         $collection = new RouteCollection();
         $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml'));
-        $this->assertEquals(array($foo), $collection->getResources(), '->addResources() adds a resource');
+        $collection->addResource($bar = new FileResource(__DIR__.'/Fixtures/bar.xml'));
+        $collection->addResource(new FileResource(__DIR__.'/Fixtures/foo.xml'));
+
+        $this->assertEquals(array($foo, $bar), $collection->getResources(),
+            '->addResource() adds a resource and getResources() only returns unique ones by comparing the string representation');
     }
 
     public function testUniqueRouteWithGivenName()
@@ -217,13 +214,31 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
         $collection2 = new RouteCollection();
         $collection2->add('b', $b = new Route('/b'));
         $collection1->addCollection($collection2);
+        $collection1->add('$péß^a|', $c = new Route('/special'));
 
         $this->assertSame($b, $collection1->get('b'), '->get() returns correct route in child collection');
+        $this->assertSame($c, $collection1->get('$péß^a|'), '->get() can handle special characters');
         $this->assertNull($collection2->get('a'), '->get() does not return the route defined in parent collection');
         $this->assertNull($collection1->get('non-existent'), '->get() returns null when route does not exist');
         $this->assertNull($collection1->get(0), '->get() does not disclose internal child RouteCollection');
     }
 
+    public function testRemove()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', $foo = new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('bar', $bar = new Route('/bar'));
+        $collection->addCollection($collection1);
+        $collection->add('last', $last = new Route('/last'));
+
+        $collection->remove('foo');
+        $this->assertSame(array('bar' => $bar, 'last' => $last), $collection->all(), '->remove() can remove a single route');
+        $collection->remove(array('bar', 'last'));
+        $this->assertSame(array(), $collection->all(), '->remove() accepts an array and can remove multiple routes at once');
+    }
+
     public function testSetHost()
     {
         $collection = new RouteCollection();

+ 6 - 0
vendor/symfony/translation/Symfony/Component/Translation/CHANGELOG.md

@@ -1,6 +1,12 @@
 CHANGELOG
 =========
 
+2.3.0
+-----
+
+ * added Translator::getFallbackLocales()
+ * deprecated Translator::setFallbackLocale() in favor of the new Translator::setFallbackLocales() method
+
 2.2.0
 -----
 

+ 1 - 1
vendor/symfony/translation/Symfony/Component/Translation/README.md

@@ -9,7 +9,7 @@ translated strings from these including support for pluralization.
     use Symfony\Component\Translation\Loader\ArrayLoader;
 
     $translator = new Translator('fr_FR', new MessageSelector());
-    $translator->setFallbackLocale('fr');
+    $translator->setFallbackLocales(array('fr'));
     $translator->addLoader('array', new ArrayLoader());
     $translator->addResource('array', array(
         'Hello World!' => 'Bonjour',

+ 10 - 10
vendor/symfony/translation/Symfony/Component/Translation/Tests/TranslatorTest.php

@@ -27,7 +27,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('fr', $translator->getLocale());
     }
 
-    public function testSetFallbackLocale()
+    public function testSetFallbackLocales()
     {
         $translator = new Translator('en', new MessageSelector());
         $translator->addLoader('array', new ArrayLoader());
@@ -37,11 +37,11 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
         // force catalogue loading
         $translator->trans('bar');
 
-        $translator->setFallbackLocale('fr');
+        $translator->setFallbackLocales(array('fr'));
         $this->assertEquals('foobar', $translator->trans('bar'));
     }
 
-    public function testSetFallbackLocaleMultiple()
+    public function testSetFallbackLocalesMultiple()
     {
         $translator = new Translator('en', new MessageSelector());
         $translator->addLoader('array', new ArrayLoader());
@@ -51,7 +51,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
         // force catalogue loading
         $translator->trans('bar');
 
-        $translator->setFallbackLocale(array('fr_FR', 'fr'));
+        $translator->setFallbackLocales(array('fr_FR', 'fr'));
         $this->assertEquals('bar (fr)', $translator->trans('bar'));
     }
 
@@ -62,7 +62,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
         $translator->addResource('array', array('foo' => 'foofoo'), 'en_US');
         $translator->addResource('array', array('bar' => 'foobar'), 'en');
 
-        $translator->setFallbackLocale('en');
+        $translator->setFallbackLocales(array('en'));
 
         $this->assertEquals('foobar', $translator->trans('bar'));
     }
@@ -113,7 +113,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
         $translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US');
         $translator->addResource('array', array('bar' => 'bar (en)'), 'en');
 
-        $translator->setFallbackLocale(array('en_US', 'en'));
+        $translator->setFallbackLocales(array('en_US', 'en'));
 
         $this->assertEquals('foo (en_US)', $translator->trans('foo'));
         $this->assertEquals('bar (en)', $translator->trans('bar'));
@@ -122,7 +122,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
     public function testTransNonExistentWithFallback()
     {
         $translator = new Translator('fr', new MessageSelector());
-        $translator->setFallbackLocale('en');
+        $translator->setFallbackLocales(array('en'));
         $translator->addLoader('array', new ArrayLoader());
         $this->assertEquals('non-existent', $translator->trans('non-existent'));
     }
@@ -246,7 +246,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
     public function testTransChoiceFallback()
     {
         $translator = new Translator('ru', new MessageSelector());
-        $translator->setFallbackLocale('en');
+        $translator->setFallbackLocales(array('en'));
         $translator->addLoader('array', new ArrayLoader());
         $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en');
 
@@ -256,7 +256,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
     public function testTransChoiceFallbackBis()
     {
         $translator = new Translator('ru', new MessageSelector());
-        $translator->setFallbackLocale(array('en_US', 'en'));
+        $translator->setFallbackLocales(array('en_US', 'en'));
         $translator->addLoader('array', new ArrayLoader());
         $translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US');
 
@@ -269,7 +269,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
     public function testTransChoiceFallbackWithNoTranslation()
     {
         $translator = new Translator('ru', new MessageSelector());
-        $translator->setFallbackLocale('en');
+        $translator->setFallbackLocales(array('en'));
         $translator->addLoader('array', new ArrayLoader());
 
         $this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));

+ 27 - 1
vendor/symfony/translation/Symfony/Component/Translation/Translator.php

@@ -122,14 +122,40 @@ class Translator implements TranslatorInterface
      *
      * @param string|array $locales The fallback locale(s)
      *
+     * @deprecated since 2.3, to be removed in 3.0. Use setFallbackLocales() instead.
+     *
      * @api
      */
     public function setFallbackLocale($locales)
+    {
+        $this->setFallbackLocales(is_array($locales) ? $locales : array($locales));
+    }
+
+    /**
+     * Sets the fallback locales.
+     *
+     * @param array $locales The fallback locales
+     *
+     * @api
+     */
+    public function setFallbackLocales(array $locales)
     {
         // needed as the fallback locales are linked to the already loaded catalogues
         $this->catalogues = array();
 
-        $this->fallbackLocales = is_array($locales) ? $locales : array($locales);
+        $this->fallbackLocales = $locales;
+    }
+
+    /**
+     * Gets the fallback locales.
+     *
+     * @return array $locales The fallback locales
+     *
+     * @api
+     */
+    public function getFallbackLocales()
+    {
+        return $this->fallbackLocales;
     }
 
     /**

+ 1 - 1
vendor/symfony/validator/Symfony/Component/Validator/Validator.php

@@ -52,7 +52,7 @@ class Validator implements ValidatorInterface
         MetadataFactoryInterface $metadataFactory,
         ConstraintValidatorFactoryInterface $validatorFactory,
         TranslatorInterface $translator,
-        $translationDomain = null,
+        $translationDomain = 'validators',
         array $objectInitializers = array()
     )
     {

+ 0 - 5
vendor/symfony/yaml/Symfony/Component/Yaml/Inline.php

@@ -235,11 +235,6 @@ class Inline
      */
     private static function parseQuotedScalar($scalar, &$i)
     {
-        // Only check the current item we're dealing with (for sequences)
-        $subject = substr($scalar, $i);
-        $items = preg_split('/[\'"]\s*(?:[,:]|[}\]]\s*,)/', $subject);
-        $subject = substr($subject, 0, strlen($items[0]) + 1);
-
         if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
             throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
         }

+ 1 - 1
vendor/twig/twig/doc/filters/merge.rst

@@ -1,7 +1,7 @@
 ``merge``
 =========
 
-The ``merge`` filter merges an array with the another array:
+The ``merge`` filter merges an array with another array:
 
 .. code-block:: jinja
 

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels