From dd2a440d31680394fe8102831a1dfe543a8620dc Mon Sep 17 00:00:00 2001
From: "g.oechsler" <g.oechsler@744070.no-reply.drupal.org>
Date: Thu, 18 Oct 2012 17:33:18 +0200
Subject: [PATCH] 1705488 - prototype of DrupalUrlGenerator

---
 .../Drupal/Routing/DrupalRouteCollection.php       |   14 +
 .../includes/Drupal/Routing/DrupalUrlGenerator.php |  154 ++++++++
 .../Symfony/Component/Routing/.gitattributes       |    2 +
 .../Symfony/Component/Routing/Annotation/Route.php |  103 +++++
 .../Symfony/Component/Routing/CHANGELOG.md         |   46 +++
 .../Symfony/Component/Routing/CompiledRoute.php    |   81 ++++
 .../Routing/Exception/ExceptionInterface.php       |   23 ++
 .../Exception/InvalidParameterException.php        |   23 ++
 .../Exception/MethodNotAllowedException.php        |   38 ++
 .../MissingMandatoryParametersException.php        |   24 ++
 .../Exception/ResourceNotFoundException.php        |   25 ++
 .../Routing/Exception/RouteNotFoundException.php   |   23 ++
 .../ConfigurableRequirementsInterface.php          |   55 +++
 .../Routing/Generator/Dumper/GeneratorDumper.php   |   42 +++
 .../Generator/Dumper/GeneratorDumperInterface.php  |   41 ++
 .../Generator/Dumper/PhpGeneratorDumper.php        |  124 ++++++
 .../Component/Routing/Generator/UrlGenerator.php   |  216 +++++++++++
 .../Routing/Generator/UrlGeneratorInterface.php    |   43 +++
 core/includes/Symfony/Component/Routing/LICENSE    |   19 +
 .../Routing/Loader/AnnotationClassLoader.php       |  204 ++++++++++
 .../Routing/Loader/AnnotationDirectoryLoader.php   |   77 ++++
 .../Routing/Loader/AnnotationFileLoader.php        |  120 ++++++
 .../Component/Routing/Loader/ClosureLoader.php     |   49 +++
 .../Component/Routing/Loader/PhpFileLoader.php     |   59 +++
 .../Component/Routing/Loader/XmlFileLoader.php     |  244 ++++++++++++
 .../Component/Routing/Loader/YamlFileLoader.php    |  141 +++++++
 .../Routing/Loader/schema/routing/routing-1.0.xsd  |   44 +++
 .../Component/Routing/Matcher/ApacheUrlMatcher.php |   76 ++++
 .../Routing/Matcher/Dumper/ApacheMatcherDumper.php |  164 ++++++++
 .../Routing/Matcher/Dumper/MatcherDumper.php       |   42 +++
 .../Matcher/Dumper/MatcherDumperInterface.php      |   37 ++
 .../Routing/Matcher/Dumper/PhpMatcherDumper.php    |  293 +++++++++++++++
 .../Routing/Matcher/RedirectableUrlMatcher.php     |   61 +++
 .../Matcher/RedirectableUrlMatcherInterface.php    |   35 ++
 .../Routing/Matcher/RequestMatcherInterface.php    |   39 ++
 .../Routing/Matcher/TraceableUrlMatcher.php        |  121 ++++++
 .../Component/Routing/Matcher/UrlMatcher.php       |  186 +++++++++
 .../Routing/Matcher/UrlMatcherInterface.php        |   43 +++
 core/includes/Symfony/Component/Routing/README.md  |   37 ++
 .../Symfony/Component/Routing/RequestContext.php   |  262 +++++++++++++
 .../Routing/RequestContextAwareInterface.php       |   36 ++
 core/includes/Symfony/Component/Routing/Route.php  |  393 ++++++++++++++++++++
 .../Symfony/Component/Routing/RouteCollection.php  |  346 +++++++++++++++++
 .../Symfony/Component/Routing/RouteCompiler.php    |  182 +++++++++
 .../Component/Routing/RouteCompilerInterface.php   |   29 ++
 core/includes/Symfony/Component/Routing/Router.php |  252 +++++++++++++
 .../Symfony/Component/Routing/RouterInterface.php  |   32 ++
 .../Routing/Tests/Annotation/RouteTest.php         |   45 +++
 .../Component/Routing/Tests/CompiledRouteTest.php  |   26 ++
 .../Fixtures/AnnotatedClasses/AbstractClass.php    |   16 +
 .../Tests/Fixtures/AnnotatedClasses/FooClass.php   |   16 +
 .../Routing/Tests/Fixtures/CustomXmlFileLoader.php |   25 ++
 .../Tests/Fixtures/RedirectableUrlMatcher.php      |   30 ++
 .../Tests/Fixtures/dumper/url_matcher1.apache      |   75 ++++
 .../Routing/Tests/Fixtures/dumper/url_matcher1.php |  239 ++++++++++++
 .../Tests/Fixtures/dumper/url_matcher2.apache      |    7 +
 .../Routing/Tests/Fixtures/dumper/url_matcher2.php |  269 ++++++++++++++
 .../Routing/Tests/Fixtures/dumper/url_matcher3.php |   45 +++
 .../Routing/Tests/Fixtures/incomplete.yml          |    2 +
 .../Component/Routing/Tests/Fixtures/nonvalid.xml  |   12 +
 .../Component/Routing/Tests/Fixtures/nonvalid.yml  |    1 +
 .../Routing/Tests/Fixtures/nonvalidkeys.yml        |    3 +
 .../Routing/Tests/Fixtures/nonvalidnode.xml        |    8 +
 .../Routing/Tests/Fixtures/nonvalidroute.xml       |   13 +
 .../Routing/Tests/Fixtures/validpattern.php        |   13 +
 .../Routing/Tests/Fixtures/validpattern.xml        |   12 +
 .../Routing/Tests/Fixtures/validpattern.yml        |    5 +
 .../Routing/Tests/Fixtures/validresource.xml       |   12 +
 .../Routing/Tests/Fixtures/validresource.yml       |    6 +
 .../Routing/Tests/Fixtures/withdoctype.xml         |    3 +
 .../Generator/Dumper/PhpGeneratorDumperTest.php    |  117 ++++++
 .../Routing/Tests/Generator/UrlGeneratorTest.php   |  391 +++++++++++++++++++
 .../Tests/Loader/AbstractAnnotationLoaderTest.php  |   38 ++
 .../Tests/Loader/AnnotationClassLoaderTest.php     |   74 ++++
 .../Tests/Loader/AnnotationDirectoryLoaderTest.php |   50 +++
 .../Tests/Loader/AnnotationFileLoaderTest.php      |   50 +++
 .../Routing/Tests/Loader/ClosureLoaderTest.php     |   61 +++
 .../Routing/Tests/Loader/PhpFileLoaderTest.php     |   52 +++
 .../Routing/Tests/Loader/XmlFileLoaderTest.php     |  101 +++++
 .../Routing/Tests/Loader/YamlFileLoaderTest.php    |  115 ++++++
 .../Routing/Tests/Matcher/ApacheUrlMatcherTest.php |  109 ++++++
 .../Matcher/Dumper/ApacheMatcherDumperTest.php     |  130 +++++++
 .../Tests/Matcher/Dumper/PhpMatcherDumperTest.php  |  202 ++++++++++
 .../Tests/Matcher/RedirectableUrlMatcherTest.php   |   58 +++
 .../Tests/Matcher/TraceableUrlMatcherTest.php      |   60 +++
 .../Routing/Tests/Matcher/UrlMatcherTest.php       |  331 +++++++++++++++++
 .../Routing/Tests/RouteCollectionTest.php          |  291 +++++++++++++++
 .../Component/Routing/Tests/RouteCompilerTest.php  |  182 +++++++++
 .../Symfony/Component/Routing/Tests/RouteTest.php  |  130 +++++++
 .../Symfony/Component/Routing/Tests/bootstrap.php  |   22 ++
 .../Symfony/Component/Routing/composer.json        |   42 +++
 .../Symfony/Component/Routing/phpunit.xml.dist     |   29 ++
 core/includes/common.inc                           |  129 +------
 93 files changed, 8234 insertions(+), 113 deletions(-)
 create mode 100644 core/includes/Drupal/Routing/DrupalRouteCollection.php
 create mode 100644 core/includes/Drupal/Routing/DrupalUrlGenerator.php
 create mode 100644 core/includes/Symfony/Component/Routing/.gitattributes
 create mode 100644 core/includes/Symfony/Component/Routing/Annotation/Route.php
 create mode 100644 core/includes/Symfony/Component/Routing/CHANGELOG.md
 create mode 100644 core/includes/Symfony/Component/Routing/CompiledRoute.php
 create mode 100644 core/includes/Symfony/Component/Routing/Exception/ExceptionInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Exception/InvalidParameterException.php
 create mode 100644 core/includes/Symfony/Component/Routing/Exception/MethodNotAllowedException.php
 create mode 100644 core/includes/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php
 create mode 100644 core/includes/Symfony/Component/Routing/Exception/ResourceNotFoundException.php
 create mode 100644 core/includes/Symfony/Component/Routing/Exception/RouteNotFoundException.php
 create mode 100644 core/includes/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php
 create mode 100644 core/includes/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php
 create mode 100644 core/includes/Symfony/Component/Routing/Generator/UrlGenerator.php
 create mode 100644 core/includes/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/LICENSE
 create mode 100644 core/includes/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
 create mode 100644 core/includes/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php
 create mode 100644 core/includes/Symfony/Component/Routing/Loader/AnnotationFileLoader.php
 create mode 100644 core/includes/Symfony/Component/Routing/Loader/ClosureLoader.php
 create mode 100644 core/includes/Symfony/Component/Routing/Loader/PhpFileLoader.php
 create mode 100644 core/includes/Symfony/Component/Routing/Loader/XmlFileLoader.php
 create mode 100644 core/includes/Symfony/Component/Routing/Loader/YamlFileLoader.php
 create mode 100644 core/includes/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/RequestMatcherInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/UrlMatcher.php
 create mode 100644 core/includes/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/README.md
 create mode 100644 core/includes/Symfony/Component/Routing/RequestContext.php
 create mode 100644 core/includes/Symfony/Component/Routing/RequestContextAwareInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Route.php
 create mode 100644 core/includes/Symfony/Component/Routing/RouteCollection.php
 create mode 100644 core/includes/Symfony/Component/Routing/RouteCompiler.php
 create mode 100644 core/includes/Symfony/Component/Routing/RouteCompilerInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Router.php
 create mode 100644 core/includes/Symfony/Component/Routing/RouterInterface.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Annotation/RouteTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/CompiledRouteTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/annotated.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/empty.yml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/foo.xml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/foo1.xml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/validresource.xml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/validresource.yml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/RouteCollectionTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/RouteCompilerTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/RouteTest.php
 create mode 100644 core/includes/Symfony/Component/Routing/Tests/bootstrap.php
 create mode 100644 core/includes/Symfony/Component/Routing/composer.json
 create mode 100644 core/includes/Symfony/Component/Routing/phpunit.xml.dist

diff --git a/core/includes/Drupal/Routing/DrupalRouteCollection.php b/core/includes/Drupal/Routing/DrupalRouteCollection.php
new file mode 100644
index 0000000..6e714b1
--- /dev/null
+++ b/core/includes/Drupal/Routing/DrupalRouteCollection.php
@@ -0,0 +1,14 @@
+<?php
+namespace Drupal\Routing;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+
+class DrupalRouteCollection extends RouteCollection {
+
+  public function add($name, Route $route) {
+    $this->remove($name);
+
+    $this->routes[$name] = $route;
+  }
+}
diff --git a/core/includes/Drupal/Routing/DrupalUrlGenerator.php b/core/includes/Drupal/Routing/DrupalUrlGenerator.php
new file mode 100644
index 0000000..6942217
--- /dev/null
+++ b/core/includes/Drupal/Routing/DrupalUrlGenerator.php
@@ -0,0 +1,154 @@
+<?php
+namespace Drupal\Routing;
+
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\LoggerInterface;
+use Symfony\Component\Routing\Route;
+
+class DrupalUrlGenerator extends UrlGenerator{
+  
+  public function url($path = NULL, $options = array()) {
+    if (null === $route = $this->routes->get($path)) {
+      $original_url = original_url($path, $options);
+      $this->routes->add($path, new Route($original_url));
+    }
+    
+    try {
+      $url = $this->generate($path);
+    }
+    catch (Exception $e) {
+      _drupal_render_exception_safe($e);
+    }
+
+    return $url;
+  }
+  
+  protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute) {
+    $url = '';
+    foreach ($tokens as $token) {
+      if ($token[0] === 'text') {
+        $url = $token[1].$url;
+      }
+    }
+    return $url;
+  }
+}
+
+function original_url($path = NULL, array $options = array()) {
+  // Merge in defaults.
+  $options += array(
+    'fragment' => '',
+    'query' => array(),
+    'absolute' => FALSE,
+    'alias' => FALSE,
+    'prefix' => ''
+  );
+
+  if (!isset($options['external'])) {
+    // Return an external link if $path contains an allowed absolute URL. Only
+    // call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
+    // before any / ? or #. Note: we could use url_is_external($path) here, but
+    // that would require another function call, and performance inside url() is
+    // critical.
+    $colonpos = strpos($path, ':');
+    $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
+  }
+
+  // Preserve the original path before altering or aliasing.
+  $original_path = $path;
+
+  // Allow other modules to alter the outbound URL and options.
+  drupal_alter('url_outbound', $path, $options, $original_path);
+
+  if (isset($options['fragment']) && $options['fragment'] !== '') {
+    $options['fragment'] = '#' . $options['fragment'];
+  }
+
+  if ($options['external']) {
+    // Split off the fragment.
+    if (strpos($path, '#') !== FALSE) {
+      list($path, $old_fragment) = explode('#', $path, 2);
+      // If $options contains no fragment, take it over from the path.
+      if (isset($old_fragment) && !$options['fragment']) {
+        $options['fragment'] = '#' . $old_fragment;
+      }
+    }
+    // Append the query.
+    if ($options['query']) {
+      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($options['query']);
+    }
+    if (isset($options['https']) && variable_get('https', FALSE)) {
+      if ($options['https'] === TRUE) {
+        $path = str_replace('http://', 'https://', $path);
+      }
+      elseif ($options['https'] === FALSE) {
+        $path = str_replace('https://', 'http://', $path);
+      }
+    }
+    // Reassemble.
+    return $path . $options['fragment'];
+  }
+
+  global $base_url, $base_secure_url, $base_insecure_url;
+
+  // The base_url might be rewritten from the language rewrite in domain mode.
+  if (!isset($options['base_url'])) {
+    if (isset($options['https']) && variable_get('https', FALSE)) {
+      if ($options['https'] === TRUE) {
+        $options['base_url'] = $base_secure_url;
+        $options['absolute'] = TRUE;
+      }
+      elseif ($options['https'] === FALSE) {
+        $options['base_url'] = $base_insecure_url;
+        $options['absolute'] = TRUE;
+      }
+    }
+    else {
+      $options['base_url'] = $base_url;
+    }
+  }
+
+  // The special path '<front>' links to the default front page.
+  if ($path == '<front>') {
+    $path = '';
+  }
+  elseif (!empty($path) && !$options['alias']) {
+    $language = isset($options['language']) && isset($options['language']->language) ? $options['language']->language : '';
+    $alias = drupal_get_path_alias($original_path, $language);
+    if ($alias != $original_path) {
+      $path = $alias;
+    }
+  }
+
+  $base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
+  $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
+
+  // With Clean URLs.
+  if (!empty($GLOBALS['conf']['clean_url'])) {
+    $path = drupal_encode_path($prefix . $path);
+    if ($options['query']) {
+      return $base . $path . '?' . drupal_http_build_query($options['query']) . $options['fragment'];
+    }
+    else {
+      return $base . $path . $options['fragment'];
+    }
+  }
+  // Without Clean URLs.
+  else {
+    $path = $prefix . $path;
+    $query = array();
+    if (!empty($path)) {
+      $query['q'] = $path;
+    }
+    if ($options['query']) {
+      // We do not use array_merge() here to prevent overriding $path via query
+      // parameters.
+      $query += $options['query'];
+    }
+    $query = $query ? ('?' . drupal_http_build_query($query)) : '';
+    $script = isset($options['script']) ? $options['script'] : '';
+    return $base . $script . $query . $options['fragment'];
+  }
+}
+
diff --git a/core/includes/Symfony/Component/Routing/.gitattributes b/core/includes/Symfony/Component/Routing/.gitattributes
new file mode 100644
index 0000000..e742c9b
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/.gitattributes
@@ -0,0 +1,2 @@
+Tests/ export-ignore
+phpunit.xml.dist export-ignore
diff --git a/core/includes/Symfony/Component/Routing/Annotation/Route.php b/core/includes/Symfony/Component/Routing/Annotation/Route.php
new file mode 100644
index 0000000..f60af46
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Annotation/Route.php
@@ -0,0 +1,103 @@
+<?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\Routing\Annotation;
+
+/**
+ * Annotation class for @Route().
+ *
+ * @Annotation
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Route
+{
+    private $pattern;
+    private $name;
+    private $requirements;
+    private $options;
+    private $defaults;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data An array of key/value parameters.
+     */
+    public function __construct(array $data)
+    {
+        $this->requirements = array();
+        $this->options = array();
+        $this->defaults = array();
+
+        if (isset($data['value'])) {
+            $data['pattern'] = $data['value'];
+            unset($data['value']);
+        }
+
+        foreach ($data as $key => $value) {
+            $method = 'set'.$key;
+            if (!method_exists($this, $method)) {
+                throw new \BadMethodCallException(sprintf("Unknown property '%s' on annotation '%s'.", $key, get_class($this)));
+            }
+            $this->$method($value);
+        }
+    }
+
+    public function setPattern($pattern)
+    {
+        $this->pattern = $pattern;
+    }
+
+    public function getPattern()
+    {
+        return $this->pattern;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setRequirements($requirements)
+    {
+        $this->requirements = $requirements;
+    }
+
+    public function getRequirements()
+    {
+        return $this->requirements;
+    }
+
+    public function setOptions($options)
+    {
+        $this->options = $options;
+    }
+
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    public function setDefaults($defaults)
+    {
+        $this->defaults = $defaults;
+    }
+
+    public function getDefaults()
+    {
+        return $this->defaults;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/CHANGELOG.md b/core/includes/Symfony/Component/Routing/CHANGELOG.md
new file mode 100644
index 0000000..d02009d
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/CHANGELOG.md
@@ -0,0 +1,46 @@
+CHANGELOG
+=========
+
+2.2.0
+-----
+
+ * Adjacent placeholders without separator work now, e.g. `/{x}{y}{z}.{_format}`.
+ * Characters that function as separator between placeholders are now whitelisted
+   to fix routes with normal text around a variable, e.g. `/prefix{var}suffix`.
+ * [BC BREAK] The default requirement of a variable has been changed slightly.
+   Previously it disallowed the previous and the next char around a variable. Now
+   it disallows the slash (`/`) and the next char. Using the previous char added
+   no value and was problematic because the route `/index.{_format}` would be
+   matched by `/index.ht/ml`.
+ * The default requirement now uses possesive quantifiers when possible which
+   improves matching performance by up to 20% because it prevents backtracking
+   when it's not needed.
+ * The ConfigurableRequirementsInterface can now also be used to disable the requirements
+   check on URL generation completely by calling `setStrictRequirements(null)`. It
+   improves performance in production environment as you should know that params always
+   pass the requirements (otherwise it would break your link anyway).
+
+2.1.0
+-----
+
+ * added RequestMatcherInterface
+ * added RequestContext::fromRequest()
+ * the UrlMatcher does not throw a \LogicException anymore when the required
+   scheme is not the current one
+ * added TraceableUrlMatcher
+ * added the possibility to define options, default values and requirements
+   for placeholders in prefix, including imported routes
+ * added RouterInterface::getRouteCollection
+ * [BC BREAK] the UrlMatcher urldecodes the route parameters only once, they
+   were decoded twice before. Note that the `urldecode()` calls have been
+   changed for a single `rawurldecode()` in order to support `+` for input
+   paths.
+ * added RouteCollection::getRoot method to retrieve the root of a
+   RouteCollection tree
+ * [BC BREAK] made RouteCollection::setParent private which could not have
+   been used anyway without creating inconsistencies
+ * [BC BREAK] RouteCollection::remove also removes a route from parent
+   collections (not only from its children)
+ * added ConfigurableRequirementsInterface that allows to disable exceptions 
+   (and generate empty URLs instead) when generating a route with an invalid
+   parameter value
diff --git a/core/includes/Symfony/Component/Routing/CompiledRoute.php b/core/includes/Symfony/Component/Routing/CompiledRoute.php
new file mode 100644
index 0000000..54d4e2c
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/CompiledRoute.php
@@ -0,0 +1,81 @@
+<?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\Routing;
+
+/**
+ * CompiledRoutes are returned by the RouteCompiler class.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class CompiledRoute
+{
+    private $variables;
+    private $tokens;
+    private $staticPrefix;
+    private $regex;
+
+    /**
+     * Constructor.
+     *
+     * @param string $staticPrefix The static prefix of the compiled route
+     * @param string $regex        The regular expression to use to match this route
+     * @param array  $tokens       An array of tokens to use to generate URL for this route
+     * @param array  $variables    An array of variables
+     */
+    public function __construct($staticPrefix, $regex, array $tokens, array $variables)
+    {
+        $this->staticPrefix = $staticPrefix;
+        $this->regex = $regex;
+        $this->tokens = $tokens;
+        $this->variables = $variables;
+    }
+
+    /**
+     * Returns the static prefix.
+     *
+     * @return string The static prefix
+     */
+    public function getStaticPrefix()
+    {
+        return $this->staticPrefix;
+    }
+
+    /**
+     * Returns the regex.
+     *
+     * @return string The regex
+     */
+    public function getRegex()
+    {
+        return $this->regex;
+    }
+
+    /**
+     * Returns the tokens.
+     *
+     * @return array The tokens
+     */
+    public function getTokens()
+    {
+        return $this->tokens;
+    }
+
+    /**
+     * Returns the variables.
+     *
+     * @return array The variables
+     */
+    public function getVariables()
+    {
+        return $this->variables;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Exception/ExceptionInterface.php b/core/includes/Symfony/Component/Routing/Exception/ExceptionInterface.php
new file mode 100644
index 0000000..5289f52
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Exception/ExceptionInterface.php
@@ -0,0 +1,23 @@
+<?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\Routing\Exception;
+
+/**
+ * ExceptionInterface
+ *
+ * @author Alexandre Salomé <alexandre.salome@gmail.com>
+ *
+ * @api
+ */
+interface ExceptionInterface
+{
+}
diff --git a/core/includes/Symfony/Component/Routing/Exception/InvalidParameterException.php b/core/includes/Symfony/Component/Routing/Exception/InvalidParameterException.php
new file mode 100644
index 0000000..4f12469
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Exception/InvalidParameterException.php
@@ -0,0 +1,23 @@
+<?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\Routing\Exception;
+
+/**
+ * Exception thrown when a parameter is not valid
+ *
+ * @author Alexandre Salomé <alexandre.salome@gmail.com>
+ *
+ * @api
+ */
+class InvalidParameterException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/core/includes/Symfony/Component/Routing/Exception/MethodNotAllowedException.php b/core/includes/Symfony/Component/Routing/Exception/MethodNotAllowedException.php
new file mode 100644
index 0000000..470ce52
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Exception/MethodNotAllowedException.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\Routing\Exception;
+
+/**
+ * The resource was found but the request method is not allowed.
+ *
+ * This exception should trigger an HTTP 405 response in your application code.
+ *
+ * @author Kris Wallsmith <kris@symfony.com>
+ *
+ * @api
+ */
+class MethodNotAllowedException extends \RuntimeException implements ExceptionInterface
+{
+    protected $allowedMethods;
+
+    public function __construct(array $allowedMethods, $message = null, $code = 0, \Exception $previous = null)
+    {
+        $this->allowedMethods = array_map('strtoupper', $allowedMethods);
+
+        parent::__construct($message, $code, $previous);
+    }
+
+    public function getAllowedMethods()
+    {
+        return $this->allowedMethods;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php b/core/includes/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php
new file mode 100644
index 0000000..5a523fa
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php
@@ -0,0 +1,24 @@
+<?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\Routing\Exception;
+
+/**
+ * Exception thrown when a route cannot be generated because of missing
+ * mandatory parameters.
+ *
+ * @author Alexandre Salomé <alexandre.salome@gmail.com>
+ *
+ * @api
+ */
+class MissingMandatoryParametersException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/core/includes/Symfony/Component/Routing/Exception/ResourceNotFoundException.php b/core/includes/Symfony/Component/Routing/Exception/ResourceNotFoundException.php
new file mode 100644
index 0000000..362a0d6
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Exception/ResourceNotFoundException.php
@@ -0,0 +1,25 @@
+<?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\Routing\Exception;
+
+/**
+ * The resource was not found.
+ *
+ * This exception should trigger an HTTP 404 response in your application code.
+ *
+ * @author Kris Wallsmith <kris@symfony.com>
+ *
+ * @api
+ */
+class ResourceNotFoundException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/core/includes/Symfony/Component/Routing/Exception/RouteNotFoundException.php b/core/includes/Symfony/Component/Routing/Exception/RouteNotFoundException.php
new file mode 100644
index 0000000..4d5f288
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Exception/RouteNotFoundException.php
@@ -0,0 +1,23 @@
+<?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\Routing\Exception;
+
+/**
+ * Exception thrown when a route does not exists
+ *
+ * @author Alexandre Salomé <alexandre.salome@gmail.com>
+ *
+ * @api
+ */
+class RouteNotFoundException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/core/includes/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php b/core/includes/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php
new file mode 100644
index 0000000..5925838
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Generator/ConfigurableRequirementsInterface.php
@@ -0,0 +1,55 @@
+<?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\Routing\Generator;
+
+/**
+ * ConfigurableRequirementsInterface must be implemented by URL generators that
+ * can be configured whether an exception should be generated when the parameters
+ * do not match the requirements. It is also possible to disable the requirements
+ * check for URL generation completely.
+ *
+ * The possible configurations and use-cases:
+ * - setStrictRequirements(true): Throw an exception for mismatching requirements. This
+ *   is mostly useful in development environment.
+ * - setStrictRequirements(false): Don't throw an exception but return null as URL for
+ *   mismatching requirements and log the problem. Useful when you cannot control all
+ *   params because they come from third party libs but don't want to have a 404 in
+ *   production environment. It should log the mismatch so one can review it.
+ * - setStrictRequirements(null): Return the URL with the given parameters without
+ *   checking the requirements at all. When generating an URL you should either trust
+ *   your params or you validated them beforehand because otherwise it would break your
+ *   link anyway. So in production environment you should know that params always pass
+ *   the requirements. Thus this option allows to disable the check on URL generation for
+ *   performance reasons (saving a preg_match for each requirement every time a URL is
+ *   generated).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ */
+interface ConfigurableRequirementsInterface
+{
+    /**
+     * Enables or disables the exception on incorrect parameters.
+     * Passing null will deactivate the requirements check completely.
+     *
+     * @param Boolean|null $enabled
+     */
+    public function setStrictRequirements($enabled);
+
+    /**
+     * Returns whether to throw an exception on incorrect parameters.
+     * Null means the requirements check is deactivated completely.
+     *
+     * @return Boolean|null
+     */
+    public function isStrictRequirements();
+}
diff --git a/core/includes/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php b/core/includes/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php
new file mode 100644
index 0000000..408be9c
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php
@@ -0,0 +1,42 @@
+<?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\Routing\Generator\Dumper;
+
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * GeneratorDumper is the base class for all built-in generator dumpers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class GeneratorDumper implements GeneratorDumperInterface
+{
+    private $routes;
+
+    /**
+     * Constructor.
+     *
+     * @param RouteCollection $routes The RouteCollection to dump
+     */
+    public function __construct(RouteCollection $routes)
+    {
+        $this->routes = $routes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRoutes()
+    {
+        return $this->routes;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php b/core/includes/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php
new file mode 100644
index 0000000..deb0c0a
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php
@@ -0,0 +1,41 @@
+<?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\Routing\Generator\Dumper;
+
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * GeneratorDumperInterface is the interface that all generator dumper classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface GeneratorDumperInterface
+{
+    /**
+     * Dumps a set of routes to a string representation of executable code
+     * that can then be used to generate a URL of such a route.
+     *
+     * @param array $options An array of options
+     *
+     * @return string Executable code
+     */
+    public function dump(array $options = array());
+
+    /**
+     * Gets the routes to dump.
+     *
+     * @return RouteCollection A RouteCollection instance
+     */
+    public function getRoutes();
+}
diff --git a/core/includes/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/core/includes/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php
new file mode 100644
index 0000000..9b5cc05
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php
@@ -0,0 +1,124 @@
+<?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\Routing\Generator\Dumper;
+
+use Symfony\Component\Routing\Route;
+
+/**
+ * PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class PhpGeneratorDumper extends GeneratorDumper
+{
+    /**
+     * Dumps a set of routes to a PHP class.
+     *
+     * Available options:
+     *
+     *  * class:      The class name
+     *  * base_class: The base class name
+     *
+     * @param array $options An array of options
+     *
+     * @return string A PHP class representing the generator class
+     *
+     * @api
+     */
+    public function dump(array $options = array())
+    {
+        $options = array_merge(array(
+            'class'      => 'ProjectUrlGenerator',
+            'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
+        ), $options);
+
+        return <<<EOF
+<?php
+
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+
+/**
+ * {$options['class']}
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class {$options['class']} extends {$options['base_class']}
+{
+    static private \$declaredRoutes = {$this->generateDeclaredRoutes()};
+
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext \$context, LoggerInterface \$logger = null)
+    {
+        \$this->context = \$context;
+        \$this->logger = \$logger;
+    }
+
+{$this->generateGenerateMethod()}
+}
+
+EOF;
+    }
+
+    /**
+     * Generates PHP code representing an array of defined routes
+     * together with the routes properties (e.g. requirements).
+     *
+     * @return string PHP code
+     */
+    private function generateDeclaredRoutes()
+    {
+        $routes = "array(\n";
+        foreach ($this->getRoutes()->all() as $name => $route) {
+            $compiledRoute = $route->compile();
+
+            $properties = array();
+            $properties[] = $compiledRoute->getVariables();
+            $properties[] = $route->getDefaults();
+            $properties[] = $route->getRequirements();
+            $properties[] = $compiledRoute->getTokens();
+
+            $routes .= sprintf("        '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true)));
+        }
+        $routes .= '    )';
+
+        return $routes;
+    }
+
+    /**
+     * Generates PHP code representing the `generate` method that implements the UrlGeneratorInterface.
+     *
+     * @return string PHP code
+     */
+    private function generateGenerateMethod()
+    {
+        return <<<EOF
+    public function generate(\$name, \$parameters = array(), \$absolute = false)
+    {
+        if (!isset(self::\$declaredRoutes[\$name])) {
+            throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', \$name));
+        }
+
+        list(\$variables, \$defaults, \$requirements, \$tokens) = self::\$declaredRoutes[\$name];
+
+        return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$absolute);
+    }
+EOF;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Generator/UrlGenerator.php b/core/includes/Symfony/Component/Routing/Generator/UrlGenerator.php
new file mode 100644
index 0000000..c1c1aef
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Generator/UrlGenerator.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\Routing\Generator;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Exception\InvalidParameterException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+
+/**
+ * UrlGenerator generates a URL based on a set of routes.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface
+{
+    protected $context;
+    protected $strictRequirements = true;
+    protected $logger;
+
+    /**
+     * This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL.
+     *
+     * PHP's rawurlencode() encodes all chars except "a-zA-Z0-9-._~" according to RFC 3986. But we want to allow some chars
+     * to be used in their literal form (reasons below). Other chars inside the path must of course be encoded, e.g.
+     * "?" and "#" (would be interpreted wrongly as query and fragment identifier),
+     * "'" and """ (are used as delimiters in HTML).
+     */
+    protected $decodedChars = array(
+        // the slash can be used to designate a hierarchical structure and we want allow using it with this meaning
+        // some webservers don't allow the slash in encoded form in the path for security reasons anyway
+        // see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss
+        '%2F' => '/',
+        // the following chars are general delimiters in the URI specification but have only special meaning in the authority component
+        // so they can safely be used in the path in unencoded form
+        '%40' => '@',
+        '%3A' => ':',
+        // these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally
+        // so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability
+        '%3B' => ';',
+        '%2C' => ',',
+        '%3D' => '=',
+        '%2B' => '+',
+        '%21' => '!',
+        '%2A' => '*',
+        '%7C' => '|',
+    );
+
+    protected $routes;
+
+    /**
+     * Constructor.
+     *
+     * @param RouteCollection $routes  A RouteCollection instance
+     * @param RequestContext  $context The context
+     * @param LoggerInterface $logger  A logger instance
+     *
+     * @api
+     */
+    public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null)
+    {
+        $this->routes = $routes;
+        $this->context = $context;
+        $this->logger = $logger;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setContext(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setStrictRequirements($enabled)
+    {
+        $this->strictRequirements = null === $enabled ? null : (Boolean) $enabled;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isStrictRequirements()
+    {
+        return $this->strictRequirements;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function generate($name, $parameters = array(), $absolute = false)
+    {
+        if (null === $route = $this->routes->get($name)) {
+            throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
+        }
+
+        // the Route has a cache of its own and is not recompiled as long as it does not get modified
+        $compiledRoute = $route->compile();
+
+        return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $absolute);
+    }
+
+    /**
+     * @throws MissingMandatoryParametersException When route has some missing mandatory parameters
+     * @throws InvalidParameterException When a parameter value is not correct
+     */
+    protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute)
+    {
+        $variables = array_flip($variables);
+        $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
+
+        // all params must be given
+        if ($diff = array_diff_key($variables, $mergedParams)) {
+            throw new MissingMandatoryParametersException(sprintf('The "%s" route has some missing mandatory parameters ("%s").', $name, implode('", "', array_keys($diff))));
+        }
+
+        $url = '';
+        $optional = true;
+        foreach ($tokens as $token) {
+            if ('variable' === $token[0]) {
+                if (!$optional || !array_key_exists($token[3], $defaults) || (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
+                    // check requirement
+                    if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) {
+                        $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $mergedParams[$token[3]]);
+                        if ($this->strictRequirements) {
+                            throw new InvalidParameterException($message);
+                        }
+
+                        if ($this->logger) {
+                            $this->logger->err($message);
+                        }
+
+                        return null;
+                    }
+
+                    $url = $token[1].$mergedParams[$token[3]].$url;
+                    $optional = false;
+                }
+            } else {
+                // static text
+                $url = $token[1].$url;
+                $optional = false;
+            }
+        }
+
+        if ('' === $url) {
+            $url = '/';
+        }
+
+        // do not encode the contexts base url as it is already encoded (see Symfony\Component\HttpFoundation\Request)
+        $url = $this->context->getBaseUrl().strtr(rawurlencode($url), $this->decodedChars);
+
+        // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3
+        // so we need to encode them as they are not used for this purpose here
+        // otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route
+        $url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/'));
+        if ('/..' === substr($url, -3)) {
+            $url = substr($url, 0, -2) . '%2E%2E';
+        } elseif ('/.' === substr($url, -2)) {
+            $url = substr($url, 0, -1) . '%2E';
+        }
+
+        // add a query string if needed
+        $extra = array_diff_key($parameters, $variables);
+        if ($extra && $query = http_build_query($extra, '', '&')) {
+            $url .= '?'.$query;
+        }
+
+        if ($this->context->getHost()) {
+            $scheme = $this->context->getScheme();
+            if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) {
+                $absolute = true;
+                $scheme = $req;
+            }
+
+            if ($absolute) {
+                $port = '';
+                if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
+                    $port = ':'.$this->context->getHttpPort();
+                } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
+                    $port = ':'.$this->context->getHttpsPort();
+                }
+
+                $url = $scheme.'://'.$this->context->getHost().$port.$url;
+            }
+        }
+
+        return $url;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php b/core/includes/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php
new file mode 100644
index 0000000..e28c79d
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php
@@ -0,0 +1,43 @@
+<?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\Routing\Generator;
+
+use Symfony\Component\Routing\RequestContextAwareInterface;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+
+/**
+ * UrlGeneratorInterface is the interface that all URL generator classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface UrlGeneratorInterface extends RequestContextAwareInterface
+{
+    /**
+     * Generates a URL from the given parameters.
+     *
+     * If the generator is not able to generate the url, it must throw the RouteNotFoundException
+     * as documented below.
+     *
+     * @param string  $name       The name of the route
+     * @param mixed   $parameters An array of parameters
+     * @param Boolean $absolute   Whether to generate an absolute URL
+     *
+     * @return string The generated URL
+     *
+     * @throws RouteNotFoundException if route doesn't exist
+     *
+     * @api
+     */
+    public function generate($name, $parameters = array(), $absolute = false);
+}
diff --git a/core/includes/Symfony/Component/Routing/LICENSE b/core/includes/Symfony/Component/Routing/LICENSE
new file mode 100644
index 0000000..cdffe7a
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2012 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.
diff --git a/core/includes/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/core/includes/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
new file mode 100644
index 0000000..d94f7d2
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
@@ -0,0 +1,204 @@
+<?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\Routing\Loader;
+
+use Doctrine\Common\Annotations\Reader;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Config\Loader\LoaderInterface;
+use Symfony\Component\Config\Loader\LoaderResolverInterface;
+
+/**
+ * AnnotationClassLoader loads routing information from a PHP class and its methods.
+ *
+ * You need to define an implementation for the getRouteDefaults() method. Most of the
+ * time, this method should define some PHP callable to be called for the route
+ * (a controller in MVC speak).
+ *
+ * The @Route annotation can be set on the class (for global parameters),
+ * and on each method.
+ *
+ * The @Route annotation main value is the route pattern. The annotation also
+ * recognizes three parameters: requirements, options, and name. The name parameter
+ * is mandatory. Here is an example of how you should be able to use it:
+ *
+ *     /**
+ *      * @Route("/Blog")
+ *      * /
+ *     class Blog
+ *     {
+ *         /**
+ *          * @Route("/", name="blog_index")
+ *          * /
+ *         public function index()
+ *         {
+ *         }
+ *
+ *         /**
+ *          * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"})
+ *          * /
+ *         public function show()
+ *         {
+ *         }
+ *     }
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class AnnotationClassLoader implements LoaderInterface
+{
+    protected $reader;
+    protected $routeAnnotationClass  = 'Symfony\\Component\\Routing\\Annotation\\Route';
+    protected $defaultRouteIndex;
+
+    /**
+     * Constructor.
+     *
+     * @param Reader $reader
+     */
+    public function __construct(Reader $reader)
+    {
+        $this->reader = $reader;
+    }
+
+    /**
+     * Sets the annotation class to read route properties from.
+     *
+     * @param string $class A fully-qualified class name
+     */
+    public function setRouteAnnotationClass($class)
+    {
+        $this->routeAnnotationClass = $class;
+    }
+
+    /**
+     * Loads from annotations from a class.
+     *
+     * @param string $class A class name
+     * @param string $type  The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When route can't be parsed
+     */
+    public function load($class, $type = null)
+    {
+        if (!class_exists($class)) {
+            throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
+        }
+
+        $globals = array(
+            'pattern'      => '',
+            'requirements' => array(),
+            'options'      => array(),
+            'defaults'     => array(),
+        );
+
+        $class = new \ReflectionClass($class);
+        if ($class->isAbstract()) {
+            throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class));
+        }
+
+        if ($annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) {
+            if (null !== $annot->getPattern()) {
+                $globals['pattern'] = $annot->getPattern();
+            }
+
+            if (null !== $annot->getRequirements()) {
+                $globals['requirements'] = $annot->getRequirements();
+            }
+
+            if (null !== $annot->getOptions()) {
+                $globals['options'] = $annot->getOptions();
+            }
+
+            if (null !== $annot->getDefaults()) {
+                $globals['defaults'] = $annot->getDefaults();
+            }
+        }
+
+        $collection = new RouteCollection();
+        $collection->addResource(new FileResource($class->getFileName()));
+
+        foreach ($class->getMethods() as $method) {
+            $this->defaultRouteIndex = 0;
+            foreach ($this->reader->getMethodAnnotations($method) as $annot) {
+                if ($annot instanceof $this->routeAnnotationClass) {
+                    $this->addRoute($collection, $annot, $globals, $class, $method);
+                }
+            }
+        }
+
+        return $collection;
+    }
+
+    protected function addRoute(RouteCollection $collection, $annot, $globals, \ReflectionClass $class, \ReflectionMethod $method)
+    {
+        $name = $annot->getName();
+        if (null === $name) {
+            $name = $this->getDefaultRouteName($class, $method);
+        }
+
+        $defaults = array_merge($globals['defaults'], $annot->getDefaults());
+        $requirements = array_merge($globals['requirements'], $annot->getRequirements());
+        $options = array_merge($globals['options'], $annot->getOptions());
+
+        $route = new Route($globals['pattern'].$annot->getPattern(), $defaults, $requirements, $options);
+
+        $this->configureRoute($route, $class, $method, $annot);
+
+        $collection->add($name, $route);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setResolver(LoaderResolverInterface $resolver)
+    {
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getResolver()
+    {
+    }
+
+    /**
+     * Gets the default route name for a class method.
+     *
+     * @param \ReflectionClass  $class
+     * @param \ReflectionMethod $method
+     *
+     * @return string
+     */
+    protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method)
+    {
+        $name = strtolower(str_replace('\\', '_', $class->name).'_'.$method->name);
+        if ($this->defaultRouteIndex > 0) {
+            $name .= '_'.$this->defaultRouteIndex;
+        }
+        $this->defaultRouteIndex++;
+
+        return $name;
+    }
+
+    abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot);
+}
diff --git a/core/includes/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php b/core/includes/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php
new file mode 100644
index 0000000..f549fd0
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php
@@ -0,0 +1,77 @@
+<?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\Routing\Loader;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Config\Resource\DirectoryResource;
+
+/**
+ * AnnotationDirectoryLoader loads routing information from annotations set
+ * on PHP classes and methods.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class AnnotationDirectoryLoader extends AnnotationFileLoader
+{
+    /**
+     * Loads from annotations from a directory.
+     *
+     * @param string $path A directory path
+     * @param string $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When the directory does not exist or its routes cannot be parsed
+     */
+    public function load($path, $type = null)
+    {
+        $dir = $this->locator->locate($path);
+
+        $collection = new RouteCollection();
+        $collection->addResource(new DirectoryResource($dir, '/\.php$/'));
+        $files = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::LEAVES_ONLY));
+        usort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
+            return (string) $a > (string) $b ? 1 : -1;
+        });
+
+        foreach ($files as $file) {
+            if (!$file->isFile() || '.php' !== substr($file->getFilename(), -4)) {
+                continue;
+            }
+
+            if ($class = $this->findClass($file)) {
+                $refl = new \ReflectionClass($class);
+                if ($refl->isAbstract()) {
+                    continue;
+                }
+
+                $collection->addCollection($this->loader->load($class, $type));
+            }
+        }
+
+        return $collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports($resource, $type = null)
+    {
+        try {
+            $path = $this->locator->locate($resource);
+        } catch (\Exception $e) {
+            return false;
+        }
+
+        return is_string($resource) && is_dir($path) && (!$type || 'annotation' === $type);
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Loader/AnnotationFileLoader.php b/core/includes/Symfony/Component/Routing/Loader/AnnotationFileLoader.php
new file mode 100644
index 0000000..bb965e9
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Loader/AnnotationFileLoader.php
@@ -0,0 +1,120 @@
+<?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\Routing\Loader;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Loader\FileLoader;
+use Symfony\Component\Config\FileLocator;
+
+/**
+ * AnnotationFileLoader loads routing information from annotations set
+ * on a PHP class and its methods.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class AnnotationFileLoader extends FileLoader
+{
+    protected $loader;
+
+    /**
+     * Constructor.
+     *
+     * @param FileLocator           $locator A FileLocator instance
+     * @param AnnotationClassLoader $loader  An AnnotationClassLoader instance
+     * @param string|array          $paths   A path or an array of paths where to look for resources
+     */
+    public function __construct(FileLocator $locator, AnnotationClassLoader $loader, $paths = array())
+    {
+        if (!function_exists('token_get_all')) {
+            throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.');
+        }
+
+        parent::__construct($locator, $paths);
+
+        $this->loader = $loader;
+    }
+
+    /**
+     * Loads from annotations from a file.
+     *
+     * @param string $file A PHP file path
+     * @param string $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed
+     */
+    public function load($file, $type = null)
+    {
+        $path = $this->locator->locate($file);
+
+        $collection = new RouteCollection();
+        if ($class = $this->findClass($path)) {
+            $collection->addResource(new FileResource($path));
+            $collection->addCollection($this->loader->load($class, $type));
+        }
+
+        return $collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type);
+    }
+
+    /**
+     * Returns the full class name for the first class in the file.
+     *
+     * @param string $file A PHP file path
+     *
+     * @return string|false Full class name if found, false otherwise
+     */
+    protected function findClass($file)
+    {
+        $class = false;
+        $namespace = false;
+        $tokens = token_get_all(file_get_contents($file));
+        for ($i = 0, $count = count($tokens); $i < $count; $i++) {
+            $token = $tokens[$i];
+
+            if (!is_array($token)) {
+                continue;
+            }
+
+            if (true === $class && T_STRING === $token[0]) {
+                return $namespace.'\\'.$token[1];
+            }
+
+            if (true === $namespace && T_STRING === $token[0]) {
+                $namespace = '';
+                do {
+                    $namespace .= $token[1];
+                    $token = $tokens[++$i];
+                } while ($i < $count && is_array($token) && in_array($token[0], array(T_NS_SEPARATOR, T_STRING)));
+            }
+
+            if (T_CLASS === $token[0]) {
+                $class = true;
+            }
+
+            if (T_NAMESPACE === $token[0]) {
+                $namespace = true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Loader/ClosureLoader.php b/core/includes/Symfony/Component/Routing/Loader/ClosureLoader.php
new file mode 100644
index 0000000..c5ad1e7
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Loader/ClosureLoader.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\Routing\Loader;
+
+use Symfony\Component\Config\Loader\Loader;
+
+/**
+ * ClosureLoader loads routes from a PHP closure.
+ *
+ * The Closure must return a RouteCollection instance.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class ClosureLoader extends Loader
+{
+    /**
+     * Loads a Closure.
+     *
+     * @param \Closure $closure A Closure
+     * @param string   $type    The resource type
+     *
+     * @api
+     */
+    public function load($closure, $type = null)
+    {
+        return call_user_func($closure);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function supports($resource, $type = null)
+    {
+        return $resource instanceof \Closure && (!$type || 'closure' === $type);
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Loader/PhpFileLoader.php b/core/includes/Symfony/Component/Routing/Loader/PhpFileLoader.php
new file mode 100644
index 0000000..de2104b
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Loader/PhpFileLoader.php
@@ -0,0 +1,59 @@
+<?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\Routing\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Loader\FileLoader;
+
+/**
+ * PhpFileLoader loads routes from a PHP file.
+ *
+ * The file must return a RouteCollection instance.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class PhpFileLoader extends FileLoader
+{
+    /**
+     * Loads a PHP file.
+     *
+     * @param mixed  $file A PHP file path
+     * @param string $type The resource type
+     *
+     * @api
+     */
+    public function load($file, $type = null)
+    {
+        // the loader variable is exposed to the included file below
+        $loader = $this;
+
+        $path = $this->locator->locate($file);
+        $this->setCurrentDir(dirname($path));
+
+        $collection = include $path;
+        $collection->addResource(new FileResource($path));
+
+        return $collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'php' === $type);
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Loader/XmlFileLoader.php b/core/includes/Symfony/Component/Routing/Loader/XmlFileLoader.php
new file mode 100644
index 0000000..5e0093d
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -0,0 +1,244 @@
+<?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\Routing\Loader;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Loader\FileLoader;
+
+/**
+ * XmlFileLoader loads XML routing files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class XmlFileLoader extends FileLoader
+{
+    /**
+     * Loads an XML file.
+     *
+     * @param string $file An XML file path
+     * @param string $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When a tag can't be parsed
+     *
+     * @api
+     */
+    public function load($file, $type = null)
+    {
+        $path = $this->locator->locate($file);
+
+        $xml = $this->loadFile($path);
+
+        $collection = new RouteCollection();
+        $collection->addResource(new FileResource($path));
+
+        // process routes and imports
+        foreach ($xml->documentElement->childNodes as $node) {
+            if (!$node instanceof \DOMElement) {
+                continue;
+            }
+
+            $this->parseNode($collection, $node, $path, $file);
+        }
+
+        return $collection;
+    }
+
+    /**
+     * Parses a node from a loaded XML file.
+     *
+     * @param RouteCollection $collection the collection to associate with the node
+     * @param DOMElement      $node       the node to parse
+     * @param string          $path       the path of the XML file being processed
+     * @param string          $file
+     */
+    protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file)
+    {
+        switch ($node->tagName) {
+            case 'route':
+                $this->parseRoute($collection, $node, $path);
+                break;
+            case 'import':
+                $resource = (string) $node->getAttribute('resource');
+                $type = (string) $node->getAttribute('type');
+                $prefix = (string) $node->getAttribute('prefix');
+
+                $defaults = array();
+                $requirements = array();
+                $options = array();
+
+                foreach ($node->childNodes as $n) {
+                    if (!$n instanceof \DOMElement) {
+                        continue;
+                    }
+
+                    switch ($n->tagName) {
+                        case 'default':
+                            $defaults[(string) $n->getAttribute('key')] = trim((string) $n->nodeValue);
+                            break;
+                        case 'requirement':
+                            $requirements[(string) $n->getAttribute('key')] = trim((string) $n->nodeValue);
+                            break;
+                        case 'option':
+                            $options[(string) $n->getAttribute('key')] = trim((string) $n->nodeValue);
+                            break;
+                        default:
+                            throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $n->tagName));
+                    }
+                }
+
+                $this->setCurrentDir(dirname($path));
+                $collection->addCollection($this->import($resource, ('' !== $type ? $type : null), false, $file), $prefix, $defaults, $requirements, $options);
+                break;
+            default:
+                throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'xml' === $type);
+    }
+
+    /**
+     * Parses a route and adds it to the RouteCollection.
+     *
+     * @param RouteCollection $collection A RouteCollection instance
+     * @param \DOMElement     $definition Route definition
+     * @param string          $file       An XML file path
+     *
+     * @throws \InvalidArgumentException When the definition cannot be parsed
+     */
+    protected function parseRoute(RouteCollection $collection, \DOMElement $definition, $file)
+    {
+        $defaults = array();
+        $requirements = array();
+        $options = array();
+
+        foreach ($definition->childNodes as $node) {
+            if (!$node instanceof \DOMElement) {
+                continue;
+            }
+
+            switch ($node->tagName) {
+                case 'default':
+                    $defaults[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue);
+                    break;
+                case 'option':
+                    $options[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue);
+                    break;
+                case 'requirement':
+                    $requirements[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue);
+                    break;
+                default:
+                    throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
+            }
+        }
+
+        $route = new Route((string) $definition->getAttribute('pattern'), $defaults, $requirements, $options);
+
+        $collection->add((string) $definition->getAttribute('id'), $route);
+    }
+
+    /**
+     * Loads an XML file.
+     *
+     * @param string $file An XML file path
+     *
+     * @return \DOMDocument
+     *
+     * @throws \InvalidArgumentException When loading of XML file returns error
+     */
+    protected function loadFile($file)
+    {
+        $internalErrors = libxml_use_internal_errors(true);
+        $disableEntities = libxml_disable_entity_loader(true);
+        libxml_clear_errors();
+
+        $dom = new \DOMDocument();
+        $dom->validateOnParse = true;
+        if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
+            libxml_disable_entity_loader($disableEntities);
+
+            throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors($internalErrors)));
+        }
+        $dom->normalizeDocument();
+
+        libxml_use_internal_errors($internalErrors);
+        libxml_disable_entity_loader($disableEntities);
+
+        foreach ($dom->childNodes as $child) {
+            if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+                throw new \InvalidArgumentException('Document types are not allowed.');
+            }
+        }
+
+        $this->validate($dom);
+
+        return $dom;
+    }
+
+    /**
+     * Validates a loaded XML file.
+     *
+     * @param \DOMDocument $dom A loaded XML file
+     *
+     * @throws \InvalidArgumentException When XML doesn't validate its XSD schema
+     */
+    protected function validate(\DOMDocument $dom)
+    {
+        $location = __DIR__.'/schema/routing/routing-1.0.xsd';
+
+        $current = libxml_use_internal_errors(true);
+        libxml_clear_errors();
+
+        if (!$dom->schemaValidate($location)) {
+            throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors($current)));
+        }
+        libxml_use_internal_errors($current);
+    }
+
+    /**
+     * Retrieves libxml errors and clears them.
+     *
+     * @return array An array of libxml error strings
+     */
+    private function getXmlErrors($internalErrors)
+    {
+        $errors = array();
+        foreach (libxml_get_errors() as $error) {
+            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
+                LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
+                $error->code,
+                trim($error->message),
+                $error->file ? $error->file : 'n/a',
+                $error->line,
+                $error->column
+            );
+        }
+
+        libxml_clear_errors();
+        libxml_use_internal_errors($internalErrors);
+
+        return $errors;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Loader/YamlFileLoader.php b/core/includes/Symfony/Component/Routing/Loader/YamlFileLoader.php
new file mode 100644
index 0000000..d51e339
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Loader/YamlFileLoader.php
@@ -0,0 +1,141 @@
+<?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\Routing\Loader;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Yaml\Yaml;
+use Symfony\Component\Config\Loader\FileLoader;
+
+/**
+ * YamlFileLoader loads Yaml routing files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class YamlFileLoader extends FileLoader
+{
+    private static $availableKeys = array(
+        'type', 'resource', 'prefix', 'pattern', 'options', 'defaults', 'requirements'
+    );
+
+    /**
+     * Loads a Yaml file.
+     *
+     * @param string $file A Yaml file path
+     * @param string $type The resource type
+     *
+     * @return RouteCollection A RouteCollection instance
+     *
+     * @throws \InvalidArgumentException When route can't be parsed
+     *
+     * @api
+     */
+    public function load($file, $type = null)
+    {
+        $path = $this->locator->locate($file);
+
+        $config = Yaml::parse($path);
+
+        $collection = new RouteCollection();
+        $collection->addResource(new FileResource($path));
+
+        // empty file
+        if (null === $config) {
+            $config = array();
+        }
+
+        // not an array
+        if (!is_array($config)) {
+            throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $file));
+        }
+
+        foreach ($config as $name => $config) {
+            $config = $this->normalizeRouteConfig($config);
+
+            if (isset($config['resource'])) {
+                $type = isset($config['type']) ? $config['type'] : null;
+                $prefix = isset($config['prefix']) ? $config['prefix'] : null;
+                $defaults = isset($config['defaults']) ? $config['defaults'] : array();
+                $requirements = isset($config['requirements']) ? $config['requirements'] : array();
+                $options = isset($config['options']) ? $config['options'] : array();
+
+                $this->setCurrentDir(dirname($path));
+                $collection->addCollection($this->import($config['resource'], $type, false, $file), $prefix, $defaults, $requirements, $options);
+            } else {
+                $this->parseRoute($collection, $name, $config, $path);
+            }
+        }
+
+        return $collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function supports($resource, $type = null)
+    {
+        return is_string($resource) && 'yml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'yaml' === $type);
+    }
+
+    /**
+     * Parses a route and adds it to the RouteCollection.
+     *
+     * @param RouteCollection $collection A RouteCollection instance
+     * @param string          $name       Route name
+     * @param array           $config     Route definition
+     * @param string          $file       A Yaml file path
+     *
+     * @throws \InvalidArgumentException When config pattern is not defined for the given route
+     */
+    protected function parseRoute(RouteCollection $collection, $name, $config, $file)
+    {
+        $defaults = isset($config['defaults']) ? $config['defaults'] : array();
+        $requirements = isset($config['requirements']) ? $config['requirements'] : array();
+        $options = isset($config['options']) ? $config['options'] : array();
+
+        if (!isset($config['pattern'])) {
+            throw new \InvalidArgumentException(sprintf('You must define a "pattern" for the "%s" route.', $name));
+        }
+
+        $route = new Route($config['pattern'], $defaults, $requirements, $options);
+
+        $collection->add($name, $route);
+    }
+
+    /**
+     * Normalize route configuration.
+     *
+     * @param array $config A resource config
+     *
+     * @return array
+     *
+     * @throws InvalidArgumentException if one of the provided config keys is not supported
+     */
+    private function normalizeRouteConfig(array $config)
+    {
+        foreach ($config as $key => $value) {
+            if (!in_array($key, self::$availableKeys)) {
+                throw new \InvalidArgumentException(sprintf(
+                    'Yaml routing loader does not support given key: "%s". Expected one of the (%s).',
+                    $key, implode(', ', self::$availableKeys)
+                ));
+            }
+        }
+
+        return $config;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd b/core/includes/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
new file mode 100644
index 0000000..b8571e6
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xsd:schema xmlns="http://symfony.com/schema/routing"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    targetNamespace="http://symfony.com/schema/routing"
+    elementFormDefault="qualified">
+
+  <xsd:element name="routes" type="routes" />
+
+  <xsd:complexType name="routes">
+    <xsd:choice maxOccurs="unbounded" minOccurs="0">
+      <xsd:element name="import" type="import" />
+      <xsd:element name="route" type="route" />
+    </xsd:choice>
+  </xsd:complexType>
+
+  <xsd:complexType name="route">
+    <xsd:sequence>
+      <xsd:element name="default" type="element" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="requirement" type="element" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="option" type="element" minOccurs="0" maxOccurs="unbounded" />
+    </xsd:sequence>
+
+    <xsd:attribute name="id" type="xsd:string" />
+    <xsd:attribute name="pattern" type="xsd:string" />
+  </xsd:complexType>
+
+  <xsd:complexType name="import">
+    <xsd:sequence>
+      <xsd:element name="default" type="element" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="requirement" type="element" minOccurs="0" maxOccurs="unbounded" />
+      <xsd:element name="option" type="element" minOccurs="0" maxOccurs="unbounded" />
+    </xsd:sequence>
+
+    <xsd:attribute name="resource" type="xsd:string" />
+    <xsd:attribute name="type" type="xsd:string" />
+    <xsd:attribute name="prefix" type="xsd:string" />
+    <xsd:attribute name="class" type="xsd:string" />
+  </xsd:complexType>
+
+  <xsd:complexType name="element" mixed="true">
+    <xsd:attribute name="key" type="xsd:string" />
+  </xsd:complexType>
+</xsd:schema>
diff --git a/core/includes/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php b/core/includes/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php
new file mode 100644
index 0000000..3003dfd
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.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\Routing\Matcher;
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+/**
+ * ApacheUrlMatcher matches URL based on Apache mod_rewrite matching (see ApacheMatcherDumper).
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ApacheUrlMatcher extends UrlMatcher
+{
+    /**
+     * Tries to match a URL based on Apache mod_rewrite matching.
+     *
+     * Returns false if no route matches the URL.
+     *
+     * @param string $pathinfo The pathinfo to be parsed
+     *
+     * @return array An array of parameters
+     *
+     * @throws MethodNotAllowedException If the current method is not allowed
+     */
+    public function match($pathinfo)
+    {
+        $parameters = array();
+        $defaults = array();
+        $allow = array();
+        $match = false;
+
+        foreach ($_SERVER as $key => $value) {
+            $name = $key;
+
+            if (0 === strpos($name, 'REDIRECT_')) {
+                $name = substr($name, 9);
+            }
+
+            if (0 === strpos($name, '_ROUTING_DEFAULTS_')) {
+                $name = substr($name, 18);
+                $defaults[$name] = $value;
+            } elseif (0 === strpos($name, '_ROUTING_')) {
+                $name = substr($name, 9);
+                if ('_route' == $name) {
+                    $match = true;
+                    $parameters[$name] = $value;
+                } elseif (0 === strpos($name, '_allow_')) {
+                    $allow[] = substr($name, 7);
+                } else {
+                    $parameters[$name] = $value;
+                }
+            } else {
+                continue;
+            }
+
+            unset($_SERVER[$key]);
+        }
+
+        if ($match) {
+            return $this->mergeDefaults($parameters, $defaults);
+        } elseif (0 < count($allow)) {
+            throw new MethodNotAllowedException($allow);
+        } else {
+            return parent::match($pathinfo);
+        }
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php b/core/includes/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php
new file mode 100644
index 0000000..102488c
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php
@@ -0,0 +1,164 @@
+<?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\Routing\Matcher\Dumper;
+
+/**
+ * Dumps a set of Apache mod_rewrite rules.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Kris Wallsmith <kris@symfony.com>
+ */
+class ApacheMatcherDumper extends MatcherDumper
+{
+    /**
+     * Dumps a set of Apache mod_rewrite rules.
+     *
+     * Available options:
+     *
+     *  * script_name: The script name (app.php by default)
+     *  * base_uri:    The base URI ("" by default)
+     *
+     * @param array $options An array of options
+     *
+     * @return string A string to be used as Apache rewrite rules
+     *
+     * @throws \LogicException When the route regex is invalid
+     */
+    public function dump(array $options = array())
+    {
+        $options = array_merge(array(
+            'script_name' => 'app.php',
+            'base_uri'    => '',
+        ), $options);
+
+        $options['script_name'] = self::escape($options['script_name'], ' ', '\\');
+
+        $rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
+        $methodVars = array();
+
+        foreach ($this->getRoutes()->all() as $name => $route) {
+            $compiledRoute = $route->compile();
+
+            // prepare the apache regex
+            $regex = $compiledRoute->getRegex();
+            $delimiter = $regex[0];
+            $regexPatternEnd = strrpos($regex, $delimiter);
+            if (strlen($regex) < 2 || 0 === $regexPatternEnd) {
+                throw new \LogicException('The "%s" route regex "%s" is invalid', $name, $regex);
+            }
+            $regex = preg_replace('/\?<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1));
+            $regex = '^'.self::escape(preg_quote($options['base_uri']).substr($regex, 1), ' ', '\\');
+
+            $methods = array();
+            if ($req = $route->getRequirement('_method')) {
+                $methods = explode('|', strtoupper($req));
+                // GET and HEAD are equivalent
+                if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
+                    $methods[] = 'HEAD';
+                }
+            }
+
+            $hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
+
+            $variables = array('E=_ROUTING__route:'.$name);
+            foreach ($compiledRoute->getVariables() as $i => $variable) {
+                $variables[] = 'E=_ROUTING_'.$variable.':%'.($i + 1);
+            }
+            foreach ($route->getDefaults() as $key => $value) {
+                $variables[] = 'E=_ROUTING_DEFAULTS_'.$key.':'.strtr($value, array(
+                    ':'  => '\\:',
+                    '='  => '\\=',
+                    '\\' => '\\\\',
+                    ' '  => '\\ ',
+                ));
+            }
+            $variables = implode(',', $variables);
+
+            $rule = array("# $name");
+
+            // method mismatch
+            if ($req = $route->getRequirement('_method')) {
+                $methods = explode('|', strtoupper($req));
+                // GET and HEAD are equivalent
+                if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
+                    $methods[] = 'HEAD';
+                }
+                $allow = array();
+                foreach ($methods as $method) {
+                    $methodVars[] = $method;
+                    $allow[] = 'E=_ROUTING__allow_'.$method.':1';
+                }
+
+                $rule[] = "RewriteCond %{REQUEST_URI} $regex";
+                $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods));
+                $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
+            }
+
+            // redirect with trailing slash appended
+            if ($hasTrailingSlash) {
+                $rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
+                $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
+            }
+
+            // the main rule
+            $rule[] = "RewriteCond %{REQUEST_URI} $regex";
+            $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
+
+            $rules[] = implode("\n", $rule);
+        }
+
+        if (0 < count($methodVars)) {
+            $rule = array('# 405 Method Not Allowed');
+            $methodVars = array_values(array_unique($methodVars));
+            foreach ($methodVars as $i => $methodVar) {
+                $rule[] = sprintf('RewriteCond %%{_ROUTING__allow_%s} !-z%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
+            }
+            $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
+
+            $rules[] = implode("\n", $rule);
+        }
+
+        return implode("\n\n", $rules)."\n";
+    }
+
+    /**
+     * Escapes a string.
+     *
+     * @param string $string The string to be escaped
+     * @param string $char   The character to be escaped
+     * @param string $with   The character to be used for escaping
+     *
+     * @return string The escaped string
+     */
+    private static function escape($string, $char, $with)
+    {
+        $escaped = false;
+        $output = '';
+        foreach (str_split($string) as $symbol) {
+            if ($escaped) {
+                $output .= $symbol;
+                $escaped = false;
+                continue;
+            }
+            if ($symbol === $char) {
+                $output .= $with.$char;
+                continue;
+            }
+            if ($symbol === $with) {
+                $escaped = true;
+            }
+            $output .= $symbol;
+        }
+
+        return $output;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php b/core/includes/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php
new file mode 100644
index 0000000..30778cc
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php
@@ -0,0 +1,42 @@
+<?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\Routing\Matcher\Dumper;
+
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * MatcherDumper is the abstract class for all built-in matcher dumpers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+abstract class MatcherDumper implements MatcherDumperInterface
+{
+    private $routes;
+
+    /**
+     * Constructor.
+     *
+     * @param RouteCollection $routes The RouteCollection to dump
+     */
+    public function __construct(RouteCollection $routes)
+    {
+        $this->routes = $routes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRoutes()
+    {
+        return $this->routes;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php b/core/includes/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php
new file mode 100644
index 0000000..f85e4ce
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php
@@ -0,0 +1,37 @@
+<?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\Routing\Matcher\Dumper;
+
+/**
+ * MatcherDumperInterface is the interface that all matcher dumper classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface MatcherDumperInterface
+{
+    /**
+     * Dumps a set of routes to a string representation of executable code
+     * that can then be used to match a request against these routes.
+     *
+     * @param array $options An array of options
+     *
+     * @return string Executable code
+     */
+    public function dump(array $options = array());
+
+    /**
+     * Gets the routes to dump.
+     *
+     * @return RouteCollection A RouteCollection instance
+     */
+    public function getRoutes();
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/core/includes/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
new file mode 100644
index 0000000..15fab9e
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
@@ -0,0 +1,293 @@
+<?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\Routing\Matcher\Dumper;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ */
+class PhpMatcherDumper extends MatcherDumper
+{
+    /**
+     * Dumps a set of routes to a PHP class.
+     *
+     * Available options:
+     *
+     *  * class:      The class name
+     *  * base_class: The base class name
+     *
+     * @param array $options An array of options
+     *
+     * @return string A PHP class representing the matcher class
+     */
+    public function dump(array $options = array())
+    {
+        $options = array_merge(array(
+            'class'      => 'ProjectUrlMatcher',
+            'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
+        ), $options);
+
+        // trailing slash support is only enabled if we know how to redirect the user
+        $interfaces = class_implements($options['base_class']);
+        $supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']);
+
+        return <<<EOF
+<?php
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * {$options['class']}
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class {$options['class']} extends {$options['base_class']}
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext \$context)
+    {
+        \$this->context = \$context;
+    }
+
+{$this->generateMatchMethod($supportsRedirections)}
+}
+
+EOF;
+    }
+
+    /**
+     * Generates the code for the match method implementing UrlMatcherInterface.
+     *
+     * @param Boolean $supportsRedirections Whether redirections are supported by the base class
+     *
+     * @return string Match method as PHP code
+     */
+    private function generateMatchMethod($supportsRedirections)
+    {
+        $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
+
+        return <<<EOF
+    public function match(\$pathinfo)
+    {
+        \$allow = array();
+        \$pathinfo = rawurldecode(\$pathinfo);
+
+$code
+
+        throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
+    }
+EOF;
+    }
+
+    /**
+     * Counts the number of routes as direct child of the RouteCollection.
+     *
+     * @param RouteCollection $routes A RouteCollection instance
+     *
+     * @return integer Number of Routes
+     */
+    private function countDirectChildRoutes(RouteCollection $routes)
+    {
+        $count = 0;
+        foreach ($routes as $route) {
+            if ($route instanceof Route) {
+                $count++;
+            }
+        }
+
+        return $count;
+    }
+
+    /**
+     * Generates PHP code recursively to match a RouteCollection with all child routes and child collections.
+     *
+     * @param RouteCollection $routes               A RouteCollection instance
+     * @param Boolean         $supportsRedirections Whether redirections are supported by the base class
+     * @param string|null     $parentPrefix         The prefix of the parent collection used to optimize the code
+     *
+     * @return string PHP code
+     */
+    private function compileRoutes(RouteCollection $routes, $supportsRedirections, $parentPrefix = null)
+    {
+        $code = '';
+
+        $prefix = $routes->getPrefix();
+        $countDirectChildRoutes = $this->countDirectChildRoutes($routes);
+        $countAllChildRoutes = count($routes->all());
+        // Can the matching be optimized by wrapping it with the prefix condition
+        // - no need to optimize if current prefix is the same as the parent prefix
+        // - if $countDirectChildRoutes === 0, the sub-collections can do their own optimizations (in case there are any)
+        // - it's not worth wrapping a single child route
+        // - prefixes with variables cannot be optimized because routes within the collection might have different requirements for the same variable
+        $optimizable = '' !== $prefix && $prefix !== $parentPrefix && $countDirectChildRoutes > 0 && $countAllChildRoutes > 1 && false === strpos($prefix, '{');
+        if ($optimizable) {
+            $code .= sprintf("    if (0 === strpos(\$pathinfo, %s)) {\n", var_export($prefix, true));
+        }
+
+        foreach ($routes as $name => $route) {
+            if ($route instanceof Route) {
+                // a single route in a sub-collection is not wrapped so it should do its own optimization in ->compileRoute with $parentPrefix = null
+                $code .= $this->compileRoute($route, $name, $supportsRedirections, 1 === $countAllChildRoutes ? null : $prefix)."\n";
+            } elseif ($countAllChildRoutes - $countDirectChildRoutes > 0) { // we can stop iterating recursively if we already know there are no more routes
+                $code .= $this->compileRoutes($route, $supportsRedirections, $prefix);
+            }
+        }
+
+        if ($optimizable) {
+            $code .= "    }\n\n";
+            // apply extra indention at each line (except empty ones)
+            $code = preg_replace('/^.{2,}$/m', '    $0', $code);
+        }
+
+        return $code;
+    }
+
+
+    /**
+     * Compiles a single Route to PHP code used to match it against the path info.
+     *
+     * @param Route       $route                A Route instance
+     * @param string      $name                 The name of the Route
+     * @param Boolean     $supportsRedirections Whether redirections are supported by the base class
+     * @param string|null $parentPrefix         The prefix of the parent collection used to optimize the code
+     *
+     * @return string PHP code
+     */
+    private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
+    {
+        $code = '';
+        $compiledRoute = $route->compile();
+        $conditions = array();
+        $hasTrailingSlash = false;
+        $matches = false;
+        $methods = array();
+
+        if ($req = $route->getRequirement('_method')) {
+            $methods = explode('|', strtoupper($req));
+            // GET and HEAD are equivalent
+            if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
+                $methods[] = 'HEAD';
+            }
+        }
+
+        $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
+
+        if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
+            if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
+                $conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
+                $hasTrailingSlash = true;
+            } else {
+                $conditions[] = sprintf("\$pathinfo === %s", var_export(str_replace('\\', '', $m['url']), true));
+            }
+        } else {
+            if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
+                $conditions[] = sprintf("0 === strpos(\$pathinfo, %s)", var_export($compiledRoute->getStaticPrefix(), true));
+            }
+
+            $regex = $compiledRoute->getRegex();
+            if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
+                $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
+                $hasTrailingSlash = true;
+            }
+            $conditions[] = sprintf("preg_match(%s, \$pathinfo, \$matches)", var_export($regex, true));
+
+            $matches = true;
+        }
+
+        $conditions = implode(' && ', $conditions);
+
+        $code .= <<<EOF
+        // $name
+        if ($conditions) {
+
+EOF;
+
+        if ($methods) {
+            $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
+
+            if (1 === count($methods)) {
+                $code .= <<<EOF
+            if (\$this->context->getMethod() != '$methods[0]') {
+                \$allow[] = '$methods[0]';
+                goto $gotoname;
+            }
+
+
+EOF;
+            } else {
+                $methods = implode("', '", $methods);
+                $code .= <<<EOF
+            if (!in_array(\$this->context->getMethod(), array('$methods'))) {
+                \$allow = array_merge(\$allow, array('$methods'));
+                goto $gotoname;
+            }
+
+
+EOF;
+            }
+        }
+
+        if ($hasTrailingSlash) {
+            $code .= <<<EOF
+            if (substr(\$pathinfo, -1) !== '/') {
+                return \$this->redirect(\$pathinfo.'/', '$name');
+            }
+
+
+EOF;
+        }
+
+        if ($scheme = $route->getRequirement('_scheme')) {
+            if (!$supportsRedirections) {
+                throw new \LogicException('The "_scheme" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
+            }
+
+            $code .= <<<EOF
+            if (\$this->context->getScheme() !== '$scheme') {
+                return \$this->redirect(\$pathinfo, '$name', '$scheme');
+            }
+
+
+EOF;
+        }
+
+        // optimize parameters array
+        if (true === $matches && $route->getDefaults()) {
+            $code .= sprintf("            return array_merge(\$this->mergeDefaults(\$matches, %s), array('_route' => '%s'));\n"
+                , str_replace("\n", '', var_export($route->getDefaults(), true)), $name);
+        } elseif (true === $matches) {
+            $code .= sprintf("            \$matches['_route'] = '%s';\n\n", $name);
+            $code .= "            return \$matches;\n";
+        } elseif ($route->getDefaults()) {
+            $code .= sprintf("            return %s;\n", str_replace("\n", '', var_export(array_merge($route->getDefaults(), array('_route' => $name)), true)));
+        } else {
+            $code .= sprintf("            return array('_route' => '%s');\n", $name);
+        }
+        $code .= "        }\n";
+
+        if ($methods) {
+            $code .= "        $gotoname:\n";
+        }
+
+        return $code;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php b/core/includes/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php
new file mode 100644
index 0000000..51e8005
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php
@@ -0,0 +1,61 @@
+<?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\Routing\Matcher;
+
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Route;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+abstract class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function match($pathinfo)
+    {
+        try {
+            $parameters = parent::match($pathinfo);
+        } catch (ResourceNotFoundException $e) {
+            if ('/' === substr($pathinfo, -1) || !in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
+                throw $e;
+            }
+
+            try {
+                parent::match($pathinfo.'/');
+
+                return $this->redirect($pathinfo.'/', null);
+            } catch (ResourceNotFoundException $e2) {
+                throw $e;
+            }
+        }
+
+        return $parameters;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected function handleRouteRequirements($pathinfo, $name, Route $route)
+    {
+        // check HTTP scheme requirement
+        $scheme = $route->getRequirement('_scheme');
+        if ($scheme && $this->context->getScheme() !== $scheme) {
+            return array(self::ROUTE_MATCH, $this->redirect($pathinfo, $name, $scheme));
+        }
+
+        return array(self::REQUIREMENT_MATCH, null);
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php b/core/includes/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php
new file mode 100644
index 0000000..929ae9c
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php
@@ -0,0 +1,35 @@
+<?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\Routing\Matcher;
+
+/**
+ * RedirectableUrlMatcherInterface knows how to redirect the user.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface RedirectableUrlMatcherInterface
+{
+    /**
+     * Redirects the user to another URL.
+     *
+     * @param string $path   The path info to redirect to.
+     * @param string $route  The route that matched
+     * @param string $scheme The URL scheme (null to keep the current one)
+     *
+     * @return array An array of parameters
+     *
+     * @api
+     */
+    public function redirect($path, $route, $scheme = null);
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/RequestMatcherInterface.php b/core/includes/Symfony/Component/Routing/Matcher/RequestMatcherInterface.php
new file mode 100644
index 0000000..b5def3d
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/RequestMatcherInterface.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\Routing\Matcher;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+/**
+ * RequestMatcherInterface is the interface that all request matcher classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RequestMatcherInterface
+{
+    /**
+     * Tries to match a request with a set of routes.
+     *
+     * If the matcher can not find information, it must throw one of the exceptions documented
+     * below.
+     *
+     * @param Request $request The request to match
+     *
+     * @return array An array of parameters
+     *
+     * @throws ResourceNotFoundException If no matching resource could be found
+     * @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed
+     */
+    public function matchRequest(Request $request);
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php b/core/includes/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php
new file mode 100644
index 0000000..1e2491c
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php
@@ -0,0 +1,121 @@
+<?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\Routing\Matcher;
+
+use Symfony\Component\Routing\Exception\ExceptionInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+
+/**
+ * TraceableUrlMatcher helps debug path info matching by tracing the match.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class TraceableUrlMatcher extends UrlMatcher
+{
+    const ROUTE_DOES_NOT_MATCH = 0;
+    const ROUTE_ALMOST_MATCHES = 1;
+    const ROUTE_MATCHES        = 2;
+
+    protected $traces;
+
+    public function getTraces($pathinfo)
+    {
+        $this->traces = array();
+
+        try {
+            $this->match($pathinfo);
+        } catch (ExceptionInterface $e) {
+        }
+
+        return $this->traces;
+    }
+
+    protected function matchCollection($pathinfo, RouteCollection $routes)
+    {
+        foreach ($routes as $name => $route) {
+            if ($route instanceof RouteCollection) {
+                if (!$ret = $this->matchCollection($pathinfo, $route)) {
+                    continue;
+                }
+
+                return true;
+            }
+
+            $compiledRoute = $route->compile();
+
+            if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
+                // does it match without any requirements?
+                $r = new Route($route->getPattern(), $route->getDefaults(), array(), $route->getOptions());
+                $cr = $r->compile();
+                if (!preg_match($cr->getRegex(), $pathinfo)) {
+                    $this->addTrace(sprintf('Pattern "%s" does not match', $route->getPattern()), self::ROUTE_DOES_NOT_MATCH, $name, $route);
+
+                    continue;
+                }
+
+                foreach ($route->getRequirements() as $n => $regex) {
+                    $r = new Route($route->getPattern(), $route->getDefaults(), array($n => $regex), $route->getOptions());
+                    $cr = $r->compile();
+
+                    if (in_array($n, $cr->getVariables()) && !preg_match($cr->getRegex(), $pathinfo)) {
+                        $this->addTrace(sprintf('Requirement for "%s" does not match (%s)', $n, $regex), self::ROUTE_ALMOST_MATCHES, $name, $route);
+
+                        continue 2;
+                    }
+                }
+
+                continue;
+            }
+
+            // check HTTP method requirement
+            if ($req = $route->getRequirement('_method')) {
+                // HEAD and GET are equivalent as per RFC
+                if ('HEAD' === $method = $this->context->getMethod()) {
+                    $method = 'GET';
+                }
+
+                if (!in_array($method, $req = explode('|', strtoupper($req)))) {
+                    $this->allow = array_merge($this->allow, $req);
+
+                    $this->addTrace(sprintf('Method "%s" does not match the requirement ("%s")', $this->context->getMethod(), implode(', ', $req)), self::ROUTE_ALMOST_MATCHES, $name, $route);
+
+                    continue;
+                }
+            }
+
+            // check HTTP scheme requirement
+            if ($scheme = $route->getRequirement('_scheme')) {
+                if ($this->context->getScheme() !== $scheme) {
+                    $this->addTrace(sprintf('Scheme "%s" does not match the requirement ("%s"); the user will be redirected', $this->context->getScheme(), $scheme), self::ROUTE_ALMOST_MATCHES, $name, $route);
+
+                    return true;
+                }
+            }
+
+            $this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route);
+
+            return true;
+        }
+    }
+
+    private function addTrace($log, $level = self::ROUTE_DOES_NOT_MATCH, $name = null, $route = null)
+    {
+        $this->traces[] = array(
+            'log'     => $log,
+            'name'    => $name,
+            'level'   => $level,
+            'pattern' => null !== $route ? $route->getPattern() : null,
+        );
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/UrlMatcher.php b/core/includes/Symfony/Component/Routing/Matcher/UrlMatcher.php
new file mode 100644
index 0000000..c417d94
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/UrlMatcher.php
@@ -0,0 +1,186 @@
+<?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\Routing\Matcher;
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Route;
+
+/**
+ * UrlMatcher matches URL based on a set of routes.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class UrlMatcher implements UrlMatcherInterface
+{
+    const REQUIREMENT_MATCH     = 0;
+    const REQUIREMENT_MISMATCH  = 1;
+    const ROUTE_MATCH           = 2;
+
+    protected $context;
+    protected $allow;
+
+    private $routes;
+
+    /**
+     * Constructor.
+     *
+     * @param RouteCollection $routes  A RouteCollection instance
+     * @param RequestContext  $context The context
+     *
+     * @api
+     */
+    public function __construct(RouteCollection $routes, RequestContext $context)
+    {
+        $this->routes = $routes;
+        $this->context = $context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setContext(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function match($pathinfo)
+    {
+        $this->allow = array();
+
+        if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) {
+            return $ret;
+        }
+
+        throw 0 < count($this->allow)
+            ? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow)))
+            : new ResourceNotFoundException();
+    }
+
+    /**
+     * Tries to match a URL with a set of routes.
+     *
+     * @param string          $pathinfo The path info to be parsed
+     * @param RouteCollection $routes   The set of routes
+     *
+     * @return array An array of parameters
+     *
+     * @throws ResourceNotFoundException If the resource could not be found
+     * @throws MethodNotAllowedException If the resource was found but the request method is not allowed
+     */
+    protected function matchCollection($pathinfo, RouteCollection $routes)
+    {
+        foreach ($routes as $name => $route) {
+            if ($route instanceof RouteCollection) {
+                if (false === strpos($route->getPrefix(), '{') && $route->getPrefix() !== substr($pathinfo, 0, strlen($route->getPrefix()))) {
+                    continue;
+                }
+
+                if (!$ret = $this->matchCollection($pathinfo, $route)) {
+                    continue;
+                }
+
+                return $ret;
+            }
+
+            $compiledRoute = $route->compile();
+
+            // check the static prefix of the URL first. Only use the more expensive preg_match when it matches
+            if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) {
+                continue;
+            }
+
+            if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) {
+                continue;
+            }
+
+            // check HTTP method requirement
+            if ($req = $route->getRequirement('_method')) {
+                // HEAD and GET are equivalent as per RFC
+                if ('HEAD' === $method = $this->context->getMethod()) {
+                    $method = 'GET';
+                }
+
+                if (!in_array($method, $req = explode('|', strtoupper($req)))) {
+                    $this->allow = array_merge($this->allow, $req);
+
+                    continue;
+                }
+            }
+
+            $status = $this->handleRouteRequirements($pathinfo, $name, $route);
+
+            if (self::ROUTE_MATCH === $status[0]) {
+                return $status[1];
+            }
+
+            if (self::REQUIREMENT_MISMATCH === $status[0]) {
+                continue;
+            }
+
+            return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name));
+        }
+    }
+
+    /**
+     * Handles specific route requirements.
+     *
+     * @param string $pathinfo The path
+     * @param string $name     The route name
+     * @param Route  $route    The route
+     *
+     * @return array The first element represents the status, the second contains additional information
+     */
+    protected function handleRouteRequirements($pathinfo, $name, Route $route)
+    {
+        // check HTTP scheme requirement
+        $scheme = $route->getRequirement('_scheme');
+        $status = $scheme && $scheme !== $this->context->getScheme() ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH;
+
+        return array($status, null);
+    }
+
+    /**
+     * Get merged default parameters.
+     *
+     * @param array $params   The parameters
+     * @param array $defaults The defaults
+     *
+     * @return array Merged default parameters
+     */
+    protected function mergeDefaults($params, $defaults)
+    {
+        $parameters = $defaults;
+        foreach ($params as $key => $value) {
+            if (!is_int($key)) {
+                $parameters[$key] = $value;
+            }
+        }
+
+        return $parameters;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php b/core/includes/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php
new file mode 100644
index 0000000..dd718b1
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php
@@ -0,0 +1,43 @@
+<?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\Routing\Matcher;
+
+use Symfony\Component\Routing\RequestContextAwareInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+/**
+ * UrlMatcherInterface is the interface that all URL matcher classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface UrlMatcherInterface extends RequestContextAwareInterface
+{
+    /**
+     * Tries to match a URL path with a set of routes.
+     *
+     * If the matcher can not find information, it must throw one of the exceptions documented
+     * below.
+     *
+     * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded)
+     *
+     * @return array An array of parameters
+     *
+     * @throws ResourceNotFoundException If the resource could not be found
+     * @throws MethodNotAllowedException If the resource was found but the request method is not allowed
+     *
+     * @api
+     */
+    public function match($pathinfo);
+}
diff --git a/core/includes/Symfony/Component/Routing/README.md b/core/includes/Symfony/Component/Routing/README.md
new file mode 100644
index 0000000..61504c8
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/README.md
@@ -0,0 +1,37 @@
+Routing Component
+=================
+
+Routing associates a request with the code that will convert it to a response.
+
+The example below demonstrates how you can set up a fully working routing
+system:
+
+    use Symfony\Component\HttpFoundation\Request;
+    use Symfony\Component\Routing\Matcher\UrlMatcher;
+    use Symfony\Component\Routing\RequestContext;
+    use Symfony\Component\Routing\RouteCollection;
+    use Symfony\Component\Routing\Route;
+
+    $routes = new RouteCollection();
+    $routes->add('hello', new Route('/hello', array('controller' => 'foo')));
+
+    $context = new RequestContext();
+
+    // this is optional and can be done without a Request instance
+    $context->fromRequest(Request::createFromGlobals());
+
+    $matcher = new UrlMatcher($routes, $context);
+
+    $parameters = $matcher->match('/hello');
+
+Resources
+---------
+
+You can run the unit tests with the following command:
+
+    phpunit
+
+If you also want to run the unit tests that depend on other Symfony
+Components, install dev dependencies before running PHPUnit:
+
+    php composer.phar install --dev
diff --git a/core/includes/Symfony/Component/Routing/RequestContext.php b/core/includes/Symfony/Component/Routing/RequestContext.php
new file mode 100644
index 0000000..7406b20
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/RequestContext.php
@@ -0,0 +1,262 @@
+<?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\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Holds information about the current request.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class RequestContext
+{
+    private $baseUrl;
+    private $method;
+    private $host;
+    private $scheme;
+    private $httpPort;
+    private $httpsPort;
+    private $parameters;
+
+    /**
+     * Constructor.
+     *
+     * @param string  $baseUrl   The base URL
+     * @param string  $method    The HTTP method
+     * @param string  $host      The HTTP host name
+     * @param string  $scheme    The HTTP scheme
+     * @param integer $httpPort  The HTTP port
+     * @param integer $httpsPort The HTTPS port
+     *
+     * @api
+     */
+    public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443)
+    {
+        $this->baseUrl = $baseUrl;
+        $this->method = strtoupper($method);
+        $this->host = $host;
+        $this->scheme = strtolower($scheme);
+        $this->httpPort = $httpPort;
+        $this->httpsPort = $httpsPort;
+        $this->parameters = array();
+    }
+
+    public function fromRequest(Request $request)
+    {
+        $this->setBaseUrl($request->getBaseUrl());
+        $this->setMethod($request->getMethod());
+        $this->setHost($request->getHost());
+        $this->setScheme($request->getScheme());
+        $this->setHttpPort($request->isSecure() ? $this->httpPort : $request->getPort());
+        $this->setHttpsPort($request->isSecure() ? $request->getPort() : $this->httpsPort);
+    }
+
+    /**
+     * Gets the base URL.
+     *
+     * @return string The base URL
+     */
+    public function getBaseUrl()
+    {
+        return $this->baseUrl;
+    }
+
+    /**
+     * Sets the base URL.
+     *
+     * @param string $baseUrl The base URL
+     *
+     * @api
+     */
+    public function setBaseUrl($baseUrl)
+    {
+        $this->baseUrl = $baseUrl;
+    }
+
+    /**
+     * Gets the HTTP method.
+     *
+     * The method is always an uppercased string.
+     *
+     * @return string The HTTP method
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * Sets the HTTP method.
+     *
+     * @param string $method The HTTP method
+     *
+     * @api
+     */
+    public function setMethod($method)
+    {
+        $this->method = strtoupper($method);
+    }
+
+    /**
+     * Gets the HTTP host.
+     *
+     * @return string The HTTP host
+     */
+    public function getHost()
+    {
+        return $this->host;
+    }
+
+    /**
+     * Sets the HTTP host.
+     *
+     * @param string $host The HTTP host
+     *
+     * @api
+     */
+    public function setHost($host)
+    {
+        $this->host = $host;
+    }
+
+    /**
+     * Gets the HTTP scheme.
+     *
+     * @return string The HTTP scheme
+     */
+    public function getScheme()
+    {
+        return $this->scheme;
+    }
+
+    /**
+     * Sets the HTTP scheme.
+     *
+     * @param string $scheme The HTTP scheme
+     *
+     * @api
+     */
+    public function setScheme($scheme)
+    {
+        $this->scheme = strtolower($scheme);
+    }
+
+    /**
+     * Gets the HTTP port.
+     *
+     * @return string The HTTP port
+     */
+    public function getHttpPort()
+    {
+        return $this->httpPort;
+    }
+
+    /**
+     * Sets the HTTP port.
+     *
+     * @param string $httpPort The HTTP port
+     *
+     * @api
+     */
+    public function setHttpPort($httpPort)
+    {
+        $this->httpPort = $httpPort;
+    }
+
+    /**
+     * Gets the HTTPS port.
+     *
+     * @return string The HTTPS port
+     */
+    public function getHttpsPort()
+    {
+        return $this->httpsPort;
+    }
+
+    /**
+     * Sets the HTTPS port.
+     *
+     * @param string $httpsPort The HTTPS port
+     *
+     * @api
+     */
+    public function setHttpsPort($httpsPort)
+    {
+        $this->httpsPort = $httpsPort;
+    }
+
+    /**
+     * Returns the parameters.
+     *
+     * @return array The parameters
+     */
+    public function getParameters()
+    {
+        return $this->parameters;
+    }
+
+    /**
+     * Sets the parameters.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $parameters The parameters
+     *
+     * @return Route The current Route instance
+     */
+    public function setParameters(array $parameters)
+    {
+        $this->parameters = $parameters;
+
+        return $this;
+    }
+
+    /**
+     * Gets a parameter value.
+     *
+     * @param string $name A parameter name
+     *
+     * @return mixed The parameter value
+     */
+    public function getParameter($name)
+    {
+        return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
+    }
+
+    /**
+     * Checks if a parameter value is set for the given parameter.
+     *
+     * @param string $name A parameter name
+     *
+     * @return Boolean true if the parameter value is set, false otherwise
+     */
+    public function hasParameter($name)
+    {
+        return array_key_exists($name, $this->parameters);
+    }
+
+    /**
+     * Sets a parameter value.
+     *
+     * @param string $name      A parameter name
+     * @param mixed  $parameter The parameter value
+     *
+     * @api
+     */
+    public function setParameter($name, $parameter)
+    {
+        $this->parameters[$name] = $parameter;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/RequestContextAwareInterface.php b/core/includes/Symfony/Component/Routing/RequestContextAwareInterface.php
new file mode 100644
index 0000000..daf5254
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/RequestContextAwareInterface.php
@@ -0,0 +1,36 @@
+<?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\Routing;
+
+/**
+ * @api
+ */
+interface RequestContextAwareInterface
+{
+    /**
+     * Sets the request context.
+     *
+     * @param RequestContext $context The context
+     *
+     * @api
+     */
+    public function setContext(RequestContext $context);
+
+    /**
+     * Gets the request context.
+     *
+     * @return RequestContext The context
+     *
+     * @api
+     */
+    public function getContext();
+}
diff --git a/core/includes/Symfony/Component/Routing/Route.php b/core/includes/Symfony/Component/Routing/Route.php
new file mode 100644
index 0000000..fd9d90d
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Route.php
@@ -0,0 +1,393 @@
+<?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\Routing;
+
+/**
+ * A Route describes a route and its parameters.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Route implements \Serializable
+{
+    private $pattern;
+    private $defaults;
+    private $requirements;
+    private $options;
+    private $compiled;
+
+    private static $compilers = array();
+
+    /**
+     * Constructor.
+     *
+     * Available options:
+     *
+     *  * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
+     *
+     * @param string $pattern      The pattern to match
+     * @param array  $defaults     An array of default parameter values
+     * @param array  $requirements An array of requirements for parameters (regexes)
+     * @param array  $options      An array of options
+     *
+     * @api
+     */
+    public function __construct($pattern, array $defaults = array(), array $requirements = array(), array $options = array())
+    {
+        $this->setPattern($pattern);
+        $this->setDefaults($defaults);
+        $this->setRequirements($requirements);
+        $this->setOptions($options);
+    }
+
+    public function __clone()
+    {
+        $this->compiled = null;
+    }
+
+    public function serialize()
+    {
+        return serialize(array(
+            'pattern' => $this->pattern,
+            'defaults' => $this->defaults,
+            'requirements' => $this->requirements,
+            'options' => $this->options,
+        ));
+    }
+
+    public function unserialize($data)
+    {
+        $data = unserialize($data);
+        $this->pattern = $data['pattern'];
+        $this->defaults = $data['defaults'];
+        $this->requirements = $data['requirements'];
+        $this->options = $data['options'];
+    }
+
+    /**
+     * Returns the pattern.
+     *
+     * @return string The pattern
+     */
+    public function getPattern()
+    {
+        return $this->pattern;
+    }
+
+    /**
+     * Sets the pattern.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param string $pattern The pattern
+     *
+     * @return Route The current Route instance
+     */
+    public function setPattern($pattern)
+    {
+        $this->pattern = trim($pattern);
+
+        // a route must start with a slash
+        if ('' === $this->pattern || '/' !== $this->pattern[0]) {
+            $this->pattern = '/'.$this->pattern;
+        }
+
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the options.
+     *
+     * @return array The options
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Sets the options.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $options The options
+     *
+     * @return Route The current Route instance
+     */
+    public function setOptions(array $options)
+    {
+        $this->options = array(
+            'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
+        );
+
+        return $this->addOptions($options);
+    }
+
+    /**
+     * Adds options.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $options The options
+     *
+     * @return Route The current Route instance
+     */
+    public function addOptions(array $options)
+    {
+        foreach ($options as $name => $option) {
+            $this->options[(string) $name] = $option;
+        }
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Sets an option value.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param string $name  An option name
+     * @param mixed  $value The option value
+     *
+     * @return Route The current Route instance
+     *
+     * @api
+     */
+    public function setOption($name, $value)
+    {
+        $this->options[$name] = $value;
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Get an option value.
+     *
+     * @param string $name An option name
+     *
+     * @return mixed The option value or null when not given
+     */
+    public function getOption($name)
+    {
+        return isset($this->options[$name]) ? $this->options[$name] : null;
+    }
+
+    /**
+     * Returns the defaults.
+     *
+     * @return array The defaults
+     */
+    public function getDefaults()
+    {
+        return $this->defaults;
+    }
+
+    /**
+     * Sets the defaults.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $defaults The defaults
+     *
+     * @return Route The current Route instance
+     */
+    public function setDefaults(array $defaults)
+    {
+        $this->defaults = array();
+
+        return $this->addDefaults($defaults);
+    }
+
+    /**
+     * Adds defaults.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $defaults The defaults
+     *
+     * @return Route The current Route instance
+     */
+    public function addDefaults(array $defaults)
+    {
+        foreach ($defaults as $name => $default) {
+            $this->defaults[(string) $name] = $default;
+        }
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Gets a default value.
+     *
+     * @param string $name A variable name
+     *
+     * @return mixed The default value or null when not given
+     */
+    public function getDefault($name)
+    {
+        return isset($this->defaults[$name]) ? $this->defaults[$name] : null;
+    }
+
+    /**
+     * Checks if a default value is set for the given variable.
+     *
+     * @param string $name A variable name
+     *
+     * @return Boolean true if the default value is set, false otherwise
+     */
+    public function hasDefault($name)
+    {
+        return array_key_exists($name, $this->defaults);
+    }
+
+    /**
+     * Sets a default value.
+     *
+     * @param string $name    A variable name
+     * @param mixed  $default The default value
+     *
+     * @return Route The current Route instance
+     *
+     * @api
+     */
+    public function setDefault($name, $default)
+    {
+        $this->defaults[(string) $name] = $default;
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the requirements.
+     *
+     * @return array The requirements
+     */
+    public function getRequirements()
+    {
+        return $this->requirements;
+    }
+
+    /**
+     * Sets the requirements.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $requirements The requirements
+     *
+     * @return Route The current Route instance
+     */
+    public function setRequirements(array $requirements)
+    {
+        $this->requirements = array();
+
+        return $this->addRequirements($requirements);
+    }
+
+    /**
+     * Adds requirements.
+     *
+     * This method implements a fluent interface.
+     *
+     * @param array $requirements The requirements
+     *
+     * @return Route The current Route instance
+     */
+    public function addRequirements(array $requirements)
+    {
+        foreach ($requirements as $key => $regex) {
+            $this->requirements[$key] = $this->sanitizeRequirement($key, $regex);
+        }
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Returns the requirement for the given key.
+     *
+     * @param string $key The key
+     *
+     * @return string|null The regex or null when not given
+     */
+    public function getRequirement($key)
+    {
+        return isset($this->requirements[$key]) ? $this->requirements[$key] : null;
+    }
+
+    /**
+     * Sets a requirement for the given key.
+     *
+     * @param string $key   The key
+     * @param string $regex The regex
+     *
+     * @return Route The current Route instance
+     *
+     * @api
+     */
+    public function setRequirement($key, $regex)
+    {
+        $this->requirements[$key] = $this->sanitizeRequirement($key, $regex);
+        $this->compiled = null;
+
+        return $this;
+    }
+
+    /**
+     * Compiles the route.
+     *
+     * @return CompiledRoute A CompiledRoute instance
+     *
+     * @see RouteCompiler which is responsible for the compilation process
+     */
+    public function compile()
+    {
+        if (null !== $this->compiled) {
+            return $this->compiled;
+        }
+
+        $class = $this->getOption('compiler_class');
+
+        if (!isset(self::$compilers[$class])) {
+            self::$compilers[$class] = new $class;
+        }
+
+        return $this->compiled = self::$compilers[$class]->compile($this);
+    }
+
+    private function sanitizeRequirement($key, $regex)
+    {
+        if (!is_string($regex)) {
+            throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" must be a string.', $key));
+        }
+
+        if ('' !== $regex && '^' === $regex[0]) {
+            $regex = (string) substr($regex, 1); // returns false for a single character
+        }
+
+        if ('$' === substr($regex, -1)) {
+            $regex = substr($regex, 0, -1);
+        }
+
+        if ('' === $regex) {
+            throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key));
+        }
+
+        return $regex;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/RouteCollection.php b/core/includes/Symfony/Component/Routing/RouteCollection.php
new file mode 100644
index 0000000..ea0fbc7
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/RouteCollection.php
@@ -0,0 +1,346 @@
+<?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\Routing;
+
+use Symfony\Component\Config\Resource\ResourceInterface;
+
+/**
+ * A RouteCollection represents a set of Route instances as a tree structure.
+ *
+ * When adding a route, it overrides existing routes with the
+ * same name defined in the instance or its children and parents.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * @api
+ */
+class RouteCollection implements \IteratorAggregate, \Countable
+{
+    protected $routes;
+    private $resources;
+    private $prefix;
+    private $parent;
+
+    /**
+     * Constructor.
+     *
+     * @api
+     */
+    public function __construct()
+    {
+        $this->routes = array();
+        $this->resources = array();
+        $this->prefix = '';
+    }
+
+    public function __clone()
+    {
+        foreach ($this->routes as $name => $route) {
+            $this->routes[$name] = clone $route;
+            if ($route instanceof RouteCollection) {
+                $this->routes[$name]->setParent($this);
+            }
+        }
+    }
+
+    /**
+     * Gets the parent RouteCollection.
+     *
+     * @return RouteCollection|null The parent RouteCollection or null when it's the root
+     */
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    /**
+     * Gets the root RouteCollection of the tree.
+     *
+     * @return RouteCollection The root RouteCollection
+     */
+    public function getRoot()
+    {
+        $parent = $this;
+        while ($parent->getParent()) {
+            $parent = $parent->getParent();
+        }
+
+        return $parent;
+    }
+
+    /**
+     * Gets the current RouteCollection as an Iterator that includes all routes and child route collections.
+     *
+     * @return \ArrayIterator An \ArrayIterator interface
+     */
+    public function getIterator()
+    {
+        return new \ArrayIterator($this->routes);
+    }
+
+    /**
+     * Gets the number of Routes in this collection.
+     *
+     * @return int The number of routes in this collection, including nested collections
+     */
+    public function count()
+    {
+        $count = 0;
+        foreach ($this->routes as $route) {
+            $count += $route instanceof RouteCollection ? count($route) : 1;
+        }
+
+        return $count;
+    }
+
+    /**
+     * Adds a route.
+     *
+     * @param string $name  The route name
+     * @param Route  $route A Route instance
+     *
+     * @throws \InvalidArgumentException When route name contains non valid characters
+     *
+     * @api
+     */
+    public function add($name, Route $route)
+    {
+        if (!preg_match('/^[a-z0-9A-Z_.]+$/', $name)) {
+            throw new \InvalidArgumentException(sprintf('The provided route name "%s" contains non valid characters. A route name must only contain digits (0-9), letters (a-z and A-Z), underscores (_) and dots (.).', $name));
+        }
+
+        $this->remove($name);
+
+        $this->routes[$name] = $route;
+    }
+
+    /**
+     * Returns all routes in this collection and its children.
+     *
+     * @return array An array of routes
+     */
+    public function all()
+    {
+        print "\n\nSUPER::routes:     ";
+        print_r($this->routes);
+        $routes = array();
+        foreach ($this->routes as $name => $route) {
+            if ($route instanceof RouteCollection) {
+                $routes = array_merge($routes, $route->all());
+            } else {
+                $routes[$name] = $route;
+            }
+        }
+
+        return $routes;
+    }
+
+    /**
+     * Gets a route by name defined in this collection or its children.
+     *
+     * @param string $name The route name
+     *
+     * @return Route|null A Route instance or null when not found
+     */
+    public function get($name)
+    {
+        if (isset($this->routes[$name])) {
+            return $this->routes[$name] instanceof RouteCollection ? null : $this->routes[$name];
+        }
+
+        foreach ($this->routes as $routes) {
+            if ($routes instanceof RouteCollection && null !== $route = $routes->get($name)) {
+                return $route;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Removes a route or an array of routes by name from all connected
+     * collections (this instance and all parents and children).
+     *
+     * @param string|array $name The route name or an array of route names
+     */
+    public function remove($name)
+    {
+        $root = $this->getRoot();
+
+        foreach ((array) $name as $n) {
+            $root->removeRecursively($n);
+        }
+    }
+
+    /**
+     * Adds a route collection to the current set of routes (at the end of the current set).
+     *
+     * @param RouteCollection $collection   A RouteCollection instance
+     * @param string          $prefix       An optional prefix to add before each pattern of the route collection
+     * @param array           $defaults     An array of default values
+     * @param array           $requirements An array of requirements
+     * @param array           $options      An array of options
+     *
+     * @throws \InvalidArgumentException When the RouteCollection already exists in the tree
+     *
+     * @api
+     */
+    public function addCollection(RouteCollection $collection, $prefix = '', $defaults = array(), $requirements = array(), $options = array())
+    {
+        // prevent infinite loops by recursive referencing
+        $root = $this->getRoot();
+        if ($root === $collection || $root->hasCollection($collection)) {
+            throw new \InvalidArgumentException('The RouteCollection already exists in the tree.');
+        }
+
+        // remove all routes with the same names in all existing collections
+        $this->remove(array_keys($collection->all()));
+
+        $collection->setParent($this);
+        // the sub-collection must have the prefix of the parent (current instance) prepended because it does not
+        // necessarily already have it applied (depending on the order RouteCollections are added to each other)
+        $collection->addPrefix($this->getPrefix() . $prefix, $defaults, $requirements, $options);
+        $this->routes[] = $collection;
+    }
+
+    /**
+     * Adds a prefix to all routes in the current set.
+     *
+     * @param string $prefix       An optional prefix to add before each pattern of the route collection
+     * @param array  $defaults     An array of default values
+     * @param array  $requirements An array of requirements
+     * @param array  $options      An array of options
+     *
+     * @api
+     */
+    public function addPrefix($prefix, $defaults = array(), $requirements = array(), $options = array())
+    {
+        // a prefix must not end with a slash
+        $prefix = rtrim($prefix, '/');
+
+        if ('' === $prefix && empty($defaults) && empty($requirements) && empty($options)) {
+            return;
+        }
+
+        // a prefix must start with a slash
+        if ('' !== $prefix && '/' !== $prefix[0]) {
+            $prefix = '/'.$prefix;
+        }
+
+        $this->prefix = $prefix.$this->prefix;
+
+        foreach ($this->routes as $route) {
+            if ($route instanceof RouteCollection) {
+                $route->addPrefix($prefix, $defaults, $requirements, $options);
+            } else {
+                $route->setPattern($prefix.$route->getPattern());
+                $route->addDefaults($defaults);
+                $route->addRequirements($requirements);
+                $route->addOptions($options);
+            }
+        }
+    }
+
+    /**
+     * Returns the prefix that may contain placeholders.
+     *
+     * @return string The prefix
+     */
+    public function getPrefix()
+    {
+        return $this->prefix;
+    }
+
+    /**
+     * Returns an array of resources loaded to build this collection.
+     *
+     * @return ResourceInterface[] An array of resources
+     */
+    public function getResources()
+    {
+        $resources = $this->resources;
+        foreach ($this as $routes) {
+            if ($routes instanceof RouteCollection) {
+                $resources = array_merge($resources, $routes->getResources());
+            }
+        }
+
+        return array_unique($resources);
+    }
+
+    /**
+     * Adds a resource for this collection.
+     *
+     * @param ResourceInterface $resource A resource instance
+     */
+    public function addResource(ResourceInterface $resource)
+    {
+        $this->resources[] = $resource;
+    }
+
+    /**
+     * Sets the parent RouteCollection. It's only used internally from one RouteCollection
+     * to another. It makes no sense to be available as part of the public API.
+     *
+     * @param RouteCollection $parent The parent RouteCollection
+     */
+    private function setParent(RouteCollection $parent)
+    {
+        $this->parent = $parent;
+    }
+
+    /**
+     * Removes a route by name from this collection and its children recursively.
+     *
+     * @param string $name The route name
+     *
+     * @return Boolean true when found
+     */
+    private function removeRecursively($name)
+    {
+        // It is ensured by the adders (->add and ->addCollection) that there can
+        // only be one route per name in all connected collections. So we can stop
+        // iterating recursively on the first hit.
+        if (isset($this->routes[$name])) {
+            unset($this->routes[$name]);
+
+            return true;
+        }
+
+        foreach ($this->routes as $routes) {
+            if ($routes instanceof RouteCollection && $routes->removeRecursively($name)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether the given RouteCollection is already set in any child of the current instance.
+     *
+     * @param RouteCollection $collection A RouteCollection instance
+     *
+     * @return Boolean
+     */
+    private function hasCollection(RouteCollection $collection)
+    {
+        foreach ($this->routes as $routes) {
+            if ($routes === $collection || $routes instanceof RouteCollection && $routes->hasCollection($collection)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/RouteCompiler.php b/core/includes/Symfony/Component/Routing/RouteCompiler.php
new file mode 100644
index 0000000..03d3e38
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/RouteCompiler.php
@@ -0,0 +1,182 @@
+<?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\Routing;
+
+/**
+ * RouteCompiler compiles Route instances to CompiledRoute instances.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Tobias Schultze <http://tobion.de>
+ */
+class RouteCompiler implements RouteCompilerInterface
+{
+    const REGEX_DELIMITER = '#';
+
+    /**
+     * This string defines the characters that are automatically considered separators in front of
+     * optional placeholders (with default and no static text following). Such a single separator
+     * can be left out together with the optional placeholder from matching and generating URLs.
+     */
+    const SEPARATORS = '/,;.:-_~+*=@|';
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws \LogicException  If a variable is referenced more than once
+     * @throws \DomainException If a variable name is numeric because PHP raises an error for such
+     *                          subpatterns in PCRE and thus would break matching, e.g. "(?<123>.+)".
+     */
+    public function compile(Route $route)
+    {
+        $pattern = $route->getPattern();
+        $tokens = array();
+        $variables = array();
+        $matches = array();
+        $pos = 0;
+
+        // Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
+        // in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself.
+        preg_match_all('#\{\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
+        foreach ($matches as $match) {
+            $varName = substr($match[0][0], 1, -1);
+            // get all static text preceding the current variable
+            $precedingText = substr($pattern, $pos, $match[0][1] - $pos);
+            $pos = $match[0][1] + strlen($match[0][0]);
+            $precedingChar = strlen($precedingText) > 0 ? substr($precedingText, -1) : '';
+            $isSeparator = '' !== $precedingChar && false !== strpos(static::SEPARATORS, $precedingChar);
+
+            if (is_numeric($varName)) {
+                throw new \DomainException(sprintf('Variable name "%s" cannot be numeric in route pattern "%s". Please use a different name.', $varName, $pattern));
+            }
+            if (in_array($varName, $variables)) {
+                throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName));
+            }
+
+            if ($isSeparator && strlen($precedingText) > 1) {
+                $tokens[] = array('text', substr($precedingText, 0, -1));
+            } elseif (!$isSeparator && strlen($precedingText) > 0) {
+                $tokens[] = array('text', $precedingText);
+            }
+
+            $regexp = $route->getRequirement($varName);
+            if (null === $regexp) {
+                $followingPattern = (string) substr($pattern, $pos);
+                // Find the next static character after the variable that functions as a separator. By default, this separator and '/'
+                // are disallowed for the variable. This default requirement makes sure that optional variables can be matched at all
+                // and that the generating-matching-combination of URLs unambiguous, i.e. the params used for generating the URL are
+                // the same that will be matched. Example: new Route('/{page}.{_format}', array('_format' => 'html'))
+                // If {page} would also match the separating dot, {_format} would never match as {page} will eagerly consume everything.
+                // Also even if {_format} was not optional the requirement prevents that {page} matches something that was originally
+                // part of {_format} when generating the URL, e.g. _format = 'mobile.html'.
+                $nextSeparator = $this->findNextSeparator($followingPattern);
+                $regexp = sprintf('[^/%s]+', '/' !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : '');
+                if (('' !== $nextSeparator && !preg_match('#^\{\w+\}#', $followingPattern)) || '' === $followingPattern) {
+                    // When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive
+                    // quantifier. This prevents useless backtracking of PCRE and improves performance by 20% for matching those patterns.
+                    // Given the above example, there is no point in backtracking into {page} (that forbids the dot) when a dot must follow 
+                    // after it. This optimization cannot be applied when the next char is no real separator or when the next variable is
+                    // directly adjacent, e.g. '/{x}{y}'.
+                    $regexp .= '+';
+                }
+            }
+
+            $tokens[] = array('variable', $isSeparator ? $precedingChar : '', $regexp, $varName);
+            $variables[] = $varName;
+        }
+
+        if ($pos < strlen($pattern)) {
+            $tokens[] = array('text', substr($pattern, $pos));
+        }
+
+        // find the first optional token
+        $firstOptional = INF;
+        for ($i = count($tokens) - 1; $i >= 0; $i--) {
+            $token = $tokens[$i];
+            if ('variable' === $token[0] && $route->hasDefault($token[3])) {
+                $firstOptional = $i;
+            } else {
+                break;
+            }
+        }
+
+        // compute the matching regexp
+        $regexp = '';
+        for ($i = 0, $nbToken = count($tokens); $i < $nbToken; $i++) {
+            $regexp .= $this->computeRegexp($tokens, $i, $firstOptional);
+        }
+
+        return new CompiledRoute(
+            'text' === $tokens[0][0] ? $tokens[0][1] : '',
+            self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s',
+            array_reverse($tokens),
+            $variables
+        );
+    }
+
+    /**
+     * Returns the next static character in the Route pattern that will serve as a separator.
+     *
+     * @param string $pattern The route pattern
+     *
+     * @return string The next static character that functions as separator (or empty string when none available)
+     */
+    private function findNextSeparator($pattern)
+    {
+        if ('' == $pattern) {
+            // return empty string if pattern is empty or false (false which can be returned by substr)
+            return '';
+        }
+        // first remove all placeholders from the pattern so we can find the next real static character
+        $pattern = preg_replace('#\{\w+\}#', '', $pattern);
+
+        return isset($pattern[0]) && false !== strpos(static::SEPARATORS, $pattern[0]) ? $pattern[0] : '';
+    }
+
+    /**
+     * Computes the regexp used to match a specific token. It can be static text or a subpattern.
+     *
+     * @param array   $tokens        The route tokens
+     * @param integer $index         The index of the current token
+     * @param integer $firstOptional The index of the first optional token
+     *
+     * @return string The regexp pattern for a single token
+     */
+    private function computeRegexp(array $tokens, $index, $firstOptional)
+    {
+        $token = $tokens[$index];
+        if ('text' === $token[0]) {
+            // Text tokens
+            return preg_quote($token[1], self::REGEX_DELIMITER);
+        } else {
+            // Variable tokens
+            if (0 === $index && 0 === $firstOptional) {
+                // When the only token is an optional variable token, the separator is required
+                return sprintf('%s(?<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
+            } else {
+                $regexp = sprintf('%s(?<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
+                if ($index >= $firstOptional) {
+                    // Enclose each optional token in a subpattern to make it optional.
+                    // "?:" means it is non-capturing, i.e. the portion of the subject string that
+                    // matched the optional subpattern is not passed back.
+                    $regexp = "(?:$regexp";
+                    $nbTokens = count($tokens);
+                    if ($nbTokens - 1 == $index) {
+                        // Close the optional subpatterns
+                        $regexp .= str_repeat(")?", $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0));
+                    }
+                }
+
+                return $regexp;
+            }
+        }
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/RouteCompilerInterface.php b/core/includes/Symfony/Component/Routing/RouteCompilerInterface.php
new file mode 100644
index 0000000..f72e9e5
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/RouteCompilerInterface.php
@@ -0,0 +1,29 @@
+<?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\Routing;
+
+/**
+ * RouteCompilerInterface is the interface that all RouteCompiler classes must implement.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RouteCompilerInterface
+{
+    /**
+     * Compiles the current route instance.
+     *
+     * @param Route $route A Route instance
+     *
+     * @return CompiledRoute A CompiledRoute instance
+     */
+    public function compile(Route $route);
+}
diff --git a/core/includes/Symfony/Component/Routing/Router.php b/core/includes/Symfony/Component/Routing/Router.php
new file mode 100644
index 0000000..bd2c5a1
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Router.php
@@ -0,0 +1,252 @@
+<?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\Routing;
+
+use Symfony\Component\Config\Loader\LoaderInterface;
+use Symfony\Component\Config\ConfigCache;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface;
+
+/**
+ * The Router class is an example of the integration of all pieces of the
+ * routing system for easier use.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class Router implements RouterInterface
+{
+    protected $matcher;
+    protected $generator;
+    protected $context;
+    protected $loader;
+    protected $collection;
+    protected $resource;
+    protected $options;
+    protected $logger;
+
+    /**
+     * Constructor.
+     *
+     * @param LoaderInterface $loader   A LoaderInterface instance
+     * @param mixed           $resource The main resource to load
+     * @param array           $options  An array of options
+     * @param RequestContext  $context  The context
+     * @param LoggerInterface $logger   A logger instance
+     */
+    public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null)
+    {
+        $this->loader = $loader;
+        $this->resource = $resource;
+        $this->logger = $logger;
+        $this->context = null === $context ? new RequestContext() : $context;
+        $this->setOptions($options);
+    }
+
+    /**
+     * Sets options.
+     *
+     * Available options:
+     *
+     *   * cache_dir:     The cache directory (or null to disable caching)
+     *   * debug:         Whether to enable debugging or not (false by default)
+     *   * resource_type: Type hint for the main resource (optional)
+     *
+     * @param array $options An array of options
+     *
+     * @throws \InvalidArgumentException When unsupported option is provided
+     */
+    public function setOptions(array $options)
+    {
+        $this->options = array(
+            'cache_dir'              => null,
+            'debug'                  => false,
+            'generator_class'        => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
+            'generator_base_class'   => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
+            'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
+            'generator_cache_class'  => 'ProjectUrlGenerator',
+            'matcher_class'          => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
+            'matcher_base_class'     => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
+            'matcher_dumper_class'   => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
+            'matcher_cache_class'    => 'ProjectUrlMatcher',
+            'resource_type'          => null,
+            'strict_requirements'    => true,
+        );
+
+        // check option names and live merge, if errors are encountered Exception will be thrown
+        $invalid = array();
+        foreach ($options as $key => $value) {
+            if (array_key_exists($key, $this->options)) {
+                $this->options[$key] = $value;
+            } else {
+                $invalid[] = $key;
+            }
+        }
+
+        if ($invalid) {
+            throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('\', \'', $invalid)));
+        }
+    }
+
+    /**
+     * Sets an option.
+     *
+     * @param string $key   The key
+     * @param mixed  $value The value
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setOption($key, $value)
+    {
+        if (!array_key_exists($key, $this->options)) {
+            throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
+        }
+
+        $this->options[$key] = $value;
+    }
+
+    /**
+     * Gets an option value.
+     *
+     * @param string $key The key
+     *
+     * @return mixed The value
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function getOption($key)
+    {
+        if (!array_key_exists($key, $this->options)) {
+            throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key));
+        }
+
+        return $this->options[$key];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRouteCollection()
+    {
+        if (null === $this->collection) {
+            $this->collection = $this->loader->load($this->resource, $this->options['resource_type']);
+        }
+
+        return $this->collection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setContext(RequestContext $context)
+    {
+        $this->context = $context;
+
+        $this->getMatcher()->setContext($context);
+        $this->getGenerator()->setContext($context);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getContext()
+    {
+        return $this->context;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function generate($name, $parameters = array(), $absolute = false)
+    {
+        return $this->getGenerator()->generate($name, $parameters, $absolute);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function match($pathinfo)
+    {
+        return $this->getMatcher()->match($pathinfo);
+    }
+
+    /**
+     * Gets the UrlMatcher instance associated with this Router.
+     *
+     * @return UrlMatcherInterface A UrlMatcherInterface instance
+     */
+    public function getMatcher()
+    {
+        if (null !== $this->matcher) {
+            return $this->matcher;
+        }
+
+        if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
+            return $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context);
+        }
+
+        $class = $this->options['matcher_cache_class'];
+        $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']);
+        if (!$cache->isFresh($class)) {
+            $dumper = new $this->options['matcher_dumper_class']($this->getRouteCollection());
+
+            $options = array(
+                'class'      => $class,
+                'base_class' => $this->options['matcher_base_class'],
+            );
+
+            $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
+        }
+
+        require_once $cache;
+
+        return $this->matcher = new $class($this->context);
+    }
+
+    /**
+     * Gets the UrlGenerator instance associated with this Router.
+     *
+     * @return UrlGeneratorInterface A UrlGeneratorInterface instance
+     */
+    public function getGenerator()
+    {
+        if (null !== $this->generator) {
+            return $this->generator;
+        }
+
+        if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
+            $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger);
+        } else {
+            $class = $this->options['generator_cache_class'];
+            $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']);
+            if (!$cache->isFresh($class)) {
+                $dumper = new $this->options['generator_dumper_class']($this->getRouteCollection());
+
+                $options = array(
+                    'class'      => $class,
+                    'base_class' => $this->options['generator_base_class'],
+                );
+
+                $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources());
+            }
+
+            require_once $cache;
+
+            $this->generator = new $class($this->context, $this->logger);
+        }
+
+        if ($this->generator instanceof ConfigurableRequirementsInterface) {
+            $this->generator->setStrictRequirements($this->options['strict_requirements']);
+        }
+
+        return $this->generator;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/RouterInterface.php b/core/includes/Symfony/Component/Routing/RouterInterface.php
new file mode 100644
index 0000000..a10ae34
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/RouterInterface.php
@@ -0,0 +1,32 @@
+<?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\Routing;
+
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+
+/**
+ * RouterInterface is the interface that all Router classes must implement.
+ *
+ * This interface is the concatenation of UrlMatcherInterface and UrlGeneratorInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface
+{
+    /**
+     * Gets the RouteCollection instance associated with this Router.
+     *
+     * @return RouteCollection A RouteCollection instance
+     */
+    public function getRouteCollection();
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Annotation/RouteTest.php b/core/includes/Symfony/Component/Routing/Tests/Annotation/RouteTest.php
new file mode 100644
index 0000000..ff3c20c
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Annotation/RouteTest.php
@@ -0,0 +1,45 @@
+<?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\Routing\Tests\Annotation;
+
+use Symfony\Component\Routing\Annotation\Route;
+
+class RouteTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException \BadMethodCallException
+     */
+    public function testInvalidRouteParameter()
+    {
+        $route = new Route(array('foo' => 'bar'));
+    }
+
+    /**
+     * @dataProvider getValidParameters
+     */
+    public function testRouteParameters($parameter, $value, $getter)
+    {
+        $route = new Route(array($parameter => $value));
+        $this->assertEquals($route->$getter(), $value);
+    }
+
+    public function getValidParameters()
+    {
+        return array(
+           array('value', '/Blog', 'getPattern'),
+           array('requirements', array('_method' => 'GET'), 'getRequirements'),
+           array('options', array('compiler_class' => 'RouteCompiler'), 'getOptions'),
+           array('name', 'blog_index', 'getName'),
+           array('defaults', array('_controller' => 'MyBlogBundle:Blog:index'), 'getDefaults')
+        );
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/CompiledRouteTest.php b/core/includes/Symfony/Component/Routing/Tests/CompiledRouteTest.php
new file mode 100644
index 0000000..c65da0c
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/CompiledRouteTest.php
@@ -0,0 +1,26 @@
+<?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\Routing\Tests;
+
+use Symfony\Component\Routing\CompiledRoute;
+
+class CompiledRouteTest extends \PHPUnit_Framework_TestCase
+{
+    public function testAccessors()
+    {
+        $compiled = new CompiledRoute('prefix', 'regex', array('tokens'), array('variables'));
+        $this->assertEquals('prefix', $compiled->getStaticPrefix(), '__construct() takes a static prefix as its first argument');
+        $this->assertEquals('regex', $compiled->getRegex(), '__construct() takes a regexp as its second argument');
+        $this->assertEquals(array('tokens'), $compiled->getTokens(), '__construct() takes an array of tokens as its third argument');
+        $this->assertEquals(array('variables'), $compiled->getVariables(), '__construct() takes an array of variables as its forth argument');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php
new file mode 100644
index 0000000..56bcab2
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php
@@ -0,0 +1,16 @@
+<?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\Routing\Tests\Fixtures\AnnotatedClasses;
+
+abstract class AbstractClass
+{
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php
new file mode 100644
index 0000000..320dc35
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/AnnotatedClasses/FooClass.php
@@ -0,0 +1,16 @@
+<?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\Routing\Tests\Fixtures\AnnotatedClasses;
+
+class FooClass
+{
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php
new file mode 100644
index 0000000..c2601d4
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/CustomXmlFileLoader.php
@@ -0,0 +1,25 @@
+<?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\Routing\Tests\Fixtures;
+
+use Symfony\Component\Routing\Loader\XmlFileLoader;
+
+/**
+ * XmlFileLoader with schema validation turned off
+ */
+class CustomXmlFileLoader extends XmlFileLoader
+{
+    protected function validate(\DOMDocument $dom)
+    {
+        return true;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php
new file mode 100644
index 0000000..e95c1f2
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/RedirectableUrlMatcher.php
@@ -0,0 +1,30 @@
+<?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\Routing\Tests\Fixtures;
+
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface
+{
+    public function redirect($path, $route, $scheme = null)
+    {
+        return array(
+            '_controller' => 'Some controller reference...',
+            'path'        => $path,
+            'scheme'      => $scheme,
+        );
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/annotated.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/annotated.php
new file mode 100644
index 0000000..e69de29
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache
new file mode 100644
index 0000000..7efda77
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache
@@ -0,0 +1,75 @@
+# skip "real" requests
+RewriteCond %{REQUEST_FILENAME} -f
+RewriteRule .* - [QSA,L]
+
+# foo
+RewriteCond %{REQUEST_URI} ^/foo/(baz|symfony)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:foo,E=_ROUTING_bar:%1,E=_ROUTING_DEFAULTS_def:test]
+
+# foobar
+RewriteCond %{REQUEST_URI} ^/foo(?:/([^/]++))?$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:foobar,E=_ROUTING_bar:%1,E=_ROUTING_DEFAULTS_bar:toto]
+
+# bar
+RewriteCond %{REQUEST_URI} ^/bar/([^/]++)$
+RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [NC]
+RewriteRule .* - [S=1,E=_ROUTING__allow_GET:1,E=_ROUTING__allow_HEAD:1]
+RewriteCond %{REQUEST_URI} ^/bar/([^/]++)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:bar,E=_ROUTING_foo:%1]
+
+# baragain
+RewriteCond %{REQUEST_URI} ^/baragain/([^/]++)$
+RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$ [NC]
+RewriteRule .* - [S=1,E=_ROUTING__allow_GET:1,E=_ROUTING__allow_POST:1,E=_ROUTING__allow_HEAD:1]
+RewriteCond %{REQUEST_URI} ^/baragain/([^/]++)$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baragain,E=_ROUTING_foo:%1]
+
+# baz
+RewriteCond %{REQUEST_URI} ^/test/baz$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz]
+
+# baz2
+RewriteCond %{REQUEST_URI} ^/test/baz\.html$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz2]
+
+# baz3
+RewriteCond %{REQUEST_URI} ^/test/baz3$
+RewriteRule .* $0/ [QSA,L,R=301]
+RewriteCond %{REQUEST_URI} ^/test/baz3/$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz3]
+
+# baz4
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)$
+RewriteRule .* $0/ [QSA,L,R=301]
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz4,E=_ROUTING_foo:%1]
+
+# baz5
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
+RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [NC]
+RewriteRule .* - [S=2,E=_ROUTING__allow_GET:1,E=_ROUTING__allow_HEAD:1]
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)$
+RewriteRule .* $0/ [QSA,L,R=301]
+RewriteCond %{REQUEST_URI} ^/test/([^/]++)/$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz5,E=_ROUTING_foo:%1]
+
+# baz5unsafe
+RewriteCond %{REQUEST_URI} ^/testunsafe/([^/]++)/$
+RewriteCond %{REQUEST_METHOD} !^(POST)$ [NC]
+RewriteRule .* - [S=1,E=_ROUTING__allow_POST:1]
+RewriteCond %{REQUEST_URI} ^/testunsafe/([^/]++)/$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz5unsafe,E=_ROUTING_foo:%1]
+
+# baz6
+RewriteCond %{REQUEST_URI} ^/test/baz$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz6,E=_ROUTING_DEFAULTS_foo:bar\ baz]
+
+# baz7
+RewriteCond %{REQUEST_URI} ^/te\ st/baz$
+RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz7]
+
+# 405 Method Not Allowed
+RewriteCond %{_ROUTING__allow_GET} !-z [OR]
+RewriteCond %{_ROUTING__allow_HEAD} !-z [OR]
+RewriteCond %{_ROUTING__allow_POST} !-z
+RewriteRule .* app.php [QSA,L]
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php
new file mode 100644
index 0000000..d64bf95
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php
@@ -0,0 +1,239 @@
+<?php
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * ProjectUrlMatcher
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    public function match($pathinfo)
+    {
+        $allow = array();
+        $pathinfo = rawurldecode($pathinfo);
+
+        // foo
+        if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?<bar>baz|symfony)$#s', $pathinfo, $matches)) {
+            return array_merge($this->mergeDefaults($matches, array (  'def' => 'test',)), array('_route' => 'foo'));
+        }
+
+        // bar
+        if (0 === strpos($pathinfo, '/bar') && preg_match('#^/bar/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
+                $allow = array_merge($allow, array('GET', 'HEAD'));
+                goto not_bar;
+            }
+
+            $matches['_route'] = 'bar';
+
+            return $matches;
+        }
+        not_bar:
+
+        // barhead
+        if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
+                $allow = array_merge($allow, array('GET', 'HEAD'));
+                goto not_barhead;
+            }
+
+            $matches['_route'] = 'barhead';
+
+            return $matches;
+        }
+        not_barhead:
+
+        // baz
+        if ($pathinfo === '/test/baz') {
+            return array('_route' => 'baz');
+        }
+
+        // baz2
+        if ($pathinfo === '/test/baz.html') {
+            return array('_route' => 'baz2');
+        }
+
+        // baz3
+        if ($pathinfo === '/test/baz3/') {
+            return array('_route' => 'baz3');
+        }
+
+        // baz4
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'baz4';
+
+            return $matches;
+        }
+
+        // baz5
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+            if ($this->context->getMethod() != 'POST') {
+                $allow[] = 'POST';
+                goto not_baz5;
+            }
+
+            $matches['_route'] = 'baz5';
+
+            return $matches;
+        }
+        not_baz5:
+
+        // baz.baz6
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+            if ($this->context->getMethod() != 'PUT') {
+                $allow[] = 'PUT';
+                goto not_bazbaz6;
+            }
+
+            $matches['_route'] = 'baz.baz6';
+
+            return $matches;
+        }
+        not_bazbaz6:
+
+        // foofoo
+        if ($pathinfo === '/foofoo') {
+            return array (  'def' => 'test',  '_route' => 'foofoo',);
+        }
+
+        // quoter
+        if (preg_match('#^/(?<quoter>[\']+)$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'quoter';
+
+            return $matches;
+        }
+
+        // space
+        if ($pathinfo === '/spa ce') {
+            return array('_route' => 'space');
+        }
+
+        if (0 === strpos($pathinfo, '/a')) {
+            if (0 === strpos($pathinfo, '/a/b\'b')) {
+                // foo1
+                if (preg_match('#^/a/b\'b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'foo1';
+
+                    return $matches;
+                }
+
+                // bar1
+                if (preg_match('#^/a/b\'b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'bar1';
+
+                    return $matches;
+                }
+
+            }
+
+            // overridden
+            if (preg_match('#^/a/(?<var>.*)$#s', $pathinfo, $matches)) {
+                $matches['_route'] = 'overridden';
+
+                return $matches;
+            }
+
+            if (0 === strpos($pathinfo, '/a/b\'b')) {
+                // foo2
+                if (preg_match('#^/a/b\'b/(?<foo1>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'foo2';
+
+                    return $matches;
+                }
+
+                // bar2
+                if (preg_match('#^/a/b\'b/(?<bar1>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'bar2';
+
+                    return $matches;
+                }
+
+            }
+
+        }
+
+        if (0 === strpos($pathinfo, '/multi')) {
+            // helloWorld
+            if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?<who>[^/]++))?$#s', $pathinfo, $matches)) {
+                return array_merge($this->mergeDefaults($matches, array (  'who' => 'World!',)), array('_route' => 'helloWorld'));
+            }
+
+            // overridden2
+            if ($pathinfo === '/multi/new') {
+                return array('_route' => 'overridden2');
+            }
+
+            // hey
+            if ($pathinfo === '/multi/hey/') {
+                return array('_route' => 'hey');
+            }
+
+        }
+
+        // foo3
+        if (preg_match('#^/(?<_locale>[^/]++)/b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'foo3';
+
+            return $matches;
+        }
+
+        // bar3
+        if (preg_match('#^/(?<_locale>[^/]++)/b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'bar3';
+
+            return $matches;
+        }
+
+        // ababa
+        if ($pathinfo === '/ababa') {
+            return array('_route' => 'ababa');
+        }
+
+        // foo4
+        if (0 === strpos($pathinfo, '/aba') && preg_match('#^/aba/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'foo4';
+
+            return $matches;
+        }
+
+        if (0 === strpos($pathinfo, '/a')) {
+            // a
+            if ($pathinfo === '/a/a...') {
+                return array('_route' => 'a');
+            }
+
+            if (0 === strpos($pathinfo, '/a/b')) {
+                // b
+                if (preg_match('#^/a/b/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'b';
+
+                    return $matches;
+                }
+
+                // c
+                if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'c';
+
+                    return $matches;
+                }
+
+            }
+
+        }
+
+        throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache
new file mode 100644
index 0000000..d16ab97
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.apache
@@ -0,0 +1,7 @@
+# skip "real" requests
+RewriteCond %{REQUEST_FILENAME} -f
+RewriteRule .* - [QSA,L]
+
+# foo
+RewriteCond %{REQUEST_URI} ^/foo$
+RewriteRule .* ap\ p_d\ ev.php [QSA,L,E=_ROUTING__route:foo]
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php
new file mode 100644
index 0000000..24f66bd
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php
@@ -0,0 +1,269 @@
+<?php
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * ProjectUrlMatcher
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    public function match($pathinfo)
+    {
+        $allow = array();
+        $pathinfo = rawurldecode($pathinfo);
+
+        // foo
+        if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?<bar>baz|symfony)$#s', $pathinfo, $matches)) {
+            return array_merge($this->mergeDefaults($matches, array (  'def' => 'test',)), array('_route' => 'foo'));
+        }
+
+        // bar
+        if (0 === strpos($pathinfo, '/bar') && preg_match('#^/bar/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
+                $allow = array_merge($allow, array('GET', 'HEAD'));
+                goto not_bar;
+            }
+
+            $matches['_route'] = 'bar';
+
+            return $matches;
+        }
+        not_bar:
+
+        // barhead
+        if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
+                $allow = array_merge($allow, array('GET', 'HEAD'));
+                goto not_barhead;
+            }
+
+            $matches['_route'] = 'barhead';
+
+            return $matches;
+        }
+        not_barhead:
+
+        // baz
+        if ($pathinfo === '/test/baz') {
+            return array('_route' => 'baz');
+        }
+
+        // baz2
+        if ($pathinfo === '/test/baz.html') {
+            return array('_route' => 'baz2');
+        }
+
+        // baz3
+        if (rtrim($pathinfo, '/') === '/test/baz3') {
+            if (substr($pathinfo, -1) !== '/') {
+                return $this->redirect($pathinfo.'/', 'baz3');
+            }
+
+            return array('_route' => 'baz3');
+        }
+
+        // baz4
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?<foo>[^/]++)/?$#s', $pathinfo, $matches)) {
+            if (substr($pathinfo, -1) !== '/') {
+                return $this->redirect($pathinfo.'/', 'baz4');
+            }
+
+            $matches['_route'] = 'baz4';
+
+            return $matches;
+        }
+
+        // baz5
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+            if ($this->context->getMethod() != 'POST') {
+                $allow[] = 'POST';
+                goto not_baz5;
+            }
+
+            $matches['_route'] = 'baz5';
+
+            return $matches;
+        }
+        not_baz5:
+
+        // baz.baz6
+        if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?<foo>[^/]++)/$#s', $pathinfo, $matches)) {
+            if ($this->context->getMethod() != 'PUT') {
+                $allow[] = 'PUT';
+                goto not_bazbaz6;
+            }
+
+            $matches['_route'] = 'baz.baz6';
+
+            return $matches;
+        }
+        not_bazbaz6:
+
+        // foofoo
+        if ($pathinfo === '/foofoo') {
+            return array (  'def' => 'test',  '_route' => 'foofoo',);
+        }
+
+        // quoter
+        if (preg_match('#^/(?<quoter>[\']+)$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'quoter';
+
+            return $matches;
+        }
+
+        // space
+        if ($pathinfo === '/spa ce') {
+            return array('_route' => 'space');
+        }
+
+        if (0 === strpos($pathinfo, '/a')) {
+            if (0 === strpos($pathinfo, '/a/b\'b')) {
+                // foo1
+                if (preg_match('#^/a/b\'b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'foo1';
+
+                    return $matches;
+                }
+
+                // bar1
+                if (preg_match('#^/a/b\'b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'bar1';
+
+                    return $matches;
+                }
+
+            }
+
+            // overridden
+            if (preg_match('#^/a/(?<var>.*)$#s', $pathinfo, $matches)) {
+                $matches['_route'] = 'overridden';
+
+                return $matches;
+            }
+
+            if (0 === strpos($pathinfo, '/a/b\'b')) {
+                // foo2
+                if (preg_match('#^/a/b\'b/(?<foo1>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'foo2';
+
+                    return $matches;
+                }
+
+                // bar2
+                if (preg_match('#^/a/b\'b/(?<bar1>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'bar2';
+
+                    return $matches;
+                }
+
+            }
+
+        }
+
+        if (0 === strpos($pathinfo, '/multi')) {
+            // helloWorld
+            if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?<who>[^/]++))?$#s', $pathinfo, $matches)) {
+                return array_merge($this->mergeDefaults($matches, array (  'who' => 'World!',)), array('_route' => 'helloWorld'));
+            }
+
+            // overridden2
+            if ($pathinfo === '/multi/new') {
+                return array('_route' => 'overridden2');
+            }
+
+            // hey
+            if (rtrim($pathinfo, '/') === '/multi/hey') {
+                if (substr($pathinfo, -1) !== '/') {
+                    return $this->redirect($pathinfo.'/', 'hey');
+                }
+
+                return array('_route' => 'hey');
+            }
+
+        }
+
+        // foo3
+        if (preg_match('#^/(?<_locale>[^/]++)/b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'foo3';
+
+            return $matches;
+        }
+
+        // bar3
+        if (preg_match('#^/(?<_locale>[^/]++)/b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'bar3';
+
+            return $matches;
+        }
+
+        // ababa
+        if ($pathinfo === '/ababa') {
+            return array('_route' => 'ababa');
+        }
+
+        // foo4
+        if (0 === strpos($pathinfo, '/aba') && preg_match('#^/aba/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
+            $matches['_route'] = 'foo4';
+
+            return $matches;
+        }
+
+        if (0 === strpos($pathinfo, '/a')) {
+            // a
+            if ($pathinfo === '/a/a...') {
+                return array('_route' => 'a');
+            }
+
+            if (0 === strpos($pathinfo, '/a/b')) {
+                // b
+                if (preg_match('#^/a/b/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'b';
+
+                    return $matches;
+                }
+
+                // c
+                if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
+                    $matches['_route'] = 'c';
+
+                    return $matches;
+                }
+
+            }
+
+        }
+
+        // secure
+        if ($pathinfo === '/secure') {
+            if ($this->context->getScheme() !== 'https') {
+                return $this->redirect($pathinfo, 'secure', 'https');
+            }
+
+            return array('_route' => 'secure');
+        }
+
+        // nonsecure
+        if ($pathinfo === '/nonsecure') {
+            if ($this->context->getScheme() !== 'http') {
+                return $this->redirect($pathinfo, 'nonsecure', 'http');
+            }
+
+            return array('_route' => 'nonsecure');
+        }
+
+        throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php
new file mode 100644
index 0000000..a0b1320
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher3.php
@@ -0,0 +1,45 @@
+<?php
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * ProjectUrlMatcher
+ *
+ * This class has been auto-generated
+ * by the Symfony Routing Component.
+ */
+class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
+{
+    /**
+     * Constructor.
+     */
+    public function __construct(RequestContext $context)
+    {
+        $this->context = $context;
+    }
+
+    public function match($pathinfo)
+    {
+        $allow = array();
+        $pathinfo = rawurldecode($pathinfo);
+
+        if (0 === strpos($pathinfo, '/rootprefix')) {
+            // static
+            if ($pathinfo === '/rootprefix/test') {
+                return array('_route' => 'static');
+            }
+
+            // dynamic
+            if (preg_match('#^/rootprefix/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
+                $matches['_route'] = 'dynamic';
+
+                return $matches;
+            }
+
+        }
+
+        throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/empty.yml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/empty.yml
new file mode 100644
index 0000000..e69de29
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/foo.xml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/foo.xml
new file mode 100644
index 0000000..e69de29
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/foo1.xml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/foo1.xml
new file mode 100644
index 0000000..e69de29
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml
new file mode 100644
index 0000000..df64d32
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/incomplete.yml
@@ -0,0 +1,2 @@
+blog_show:
+    defaults:  { _controller: MyBlogBundle:Blog:show }
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml
new file mode 100644
index 0000000..a9b72f3
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalid.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    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}">
+        <default key="_controller">MyBundle:Blog:show</default>
+        <requirement key="_method">GET</requirement>
+        <option key="segment_separators">/</option>
+    <!-- </route> -->
+</routes>
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalid.yml
@@ -0,0 +1 @@
+foo
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml
new file mode 100644
index 0000000..015e270
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidkeys.yml
@@ -0,0 +1,3 @@
+someroute:
+  resource: path/to/some.yml
+  name_prefix: test_
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml
new file mode 100644
index 0000000..863ef03
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidnode.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    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">
+
+    <foo>bar</foo>
+</routes>
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml
new file mode 100644
index 0000000..cba82f3
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/nonvalidroute.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    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}">
+        <default key="_controller">MyBundle:Blog:show</default>
+        <requirement key="_method">GET</requirement>
+        <option key="compiler_class">RouteCompiler</option>
+        <foo key="bar">baz</foo>
+    </route>
+</routes>
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.php b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.php
new file mode 100644
index 0000000..0d1d3a7
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.php
@@ -0,0 +1,13 @@
+<?php
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+
+$collection = new RouteCollection();
+$collection->add('blog_show', new Route(
+    '/blog/{slug}',
+    array('_controller' => 'MyBlogBundle:Blog:show'),
+    array(),
+    array('compiler_class' => 'RouteCompiler')
+));
+
+return $collection;
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml
new file mode 100644
index 0000000..fdb3807
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    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}">
+        <default key="_controller">MyBundle:Blog:show</default>
+        <requirement key="_method">GET</requirement>
+        <option key="compiler_class">RouteCompiler</option>
+    </route>
+</routes>
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml
new file mode 100644
index 0000000..d5136cb
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validpattern.yml
@@ -0,0 +1,5 @@
+blog_show:
+    pattern:   /blog/{slug}
+    defaults:  { _controller: MyBlogBundle:Blog:show }
+    options:
+        compiler_class: RouteCompiler
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/validresource.xml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validresource.xml
new file mode 100644
index 0000000..14ac0fc
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validresource.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<routes xmlns="http://symfony.com/schema/routing"
+    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}">
+        <default key="foo">foo</default>
+        <requirement key="foo">\d+</requirement>
+        <option key="foo">bar</option>
+    </import>
+</routes>
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/validresource.yml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validresource.yml
new file mode 100644
index 0000000..e66f376
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/validresource.yml
@@ -0,0 +1,6 @@
+blog_show:
+    resource:     validpattern.yml
+    prefix:       /{foo}
+    defaults:     { 'foo': 'foo' }
+    requirements: { 'foo': '\d+' }
+    options:      { 'foo': 'bar' }
diff --git a/core/includes/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml b/core/includes/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml
new file mode 100644
index 0000000..f217d5b
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Fixtures/withdoctype.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE foo>
+<foo></foo>
diff --git a/core/includes/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/core/includes/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
new file mode 100644
index 0000000..6a86736
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
@@ -0,0 +1,117 @@
+<?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\Routing\Tests\Generator\Dumper;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper;
+use Symfony\Component\Routing\RequestContext;
+
+class PhpGeneratorDumperTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var RouteCollection
+     */
+    private $routeCollection;
+
+    /**
+     * @var PhpGeneratorDumper
+     */
+    private $generatorDumper;
+
+    /**
+     * @var string
+     */
+    private $testTmpFilepath;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->routeCollection = new RouteCollection();
+        $this->generatorDumper = new PhpGeneratorDumper($this->routeCollection);
+        $this->testTmpFilepath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_generator.php';
+        @unlink($this->testTmpFilepath);
+    }
+
+    protected function tearDown()
+    {
+        parent::tearDown();
+
+        @unlink($this->testTmpFilepath);
+
+        $this->routeCollection = null;
+        $this->generatorDumper = null;
+        $this->testTmpFilepath = null;
+    }
+
+    public function testDumpWithRoutes()
+    {
+        $this->routeCollection->add('Test', new Route('/testing/{foo}'));
+        $this->routeCollection->add('Test2', new Route('/testing2'));
+
+        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
+        include ($this->testTmpFilepath);
+
+        $projectUrlGenerator = new \ProjectUrlGenerator(new RequestContext('/app.php'));
+
+        $absoluteUrlWithParameter    = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), true);
+        $absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), true);
+        $relativeUrlWithParameter    = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), false);
+        $relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), false);
+
+        $this->assertEquals($absoluteUrlWithParameter, 'http://localhost/app.php/testing/bar');
+        $this->assertEquals($absoluteUrlWithoutParameter, 'http://localhost/app.php/testing2');
+        $this->assertEquals($relativeUrlWithParameter, '/app.php/testing/bar');
+        $this->assertEquals($relativeUrlWithoutParameter, '/app.php/testing2');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testDumpWithoutRoutes()
+    {
+        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'WithoutRoutesUrlGenerator')));
+        include ($this->testTmpFilepath);
+
+        $projectUrlGenerator = new \WithoutRoutesUrlGenerator(new RequestContext('/app.php'));
+
+        $projectUrlGenerator->generate('Test', array());
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateNonExistingRoute()
+    {
+        $this->routeCollection->add('Test', new Route('/test'));
+
+        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'NonExistingRoutesUrlGenerator')));
+        include ($this->testTmpFilepath);
+
+        $projectUrlGenerator = new \NonExistingRoutesUrlGenerator(new RequestContext());
+        $url = $projectUrlGenerator->generate('NonExisting', array());
+    }
+
+    public function testDumpForRouteWithDefaults()
+    {
+        $this->routeCollection->add('Test', new Route('/testing/{foo}', array('foo' => 'bar')));
+
+        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'DefaultRoutesUrlGenerator')));
+        include ($this->testTmpFilepath);
+
+        $projectUrlGenerator = new \DefaultRoutesUrlGenerator(new RequestContext());
+        $url = $projectUrlGenerator->generate('Test', array());
+
+        $this->assertEquals($url, '/testing');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/core/includes/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
new file mode 100644
index 0000000..224744f
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
@@ -0,0 +1,391 @@
+<?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\Routing\Tests\Generator;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\Routing\RequestContext;
+
+class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
+{
+    public function testAbsoluteUrlWithPort80()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array(), true);
+
+        $this->assertEquals('http://localhost/app.php/testing', $url);
+    }
+
+    public function testAbsoluteSecureUrlWithPort443()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes, array('scheme' => 'https'))->generate('test', array(), true);
+
+        $this->assertEquals('https://localhost/app.php/testing', $url);
+    }
+
+    public function testAbsoluteUrlWithNonStandardPort()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes, array('httpPort' => 8080))->generate('test', array(), true);
+
+        $this->assertEquals('http://localhost:8080/app.php/testing', $url);
+    }
+
+    public function testAbsoluteSecureUrlWithNonStandardPort()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes, array('httpsPort' => 8080, 'scheme' => 'https'))->generate('test', array(), true);
+
+        $this->assertEquals('https://localhost:8080/app.php/testing', $url);
+    }
+
+    public function testRelativeUrlWithoutParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array(), false);
+
+        $this->assertEquals('/app.php/testing', $url);
+    }
+
+    public function testRelativeUrlWithParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
+        $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), false);
+
+        $this->assertEquals('/app.php/testing/bar', $url);
+    }
+
+    public function testRelativeUrlWithNullParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing.{format}', array('format' => null)));
+        $url = $this->getGenerator($routes)->generate('test', array(), false);
+
+        $this->assertEquals('/app.php/testing', $url);
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testRelativeUrlWithNullParameterButNotOptional()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}/bar', array('foo' => null)));
+        // This must raise an exception because the default requirement for "foo" is "[^/]+" which is not met with these params.
+        // Generating path "/testing//bar" would be wrong as matching this route would fail.
+        $this->getGenerator($routes)->generate('test', array(), false);
+    }
+
+    public function testRelativeUrlWithOptionalZeroParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{page}'));
+        $url = $this->getGenerator($routes)->generate('test', array('page' => 0), false);
+
+        $this->assertEquals('/app.php/testing/0', $url);
+    }
+
+    public function testNotPassedOptionalParameterInBetween()
+    {
+        $routes = $this->getRoutes('test', new Route('/{slug}/{page}', array('slug' => 'index', 'page' => 0)));
+        $this->assertSame('/app.php/index/1', $this->getGenerator($routes)->generate('test', array('page' => 1)));
+        $this->assertSame('/app.php/', $this->getGenerator($routes)->generate('test'));
+    }
+
+    public function testRelativeUrlWithExtraParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), false);
+
+        $this->assertEquals('/app.php/testing?foo=bar', $url);
+    }
+
+    public function testAbsoluteUrlWithExtraParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), true);
+
+        $this->assertEquals('http://localhost/app.php/testing?foo=bar', $url);
+    }
+
+    public function testUrlWithNullExtraParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $url = $this->getGenerator($routes)->generate('test', array('foo' => null), true);
+
+        $this->assertEquals('http://localhost/app.php/testing', $url);
+    }
+
+    public function testUrlWithExtraParametersFromGlobals()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing'));
+        $generator = $this->getGenerator($routes);
+        $context = new RequestContext('/app.php');
+        $context->setParameter('bar', 'bar');
+        $generator->setContext($context);
+        $url = $generator->generate('test', array('foo' => 'bar'));
+
+        $this->assertEquals('/app.php/testing?foo=bar', $url);
+    }
+
+    public function testUrlWithGlobalParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
+        $generator = $this->getGenerator($routes);
+        $context = new RequestContext('/app.php');
+        $context->setParameter('foo', 'bar');
+        $generator->setContext($context);
+        $url = $generator->generate('test', array());
+
+        $this->assertEquals('/app.php/testing/bar', $url);
+    }
+
+    public function testGlobalParameterHasHigherPriorityThanDefault()
+    {
+        $routes = $this->getRoutes('test', new Route('/{_locale}', array('_locale' => 'en')));
+        $generator = $this->getGenerator($routes);
+        $context = new RequestContext('/app.php');
+        $context->setParameter('_locale', 'de');
+        $generator->setContext($context);
+        $url = $generator->generate('test', array());
+
+        $this->assertSame('/app.php/de', $url);
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\RouteNotFoundException
+     */
+    public function testGenerateWithoutRoutes()
+    {
+        $routes = $this->getRoutes('foo', new Route('/testing/{foo}'));
+        $this->getGenerator($routes)->generate('test', array(), true);
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\MissingMandatoryParametersException
+     */
+    public function testGenerateForRouteWithoutMandatoryParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
+        $this->getGenerator($routes)->generate('test', array(), true);
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testGenerateForRouteWithInvalidOptionalParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+')));
+        $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), true);
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testGenerateForRouteWithInvalidParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array(), array('foo' => '1|2')));
+        $this->getGenerator($routes)->generate('test', array('foo' => '0'), true);
+    }
+
+    public function testGenerateForRouteWithInvalidOptionalParameterNonStrict()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+')));
+        $generator = $this->getGenerator($routes);
+        $generator->setStrictRequirements(false);
+        $this->assertNull($generator->generate('test', array('foo' => 'bar'), true));
+    }
+
+    public function testGenerateForRouteWithInvalidOptionalParameterNonStrictWithLogger()
+    {
+        if (!interface_exists('Symfony\Component\HttpKernel\Log\LoggerInterface')) {
+            $this->markTestSkipped('The "HttpKernel" component is not available');
+        }
+
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+')));
+        $logger = $this->getMock('Symfony\Component\HttpKernel\Log\LoggerInterface');
+        $logger->expects($this->once())
+            ->method('err');
+        $generator = $this->getGenerator($routes, array(), $logger);
+        $generator->setStrictRequirements(false);
+        $this->assertNull($generator->generate('test', array('foo' => 'bar'), true));
+    }
+
+    public function testGenerateForRouteWithInvalidParameterButDisabledRequirementsCheck()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+')));
+        $generator = $this->getGenerator($routes);
+        $generator->setStrictRequirements(null);
+        $this->assertSame('/app.php/testing/bar', $generator->generate('test', array('foo' => 'bar')));
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testGenerateForRouteWithInvalidMandatoryParameter()
+    {
+        $routes = $this->getRoutes('test', new Route('/testing/{foo}', array(), array('foo' => 'd+')));
+        $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), true);
+    }
+    
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testRequiredParamAndEmptyPassed()
+    {
+        $routes = $this->getRoutes('test', new Route('/{slug}', array(), array('slug' => '.+')));
+        $this->getGenerator($routes)->generate('test', array('slug' => ''));
+    }
+
+    public function testSchemeRequirementDoesNothingIfSameCurrentScheme()
+    {
+        $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'http')));
+        $this->assertEquals('/app.php/', $this->getGenerator($routes)->generate('test'));
+
+        $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'https')));
+        $this->assertEquals('/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test'));
+    }
+
+    public function testSchemeRequirementForcesAbsoluteUrl()
+    {
+        $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'https')));
+        $this->assertEquals('https://localhost/app.php/', $this->getGenerator($routes)->generate('test'));
+
+        $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'http')));
+        $this->assertEquals('http://localhost/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test'));
+    }
+
+    public function testNoTrailingSlashForMultipleOptionalParameters()
+    {
+        $routes = $this->getRoutes('test', new Route('/category/{slug1}/{slug2}/{slug3}', array('slug2' => null, 'slug3' => null)));
+
+        $this->assertEquals('/app.php/category/foo', $this->getGenerator($routes)->generate('test', array('slug1' => 'foo')));
+    }
+
+    public function testWithAnIntegerAsADefaultValue()
+    {
+        $routes = $this->getRoutes('test', new Route('/{default}', array('default' => 0)));
+
+        $this->assertEquals('/app.php/foo', $this->getGenerator($routes)->generate('test', array('default' => 'foo')));
+    }
+
+    public function testQueryParamSameAsDefault()
+    {
+        $routes = $this->getRoutes('test', new Route('/test', array('default' => 'value')));
+
+        $this->assertSame('/app.php/test?default=foo', $this->getGenerator($routes)->generate('test', array('default' => 'foo')));
+        $this->assertSame('/app.php/test?default=value', $this->getGenerator($routes)->generate('test', array('default' => 'value')));
+        $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test'));
+    }
+
+    public function testUrlEncoding()
+    {
+        // This tests the encoding of reserved characters that are used for delimiting of URI components (defined in RFC 3986)
+        // and other special ASCII chars. These chars are tested as static text path, variable path and query param.
+        $chars = '@:[]/()*\'" +,;-._~&$<>|{}%\\^`!?foo=bar#id';
+        $routes = $this->getRoutes('test', new Route("/$chars/{varpath}", array(), array('varpath' => '.+')));
+        $this->assertSame('/app.php/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id'
+            . '/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id'
+            . '?query=%40%3A%5B%5D%2F%28%29%2A%27%22+%2B%2C%3B-._%7E%26%24%3C%3E%7C%7B%7D%25%5C%5E%60%21%3Ffoo%3Dbar%23id',
+            $this->getGenerator($routes)->generate('test', array(
+                'varpath' => $chars,
+                'query' => $chars
+            ))
+        );
+    }
+
+    public function testEncodingOfRelativePathSegments()
+    {
+        $routes = $this->getRoutes('test', new Route('/dir/../dir/..'));
+        $this->assertSame('/app.php/dir/%2E%2E/dir/%2E%2E', $this->getGenerator($routes)->generate('test'));
+        $routes = $this->getRoutes('test', new Route('/dir/./dir/.'));
+        $this->assertSame('/app.php/dir/%2E/dir/%2E', $this->getGenerator($routes)->generate('test'));
+        $routes = $this->getRoutes('test', new Route('/a./.a/a../..a/...'));
+        $this->assertSame('/app.php/a./.a/a../..a/...', $this->getGenerator($routes)->generate('test'));
+    }
+
+    public function testAdjacentVariables()
+    {
+        $routes = $this->getRoutes('test', new Route('/{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => '\d+')));
+        $generator = $this->getGenerator($routes);
+        $this->assertSame('/app.php/foo123', $generator->generate('test', array('x' => 'foo', 'y' => '123')));
+        $this->assertSame('/app.php/foo123bar.xml', $generator->generate('test', array('x' => 'foo', 'y' => '123', 'z' => 'bar', '_format' => 'xml')));
+
+        // The default requirement for 'x' should not allow the separator '.' in this case because it would otherwise match everything
+        // and following optional variables like _format could never match.
+        $this->setExpectedException('Symfony\Component\Routing\Exception\InvalidParameterException');
+        $generator->generate('test', array('x' => 'do.t', 'y' => '123', 'z' => 'bar', '_format' => 'xml'));
+    }
+
+    public function testOptionalVariableWithNoRealSeparator()
+    {
+        $routes = $this->getRoutes('test', new Route('/get{what}', array('what' => 'All')));
+        $generator = $this->getGenerator($routes);
+
+        $this->assertSame('/app.php/get', $generator->generate('test'));
+        $this->assertSame('/app.php/getSites', $generator->generate('test', array('what' => 'Sites')));
+    }
+
+    public function testRequiredVariableWithNoRealSeparator()
+    {
+        $routes = $this->getRoutes('test', new Route('/get{what}Suffix'));
+        $generator = $this->getGenerator($routes);
+
+        $this->assertSame('/app.php/getSitesSuffix', $generator->generate('test', array('what' => 'Sites')));
+    }
+
+    public function testDefaultRequirementOfVariable()
+    {
+        $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
+        $generator = $this->getGenerator($routes);
+
+        $this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html')));
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testDefaultRequirementOfVariableDisallowsSlash()
+    {
+        $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
+        $this->getGenerator($routes)->generate('test', array('page' => 'index', '_format' => 'sl/ash'));
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
+     */
+    public function testDefaultRequirementOfVariableDisallowsNextSeparator()
+    {
+
+        $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
+        $this->getGenerator($routes)->generate('test', array('page' => 'do.t', '_format' => 'html'));
+    }
+
+    protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null)
+    {
+        $context = new RequestContext('/app.php');
+        foreach ($parameters as $key => $value) {
+            $method = 'set'.$key;
+            $context->$method($value);
+        }
+        $generator = new UrlGenerator($routes, $context, $logger);
+
+        return $generator;
+    }
+
+    protected function getRoutes($name, Route $route)
+    {
+        $routes = new RouteCollection();
+        $routes->add($name, $route);
+
+        return $routes;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.php b/core/includes/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.php
new file mode 100644
index 0000000..c927ae4
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Loader/AbstractAnnotationLoaderTest.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\Routing\Tests\Loader;
+
+abstract class AbstractAnnotationLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Doctrine\\Common\\Version')) {
+            $this->markTestSkipped('Doctrine is not available.');
+        }
+    }
+
+    public function getReader()
+    {
+        return $this->getMockBuilder('Doctrine\Common\Annotations\Reader')
+            ->disableOriginalConstructor()
+            ->getMock()
+        ;
+    }
+
+    public function getClassLoader($reader)
+    {
+        return $this->getMockBuilder('Symfony\Component\Routing\Loader\AnnotationClassLoader')
+            ->setConstructorArgs(array($reader))
+            ->getMockForAbstractClass()
+        ;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php b/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php
new file mode 100644
index 0000000..faeacfb
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationClassLoaderTest.php
@@ -0,0 +1,74 @@
+<?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\Routing\Tests\Loader;
+
+use Symfony\Component\Routing\Loader\AnnotationClassLoader;
+
+class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
+{
+    protected $loader;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->loader = $this->getClassLoader($this->getReader());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testLoadMissingClass()
+    {
+        $this->loader->load('MissingClass');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testLoadAbstractClass()
+    {
+        $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\AbstractClass');
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\AnnotationClassLoader::supports
+     * @dataProvider provideTestSupportsChecksResource
+     */
+    public function testSupportsChecksResource($resource, $expectedSupports)
+    {
+        $this->assertSame($expectedSupports, $this->loader->supports($resource), '->supports() returns true if the resource is loadable');
+    }
+
+    public function provideTestSupportsChecksResource()
+    {
+        return array(
+            array('class', true),
+            array('\fully\qualified\class\name', true),
+            array('namespaced\class\without\leading\slash', true),
+            array('ÿClassWithLegalSpecialCharacters', true),
+            array('5', false),
+            array('foo.foo', false),
+            array(null, false),
+        );
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\AnnotationClassLoader::supports
+     */
+    public function testSupportsChecksTypeIfSpecified()
+    {
+        $this->assertTrue($this->loader->supports('class', 'annotation'), '->supports() checks the resource type if specified');
+        $this->assertFalse($this->loader->supports('class', 'foo'), '->supports() checks the resource type if specified');
+    }
+
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php b/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php
new file mode 100644
index 0000000..3877eec
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationDirectoryLoaderTest.php
@@ -0,0 +1,50 @@
+<?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\Routing\Tests\Loader;
+
+use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
+use Symfony\Component\Config\FileLocator;
+
+class AnnotationDirectoryLoaderTest extends AbstractAnnotationLoaderTest
+{
+    protected $loader;
+    protected $reader;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->reader = $this->getReader();
+        $this->loader = new AnnotationDirectoryLoader(new FileLocator(), $this->getClassLoader($this->reader));
+    }
+
+    public function testLoad()
+    {
+        $this->reader->expects($this->once())->method('getClassAnnotation');
+
+        $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses');
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\AnnotationDirectoryLoader::supports
+     */
+    public function testSupports()
+    {
+        $fixturesDir = __DIR__.'/../Fixtures';
+
+        $this->assertTrue($this->loader->supports($fixturesDir), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($this->loader->supports($fixturesDir, 'annotation'), '->supports() checks the resource type if specified');
+        $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports() checks the resource type if specified');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php b/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php
new file mode 100644
index 0000000..02d0576
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Loader/AnnotationFileLoaderTest.php
@@ -0,0 +1,50 @@
+<?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\Routing\Tests\Loader;
+
+use Symfony\Component\Routing\Loader\AnnotationFileLoader;
+use Symfony\Component\Config\FileLocator;
+
+class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest
+{
+    protected $loader;
+    protected $reader;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->reader = $this->getReader();
+        $this->loader = new AnnotationFileLoader(new FileLocator(), $this->getClassLoader($this->reader));
+    }
+
+    public function testLoad()
+    {
+        $this->reader->expects($this->once())->method('getClassAnnotation');
+
+        $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php');
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\AnnotationFileLoader::supports
+     */
+    public function testSupports()
+    {
+        $fixture = __DIR__.'/../Fixtures/annotated.php';
+
+        $this->assertTrue($this->loader->supports($fixture), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($this->loader->supports($fixture, 'annotation'), '->supports() checks the resource type if specified');
+        $this->assertFalse($this->loader->supports($fixture, 'foo'), '->supports() checks the resource type if specified');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php b/core/includes/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php
new file mode 100644
index 0000000..d9e3ff5
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Loader/ClosureLoaderTest.php
@@ -0,0 +1,61 @@
+<?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\Routing\Tests\Loader;
+
+use Symfony\Component\Routing\Loader\ClosureLoader;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+class ClosureLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\FileLocator')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\ClosureLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new ClosureLoader();
+
+        $closure = function () {};
+
+        $this->assertTrue($loader->supports($closure), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($loader->supports($closure, 'closure'), '->supports() checks the resource type if specified');
+        $this->assertFalse($loader->supports($closure, 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\ClosureLoader::load
+     */
+    public function testLoad()
+    {
+        $loader = new ClosureLoader();
+
+        $route = new Route('/');
+        $routes = $loader->load(function () use ($route) {
+            $routes = new RouteCollection();
+
+            $routes->add('foo', $route);
+
+            return $routes;
+        });
+
+        $this->assertEquals($route, $routes->get('foo'), '->load() loads a \Closure resource');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php b/core/includes/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
new file mode 100644
index 0000000..74c3873
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
@@ -0,0 +1,52 @@
+<?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\Routing\Tests\Loader;
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Routing\Loader\PhpFileLoader;
+use Symfony\Component\Routing\Route;
+
+class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\FileLocator')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\PhpFileLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new PhpFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
+
+        $this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($loader->supports('foo.php', 'php'), '->supports() checks the resource type if specified');
+        $this->assertFalse($loader->supports('foo.php', 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    public function testLoadWithRoute()
+    {
+        $loader = new PhpFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validpattern.php');
+        $routes = $routeCollection->all();
+
+        $this->assertEquals(1, count($routes), 'One route is loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+        $route = $routes['blog_show'];
+        $this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/core/includes/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
new file mode 100644
index 0000000..e6f2176
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.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\Routing\Tests\Loader;
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Routing\Loader\XmlFileLoader;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Tests\Fixtures\CustomXmlFileLoader;
+
+class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\FileLocator')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\XmlFileLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new XmlFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
+
+        $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($loader->supports('foo.xml', 'xml'), '->supports() checks the resource type if specified');
+        $this->assertFalse($loader->supports('foo.xml', 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    public function testLoadWithRoute()
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validpattern.xml');
+        $routes = $routeCollection->all();
+
+        $this->assertEquals(1, count($routes), 'One route is loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+        $route = $routes['blog_show'];
+        $this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
+    }
+
+    public function testLoadWithImport()
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validresource.xml');
+        $routes = $routeCollection->all();
+
+        $this->assertEquals(1, count($routes), 'One route is loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+        $this->assertEquals('foo', $routes['blog_show']->getDefault('foo'));
+        $this->assertEquals('\d+', $routes['blog_show']->getRequirement('foo'));
+        $this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @dataProvider getPathsToInvalidFiles
+     */
+    public function testLoadThrowsExceptionWithInvalidFile($filePath)
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load($filePath);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @dataProvider getPathsToInvalidFiles
+     */
+    public function testLoadThrowsExceptionWithInvalidFileEvenWithoutSchemaValidation($filePath)
+    {
+        $loader = new CustomXmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load($filePath);
+    }
+
+    public function getPathsToInvalidFiles()
+    {
+        return array(array('nonvalidnode.xml'), array('nonvalidroute.xml'), array('nonvalid.xml'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage Document types are not allowed.
+     */
+    public function testDocTypeIsNotAllowed()
+    {
+        $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load('withdoctype.xml');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php b/core/includes/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
new file mode 100644
index 0000000..0c870e6
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
@@ -0,0 +1,115 @@
+<?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\Routing\Tests\Loader;
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Routing\Loader\YamlFileLoader;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Config\Resource\FileResource;
+
+class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        if (!class_exists('Symfony\Component\Config\FileLocator')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        if (!class_exists('Symfony\Component\Yaml\Yaml')) {
+            $this->markTestSkipped('The "Yaml" component is not available');
+        }
+    }
+
+    /**
+     * @covers Symfony\Component\Routing\Loader\YamlFileLoader::supports
+     */
+    public function testSupports()
+    {
+        $loader = new YamlFileLoader($this->getMock('Symfony\Component\Config\FileLocator'));
+
+        $this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable');
+        $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable');
+
+        $this->assertTrue($loader->supports('foo.yml', 'yaml'), '->supports() checks the resource type if specified');
+        $this->assertFalse($loader->supports('foo.yml', 'foo'), '->supports() checks the resource type if specified');
+    }
+
+    public function testLoadDoesNothingIfEmpty()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $collection = $loader->load('empty.yml');
+
+        $this->assertEquals(array(), $collection->all());
+        $this->assertEquals(array(new FileResource(realpath(__DIR__.'/../Fixtures/empty.yml'))), $collection->getResources());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testLoadThrowsExceptionIfNotAnArray()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load('nonvalid.yml');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testLoadThrowsExceptionIfArrayHasUnsupportedKeys()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load('nonvalidkeys.yml');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testLoadThrowsExceptionWhenIncomplete()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load('incomplete.yml');
+    }
+
+    public function testLoadWithPattern()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validpattern.yml');
+        $routes = $routeCollection->all();
+
+        $this->assertEquals(1, count($routes), 'One route is loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+        $route = $routes['blog_show'];
+        $this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
+    }
+
+    public function testLoadWithResource()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $routeCollection = $loader->load('validresource.yml');
+        $routes = $routeCollection->all();
+
+        $this->assertEquals(1, count($routes), 'One route is loaded');
+        $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
+        $this->assertEquals('foo', $routes['blog_show']->getDefault('foo'));
+        $this->assertEquals('\d+', $routes['blog_show']->getRequirement('foo'));
+        $this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testParseRouteThrowsExceptionWithMissingPattern()
+    {
+        $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
+        $loader->load('incomplete.yml');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php b/core/includes/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php
new file mode 100644
index 0000000..8848720
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php
@@ -0,0 +1,109 @@
+<?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\Routing\Tests\Matcher;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Matcher\ApacheUrlMatcher;
+
+class ApacheUrlMatcherTest extends \PHPUnit_Framework_TestCase
+{
+    protected $server;
+
+    protected function setUp()
+    {
+        $this->server = $_SERVER;
+    }
+
+    protected function tearDown()
+    {
+        $_SERVER = $this->server;
+    }
+
+    /**
+     * @dataProvider getMatchData
+     */
+    public function testMatch($name, $pathinfo, $server, $expect)
+    {
+        $collection = new RouteCollection();
+        $context = new RequestContext();
+        $matcher = new ApacheUrlMatcher($collection, $context);
+
+        $_SERVER = $server;
+
+        $result = $matcher->match($pathinfo, $server);
+        $this->assertSame(var_export($expect, true), var_export($result, true));
+    }
+
+    public function getMatchData()
+    {
+        return array(
+            array(
+                'Simple route',
+                '/hello/world',
+                array(
+                    '_ROUTING__route' => 'hello',
+                    '_ROUTING__controller' => 'AcmeBundle:Default:index',
+                    '_ROUTING_name' => 'world',
+                ),
+                array(
+                    '_route' => 'hello',
+                    '_controller' => 'AcmeBundle:Default:index',
+                    'name' => 'world',
+                ),
+            ),
+            array(
+                'Route with params and defaults',
+                '/hello/hugo',
+                array(
+                    '_ROUTING__route' => 'hello',
+                    '_ROUTING__controller' => 'AcmeBundle:Default:index',
+                    '_ROUTING_name' => 'hugo',
+                    '_ROUTING_DEFAULTS_name' => 'world',
+                ),
+                array(
+                    'name' => 'hugo',
+                    '_route' => 'hello',
+                    '_controller' => 'AcmeBundle:Default:index',
+                ),
+            ),
+            array(
+                'Route with defaults only',
+                '/hello',
+                array(
+                    '_ROUTING__route' => 'hello',
+                    '_ROUTING__controller' => 'AcmeBundle:Default:index',
+                    '_ROUTING_DEFAULTS_name' => 'world',
+                ),
+                array(
+                    'name' => 'world',
+                    '_route' => 'hello',
+                    '_controller' => 'AcmeBundle:Default:index',
+                ),
+            ),
+            array(
+                'REDIRECT_ envs',
+                '/hello/world',
+                array(
+                    'REDIRECT__ROUTING__route' => 'hello',
+                    'REDIRECT__ROUTING__controller' => 'AcmeBundle:Default:index',
+                    'REDIRECT__ROUTING_name' => 'world',
+                ),
+                array(
+                    '_route' => 'hello',
+                    '_controller' => 'AcmeBundle:Default:index',
+                    'name' => 'world',
+                ),
+            ),
+        );
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php b/core/includes/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php
new file mode 100644
index 0000000..2847564
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php
@@ -0,0 +1,130 @@
+<?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\Routing\Tests\Matcher\Dumper;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Matcher\Dumper\ApacheMatcherDumper;
+
+class ApacheMatcherDumperTest extends \PHPUnit_Framework_TestCase
+{
+    protected static $fixturesPath;
+
+    public static function setUpBeforeClass()
+    {
+        self::$fixturesPath = realpath(__DIR__.'/../../Fixtures/');
+    }
+
+    public function testDump()
+    {
+        $dumper = new ApacheMatcherDumper($this->getRouteCollection());
+
+        $this->assertStringEqualsFile(self::$fixturesPath.'/dumper/url_matcher1.apache', $dumper->dump(), '->dump() dumps basic routes to the correct apache format.');
+    }
+
+    /**
+     * @dataProvider provideEscapeFixtures
+     */
+    public function testEscapePattern($src, $dest, $char, $with, $message)
+    {
+        $r = new \ReflectionMethod(new ApacheMatcherDumper($this->getRouteCollection()), 'escape');
+        $r->setAccessible(true);
+        $this->assertEquals($dest, $r->invoke(null, $src, $char, $with), $message);
+    }
+
+    public function provideEscapeFixtures()
+    {
+        return array(
+            array('foo', 'foo', ' ', '-', 'Preserve string that should not be escaped'),
+            array('fo-o', 'fo-o', ' ', '-', 'Preserve string that should not be escaped'),
+            array('fo o', 'fo- o', ' ', '-', 'Escape special characters'),
+            array('fo-- o', 'fo--- o', ' ', '-', 'Escape special characters'),
+            array('fo- o', 'fo- o', ' ', '-', 'Do not escape already escaped string'),
+        );
+    }
+
+    public function testEscapeScriptName()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+        $dumper = new ApacheMatcherDumper($collection);
+        $this->assertStringEqualsFile(self::$fixturesPath.'/dumper/url_matcher2.apache', $dumper->dump(array('script_name' => 'ap p_d\ ev.php')));
+    }
+
+    private function getRouteCollection()
+    {
+        $collection = new RouteCollection();
+
+        // defaults and requirements
+        $collection->add('foo', new Route(
+            '/foo/{bar}',
+            array('def' => 'test'),
+            array('bar' => 'baz|symfony')
+        ));
+        // defaults parameters in pattern
+        $collection->add('foobar', new Route(
+            '/foo/{bar}',
+            array('bar' => 'toto')
+        ));
+        // method requirement
+        $collection->add('bar', new Route(
+            '/bar/{foo}',
+            array(),
+            array('_method' => 'GET|head')
+        ));
+        // method requirement (again)
+        $collection->add('baragain', new Route(
+            '/baragain/{foo}',
+            array(),
+            array('_method' => 'get|post')
+        ));
+        // simple
+        $collection->add('baz', new Route(
+            '/test/baz'
+        ));
+        // simple with extension
+        $collection->add('baz2', new Route(
+            '/test/baz.html'
+        ));
+        // trailing slash
+        $collection->add('baz3', new Route(
+            '/test/baz3/'
+        ));
+        // trailing slash with variable
+        $collection->add('baz4', new Route(
+            '/test/{foo}/'
+        ));
+        // trailing slash and safe method
+        $collection->add('baz5', new Route(
+            '/test/{foo}/',
+            array(),
+            array('_method' => 'get')
+        ));
+        // trailing slash and unsafe method
+        $collection->add('baz5unsafe', new Route(
+            '/testunsafe/{foo}/',
+            array(),
+            array('_method' => 'post')
+        ));
+        // complex
+        $collection->add('baz6', new Route(
+            '/test/baz',
+            array('foo' => 'bar baz')
+        ));
+        // space in path
+        $collection->add('baz7', new Route(
+            '/te st/baz'
+        ));
+
+        return $collection;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php b/core/includes/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php
new file mode 100644
index 0000000..ee11385
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php
@@ -0,0 +1,202 @@
+<?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\Routing\Tests\Matcher\Dumper;
+
+use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @expectedException \LogicException
+     */
+    public function testDumpWhenSchemeIsUsedWithoutAProperDumper()
+    {
+        $collection = new RouteCollection();
+        $collection->add('secure', new Route(
+            '/secure',
+            array(),
+            array('_scheme' => 'https')
+        ));
+        $dumper = new PhpMatcherDumper($collection);
+        $dumper->dump();
+    }
+
+    /**
+     * @dataProvider getRouteCollections
+     */
+    public function testDump(RouteCollection $collection, $fixture, $options = array())
+    {
+        $basePath = __DIR__.'/../../Fixtures/dumper/';
+
+        $dumper = new PhpMatcherDumper($collection);
+
+        $this->assertStringEqualsFile($basePath.$fixture, $dumper->dump($options), '->dump() correctly dumps routes as optimized PHP code.');
+    }
+
+    public function getRouteCollections()
+    {
+        /* test case 1 */
+
+        $collection = new RouteCollection();
+
+        $collection->add('overridden', new Route('/overridden'));
+
+        // defaults and requirements
+        $collection->add('foo', new Route(
+            '/foo/{bar}',
+            array('def' => 'test'),
+            array('bar' => 'baz|symfony')
+        ));
+        // method requirement
+        $collection->add('bar', new Route(
+            '/bar/{foo}',
+            array(),
+            array('_method' => 'GET|head')
+        ));
+        // GET method requirement automatically adds HEAD as valid
+        $collection->add('barhead', new Route(
+            '/barhead/{foo}',
+            array(),
+            array('_method' => 'GET')
+        ));
+        // simple
+        $collection->add('baz', new Route(
+            '/test/baz'
+        ));
+        // simple with extension
+        $collection->add('baz2', new Route(
+            '/test/baz.html'
+        ));
+        // trailing slash
+        $collection->add('baz3', new Route(
+            '/test/baz3/'
+        ));
+        // trailing slash with variable
+        $collection->add('baz4', new Route(
+            '/test/{foo}/'
+        ));
+        // trailing slash and method
+        $collection->add('baz5', new Route(
+            '/test/{foo}/',
+            array(),
+            array('_method' => 'post')
+        ));
+        // complex name
+        $collection->add('baz.baz6', new Route(
+            '/test/{foo}/',
+            array(),
+            array('_method' => 'put')
+        ));
+        // defaults without variable
+        $collection->add('foofoo', new Route(
+            '/foofoo',
+            array('def' => 'test')
+        ));
+        // pattern with quotes
+        $collection->add('quoter', new Route(
+            '/{quoter}',
+            array(),
+            array('quoter' => '[\']+')
+        ));
+        // space in pattern
+        $collection->add('space', new Route(
+            '/spa ce'
+        ));
+
+        // prefixes
+        $collection1 = new RouteCollection();
+        $collection1->add('overridden', new Route('/overridden1'));
+        $collection1->add('foo1', new Route('/{foo}'));
+        $collection1->add('bar1', new Route('/{bar}'));
+        $collection2 = new RouteCollection();
+        $collection2->addCollection($collection1, '/b\'b');
+        $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');
+
+        // overridden through addCollection() and multiple sub-collections with no own prefix
+        $collection1 = new RouteCollection();
+        $collection1->add('overridden2', new Route('/old'));
+        $collection1->add('helloWorld', new Route('/hello/{who}', array('who' => 'World!')));
+        $collection2 = new RouteCollection();
+        $collection3 = new RouteCollection();
+        $collection3->add('overridden2', new Route('/new'));
+        $collection3->add('hey', new Route('/hey/'));
+        $collection1->addCollection($collection2);
+        $collection2->addCollection($collection3);
+        $collection->addCollection($collection1, '/multi');
+
+        // "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}');
+
+        // route between collections
+        $collection->add('ababa', new Route('/ababa'));
+
+        // collection with static prefix but only one route
+        $collection1 = new RouteCollection();
+        $collection1->add('foo4', new Route('/{foo}'));
+        $collection->addCollection($collection1, '/aba');
+
+        // multiple sub-collections with a single route and a prefix each
+        $collection1 = new RouteCollection();
+        $collection1->add('a', new Route('/a...'));
+        $collection2 = new RouteCollection();
+        $collection2->add('b', new Route('/{var}'));
+        $collection3 = new RouteCollection();
+        $collection3->add('c', new Route('/{var}'));
+
+        $collection1->addCollection($collection2, '/b');
+        $collection2->addCollection($collection3, '/c');
+        $collection->addCollection($collection1, '/a');
+
+        /* test case 2 */
+
+        $redirectCollection = clone $collection;
+
+        // force HTTPS redirection
+        $redirectCollection->add('secure', new Route(
+            '/secure',
+            array(),
+            array('_scheme' => 'https')
+        ));
+
+        // force HTTP redirection
+        $redirectCollection->add('nonsecure', new Route(
+            '/nonsecure',
+            array(),
+            array('_scheme' => 'http')
+        ));
+
+        /* test case 3 */
+
+        $rootprefixCollection = new RouteCollection();
+        $rootprefixCollection->add('static', new Route('/test'));
+        $rootprefixCollection->add('dynamic', new Route('/{var}'));
+        $rootprefixCollection->addPrefix('rootprefix');
+
+        return array(
+           array($collection, 'url_matcher1.php', array()),
+           array($redirectCollection, 'url_matcher2.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')),
+           array($rootprefixCollection, 'url_matcher3.php', array())
+        );
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/core/includes/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php
new file mode 100644
index 0000000..b8ab264
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php
@@ -0,0 +1,58 @@
+<?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\Routing\Tests\Matcher;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+
+class RedirectableUrlMatcherTest extends \PHPUnit_Framework_TestCase
+{
+    public function testRedirectWhenNoSlash()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/'));
+
+        $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
+        $matcher->expects($this->once())->method('redirect');
+        $matcher->match('/foo');
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testRedirectWhenNoSlashForNonSafeMethod()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/'));
+
+        $context = new RequestContext();
+        $context->setMethod('POST');
+        $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, $context));
+        $matcher->match('/foo');
+    }
+
+    public function testSchemeRedirect()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_scheme' => 'https')));
+
+        $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
+        $matcher
+            ->expects($this->once())
+            ->method('redirect')
+            ->with('/foo', 'foo', 'https')
+            ->will($this->returnValue(array('_route' => 'foo')))
+        ;
+        $matcher->match('/foo');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php b/core/includes/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php
new file mode 100644
index 0000000..7f8725c
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php
@@ -0,0 +1,60 @@
+<?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\Routing\Tests\Matcher;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Matcher\TraceableUrlMatcher;
+
+class TraceableUrlMatcherTest extends \PHPUnit_Framework_TestCase
+{
+    public function test()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_method' => 'POST')));
+        $coll->add('bar', new Route('/bar/{id}', array(), array('id' => '\d+')));
+        $coll->add('bar1', new Route('/bar/{name}', array(), array('id' => '\w+', '_method' => 'POST')));
+
+        $context = new RequestContext();
+
+        $matcher = new TraceableUrlMatcher($coll, $context);
+        $traces = $matcher->getTraces('/babar');
+        $this->assertEquals(array(0, 0, 0), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/foo');
+        $this->assertEquals(array(1, 0, 0), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/bar/12');
+        $this->assertEquals(array(0, 2), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/bar/dd');
+        $this->assertEquals(array(0, 1, 1), $this->getLevels($traces));
+
+        $context->setMethod('POST');
+        $traces = $matcher->getTraces('/foo');
+        $this->assertEquals(array(2), $this->getLevels($traces));
+
+        $traces = $matcher->getTraces('/bar/dd');
+        $this->assertEquals(array(0, 1, 2), $this->getLevels($traces));
+    }
+
+    public function getLevels($traces)
+    {
+        $levels = array();
+        foreach ($traces as $trace) {
+            $levels[] = $trace['level'];
+        }
+
+        return $levels;
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/core/includes/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
new file mode 100644
index 0000000..0927c75
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
@@ -0,0 +1,331 @@
+<?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\Routing\Tests\Matcher;
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RequestContext;
+
+class UrlMatcherTest extends \PHPUnit_Framework_TestCase
+{
+    public function testNoMethodSoAllowed()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $matcher->match('/foo');
+    }
+
+    public function testMethodNotAllowed()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_method' => 'post')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        try {
+            $matcher->match('/foo');
+            $this->fail();
+        } catch (MethodNotAllowedException $e) {
+            $this->assertEquals(array('POST'), $e->getAllowedMethods());
+        }
+    }
+
+    public function testHeadAllowedWhenRequirementContainsGet()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_method' => 'get')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext('', 'head'));
+        $matcher->match('/foo');
+    }
+
+    public function testMethodNotAllowedAggregatesAllowedMethods()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo1', new Route('/foo', array(), array('_method' => 'post')));
+        $coll->add('foo2', new Route('/foo', array(), array('_method' => 'put|delete')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        try {
+            $matcher->match('/foo');
+            $this->fail();
+        } catch (MethodNotAllowedException $e) {
+            $this->assertEquals(array('POST', 'PUT', 'DELETE'), $e->getAllowedMethods());
+        }
+    }
+
+    public function testMatch()
+    {
+        // test the patterns are matched and parameters are returned
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo/{bar}'));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        try {
+            $matcher->match('/no-match');
+            $this->fail();
+        } catch (ResourceNotFoundException $e) {}
+        $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz'), $matcher->match('/foo/baz'));
+
+        // test that defaults are merged
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo/{bar}', array('def' => 'test')));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz', 'def' => 'test'), $matcher->match('/foo/baz'));
+
+        // test that route "method" is ignored if no method is given in the context
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo', array(), array('_method' => 'GET|head')));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertInternalType('array', $matcher->match('/foo'));
+
+        // route does not match with POST method context
+        $matcher = new UrlMatcher($collection, new RequestContext('', 'post'));
+        try {
+            $matcher->match('/foo');
+            $this->fail();
+        } catch (MethodNotAllowedException $e) {}
+
+        // route does match with GET or HEAD method context
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertInternalType('array', $matcher->match('/foo'));
+        $matcher = new UrlMatcher($collection, new RequestContext('', 'head'));
+        $this->assertInternalType('array', $matcher->match('/foo'));
+
+        // route with an optional variable as the first segment
+        $collection = new RouteCollection();
+        $collection->add('bar', new Route('/{bar}/foo', array('bar' => 'bar'), array('bar' => 'foo|bar')));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'bar', 'bar' => 'bar'), $matcher->match('/bar/foo'));
+        $this->assertEquals(array('_route' => 'bar', 'bar' => 'foo'), $matcher->match('/foo/foo'));
+
+        $collection = new RouteCollection();
+        $collection->add('bar', new Route('/{bar}', array('bar' => 'bar'), array('bar' => 'foo|bar')));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'bar', 'bar' => 'foo'), $matcher->match('/foo'));
+        $this->assertEquals(array('_route' => 'bar', 'bar' => 'bar'), $matcher->match('/'));
+
+        // route with only optional variables
+        $collection = new RouteCollection();
+        $collection->add('bar', new Route('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar'), array()));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'bar', 'foo' => 'foo', 'bar' => 'bar'), $matcher->match('/'));
+        $this->assertEquals(array('_route' => 'bar', 'foo' => 'a', 'bar' => 'bar'), $matcher->match('/a'));
+        $this->assertEquals(array('_route' => 'bar', 'foo' => 'a', 'bar' => 'b'), $matcher->match('/a/b'));
+    }
+
+    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');
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'foo', 'foo' => 'foo'), $matcher->match('/a/b/foo'));
+    }
+
+    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}');
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_locale' => 'fr', '_route' => 'foo', 'foo' => 'foo'), $matcher->match('/fr/b/foo'));
+    }
+
+    public function testMatchNonAlpha()
+    {
+        $collection = new RouteCollection();
+        $chars = '!"$%éà &\'()*+,./:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[]^_`abcdefghijklmnopqrstuvwxyz{|}~-';
+        $collection->add('foo', new Route('/{foo}/bar', array(), array('foo' => '['.preg_quote($chars).']+')));
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'foo', 'foo' => $chars), $matcher->match('/'.rawurlencode($chars).'/bar'));
+        $this->assertEquals(array('_route' => 'foo', 'foo' => $chars), $matcher->match('/'.strtr($chars, array('%' => '%25')).'/bar'));
+    }
+
+    public function testMatchWithDotMetacharacterInRequirements()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/{foo}/bar', array(), array('foo' => '.+')));
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        $this->assertEquals(array('_route' => 'foo', 'foo' => "\n"), $matcher->match('/'.urlencode("\n").'/bar'), 'linefeed character is matched');
+    }
+
+    public function testMatchOverriddenRoute()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('foo', new Route('/foo1'));
+
+        $collection->addCollection($collection1);
+
+        $matcher = new UrlMatcher($collection, new RequestContext());
+
+        $this->assertEquals(array('_route' => 'foo'), $matcher->match('/foo1'));
+        $this->setExpectedException('Symfony\Component\Routing\Exception\ResourceNotFoundException');
+        $this->assertEquals(array(), $matcher->match('/foo'));
+    }
+
+    public function testMatchRegression()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/{foo}'));
+        $coll->add('bar', new Route('/foo/bar/{foo}'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('foo' => 'bar', '_route' => 'bar'), $matcher->match('/foo/bar/bar'));
+
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/{bar}'));
+        $matcher = new UrlMatcher($collection, new RequestContext());
+        try {
+            $matcher->match('/');
+            $this->fail();
+        } catch (ResourceNotFoundException $e) {
+        }
+    }
+
+    public function testDefaultRequirementForOptionalVariables()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{page}.{_format}', array('page' => 'index', '_format' => 'html')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('page' => 'my-page', '_format' => 'xml', '_route' => 'test'), $matcher->match('/my-page.xml'));
+    }
+
+    public function testMatchingIsEager()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{foo}-{bar}-', array(), array('foo' => '.+', 'bar' => '.+')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('foo' => 'text1-text2-text3', 'bar' => 'text4', '_route' => 'test'), $matcher->match('/text1-text2-text3-text4-'));
+    }
+
+    public function testAdjacentVariables()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => 'y|Y')));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        // 'w' eagerly matches as much as possible and the other variables match the remaining chars.
+        // This also shows that the variables w-z must all exclude the separating char (the dot '.' in this case) by default requirement.
+        // Otherwise they would also comsume '.xml' and _format would never match as it's an optional variable.
+        $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'Y', 'z' => 'Z','_format' => 'xml', '_route' => 'test'), $matcher->match('/wwwwwxYZ.xml'));
+        // As 'y' has custom requirement and can only be of value 'y|Y', it will leave  'ZZZ' to variable z.
+        // So with carefully chosen requirements adjacent variables, can be useful.
+        $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'y', 'z' => 'ZZZ','_format' => 'html', '_route' => 'test'), $matcher->match('/wwwwwxyZZZ'));
+        // z and _format are optional.
+        $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'y', 'z' => 'default-z','_format' => 'html', '_route' => 'test'), $matcher->match('/wwwwwxy'));
+
+        $this->setExpectedException('Symfony\Component\Routing\Exception\ResourceNotFoundException');
+        $matcher->match('/wxy.html');
+    }
+
+    public function testOptionalVariableWithNoRealSeparator()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/get{what}', array('what' => 'All')));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $this->assertEquals(array('what' => 'All', '_route' => 'test'), $matcher->match('/get'));
+        $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSites'));
+
+        // Usually the character in front of an optional parameter can be left out, e.g. with pattern '/get/{what}' just '/get' would match.
+        // But here the 't' in 'get' is not a separating character, so it makes no sense to match without it.
+        $this->setExpectedException('Symfony\Component\Routing\Exception\ResourceNotFoundException');
+        $matcher->match('/ge');
+    }
+
+    public function testRequiredVariableWithNoRealSeparator()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/get{what}Suffix'));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSitesSuffix'));
+    }
+
+    public function testDefaultRequirementOfVariable()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{page}.{_format}'));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $this->assertEquals(array('page' => 'index', '_format' => 'mobile.html', '_route' => 'test'), $matcher->match('/index.mobile.html'));
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testDefaultRequirementOfVariableDisallowsSlash()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{page}.{_format}'));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $matcher->match('/index.sl/ash');
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testDefaultRequirementOfVariableDisallowsNextSeparator()
+    {
+        $coll = new RouteCollection();
+        $coll->add('test', new Route('/{page}.{_format}', array(), array('_format' => 'html|xml')));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+
+        $matcher->match('/do.t.html');
+    }
+
+    /**
+     * @expectedException Symfony\Component\Routing\Exception\ResourceNotFoundException
+     */
+    public function testSchemeRequirement()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo', array(), array('_scheme' => 'https')));
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $matcher->match('/foo');
+    }
+
+    public function testDecodeOnce()
+    {
+        $coll = new RouteCollection();
+        $coll->add('foo', new Route('/foo/{foo}'));
+
+        $matcher = new UrlMatcher($coll, new RequestContext());
+        $this->assertEquals(array('foo' => 'bar%23', '_route' => 'foo'), $matcher->match('/foo/bar%2523'));
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/RouteCollectionTest.php b/core/includes/Symfony/Component/Routing/Tests/RouteCollectionTest.php
new file mode 100644
index 0000000..792e65d
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/RouteCollectionTest.php
@@ -0,0 +1,291 @@
+<?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\Routing\Tests;
+
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Config\Resource\FileResource;
+
+class RouteCollectionTest extends \PHPUnit_Framework_TestCase
+{
+    public function testRoute()
+    {
+        $collection = new RouteCollection();
+        $route = new Route('/foo');
+        $collection->add('foo', $route);
+        $this->assertEquals(array('foo' => $route), $collection->all(), '->add() adds a route');
+        $this->assertEquals($route, $collection->get('foo'), '->get() returns a route by name');
+        $this->assertNull($collection->get('bar'), '->get() returns null if a route does not exist');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testAddInvalidRoute()
+    {
+        $collection = new RouteCollection();
+        $route = new Route('/foo');
+        $collection->add('f o o', $route);
+    }
+
+    public function testOverriddenRoute()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+        $collection->add('foo', new Route('/foo1'));
+
+        $this->assertEquals('/foo1', $collection->get('foo')->getPattern());
+    }
+
+    public function testDeepOverriddenRoute()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection1->add('foo', new Route('/foo1'));
+
+        $collection2 = new RouteCollection();
+        $collection2->add('foo', new Route('/foo2'));
+
+        $collection1->addCollection($collection2);
+        $collection->addCollection($collection1);
+
+        $this->assertEquals('/foo2', $collection1->get('foo')->getPattern());
+        $this->assertEquals('/foo2', $collection->get('foo')->getPattern());
+    }
+
+    public function testIteratorWithOverriddenRoutes()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection->addCollection($collection1);
+        $collection1->add('foo', new Route('/foo1'));
+
+        $this->assertEquals('/foo1', $this->getFirstNamedRoute($collection, 'foo')->getPattern());
+    }
+
+    public function testCount()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', new Route('/foo'));
+
+        $collection1 = new RouteCollection();
+        $collection->addCollection($collection1);
+        $collection1->add('foo1', new Route('/foo1'));
+
+        $this->assertCount(2, $collection);
+    }
+
+    protected function getFirstNamedRoute(RouteCollection $routeCollection, $name)
+    {
+        foreach ($routeCollection as $key => $route) {
+            if ($route instanceof RouteCollection) {
+                return $this->getFirstNamedRoute($route, $name);
+            }
+
+            if ($name === $key) {
+                return $route;
+            }
+        }
+    }
+
+    public function testAddCollection()
+    {
+        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'));
+        $collection1 = new RouteCollection();
+        $collection1->add('foo', $foo1 = new Route('/foo1'));
+        $collection1->add('bar', $bar1 = new Route('/bar1'));
+        $collection->addCollection($collection1);
+        $this->assertEquals(array('foo' => $foo1, 'bar' => $bar1), $collection->all(), '->addCollection() adds routes from another collection');
+
+        $collection = new RouteCollection();
+        $collection->add('foo', $foo = new Route('/foo'));
+        $collection1 = new RouteCollection();
+        $collection1->add('foo', $foo1 = new Route('/foo1'));
+        $collection->addCollection($collection1, '/{foo}', array('foo' => 'foo'), array('foo' => '\d+'), array('foo' => 'bar'));
+        $this->assertEquals('/{foo}/foo1', $collection->get('foo')->getPattern(), '->addCollection() can add a prefix to all merged routes');
+        $this->assertEquals(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'
+        );
+
+        $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');
+    }
+
+    public function testAddPrefix()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', $foo = new Route('/foo'));
+        $collection->add('bar', $bar = new Route('/bar'));
+        $collection->addPrefix('/{admin}', array('admin' => 'admin'), array('admin' => '\d+'), array('foo' => 'bar'));
+        $this->assertEquals('/{admin}/foo', $collection->get('foo')->getPattern(), '->addPrefix() adds a prefix to all routes');
+        $this->assertEquals('/{admin}/bar', $collection->get('bar')->getPattern(), '->addPrefix() adds a prefix to all routes');
+        $this->assertEquals(array('admin' => 'admin'), $collection->get('foo')->getDefaults(), '->addPrefix() adds a prefix to all routes');
+        $this->assertEquals(array('admin' => 'admin'), $collection->get('bar')->getDefaults(), '->addPrefix() adds a prefix to all routes');
+        $this->assertEquals(array('admin' => '\d+'), $collection->get('foo')->getRequirements(), '->addPrefix() adds a prefix to all routes');
+        $this->assertEquals(array('admin' => '\d+'), $collection->get('bar')->getRequirements(), '->addPrefix() adds a prefix 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');
+    }
+
+    public function testAddPrefixOverridesDefaultsAndRequirements()
+    {
+        $collection = new RouteCollection();
+        $collection->add('foo', $foo = new Route('/foo'));
+        $collection->add('bar', $bar = new Route('/bar', array(), array('_scheme' => 'http')));
+        $collection->addPrefix('/admin', array(), array('_scheme' => 'https'));
+
+        $this->assertEquals('https', $collection->get('foo')->getRequirement('_scheme'), '->addPrefix() overrides existing requirements');
+        $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')) {
+            $this->markTestSkipped('The "Config" component is not available');
+        }
+
+        $collection = new RouteCollection();
+        $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml'));
+        $this->assertEquals(array($foo), $collection->getResources(), '->addResources() adds a resource');
+    }
+
+    public function testUniqueRouteWithGivenName()
+    {
+        $collection1 = new RouteCollection();
+        $collection1->add('foo', new Route('/old'));
+        $collection2 = new RouteCollection();
+        $collection3 = new RouteCollection();
+        $collection3->add('foo', $new = new Route('/new'));
+
+        $collection1->addCollection($collection2);
+        $collection2->addCollection($collection3);
+
+        $collection1->add('stay', new Route('/stay'));
+
+        $iterator = $collection1->getIterator();
+
+        $this->assertSame($new, $collection1->get('foo'), '->get() returns new route that overrode previous one');
+        // size of 2 because collection1 contains collection2 and /stay but not /old anymore
+        $this->assertCount(2, $iterator, '->addCollection() removes previous routes when adding new routes with the same name');
+        $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $iterator->current(), '->getIterator returns both Routes and RouteCollections');
+        $iterator->next();
+        $this->assertInstanceOf('Symfony\Component\Routing\Route', $iterator->current(), '->getIterator returns both Routes and RouteCollections');
+    }
+
+    public function testGet()
+    {
+        $collection1 = new RouteCollection();
+        $collection1->add('a', $a = new Route('/a'));
+        $collection2 = new RouteCollection();
+        $collection2->add('b', $b = new Route('/b'));
+        $collection1->addCollection($collection2);
+
+        $this->assertSame($b, $collection1->get('b'), '->get() returns correct route in child collection');
+        $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');
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testCannotSelfJoinCollection()
+    {
+        $collection = new RouteCollection();
+
+        $collection->addCollection($collection);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testCannotAddExistingCollectionToTree()
+    {
+        $collection1 = new RouteCollection();
+        $collection2 = new RouteCollection();
+        $collection3 = new RouteCollection();
+
+        $collection1->addCollection($collection2);
+        $collection1->addCollection($collection3);
+        $collection2->addCollection($collection3);
+    }
+
+    public function testPatternDoesNotChangeWhenDefinitionOrderChanges()
+    {
+        $collection1 = new RouteCollection();
+        $collection1->add('a', new Route('/a...'));
+        $collection2 = new RouteCollection();
+        $collection2->add('b', new Route('/b...'));
+        $collection3 = new RouteCollection();
+        $collection3->add('c', new Route('/c...'));
+
+        $rootCollection_A = new RouteCollection();
+        $collection2->addCollection($collection3, '/c');
+        $collection1->addCollection($collection2, '/b');
+        $rootCollection_A->addCollection($collection1, '/a');
+
+        // above should mean the same as below
+
+        $collection1 = new RouteCollection();
+        $collection1->add('a', new Route('/a...'));
+        $collection2 = new RouteCollection();
+        $collection2->add('b', new Route('/b...'));
+        $collection3 = new RouteCollection();
+        $collection3->add('c', new Route('/c...'));
+
+        $rootCollection_B = new RouteCollection();
+        $collection1->addCollection($collection2, '/b');
+        $collection2->addCollection($collection3, '/c');
+        $rootCollection_B->addCollection($collection1, '/a');
+
+        // test it now
+
+        $this->assertEquals($rootCollection_A, $rootCollection_B);
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/RouteCompilerTest.php b/core/includes/Symfony/Component/Routing/Tests/RouteCompilerTest.php
new file mode 100644
index 0000000..1c87ca3
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/RouteCompilerTest.php
@@ -0,0 +1,182 @@
+<?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\Routing\Tests;
+
+use Symfony\Component\Routing\Route;
+
+class RouteCompilerTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @dataProvider provideCompileData
+     */
+    public function testCompile($name, $arguments, $prefix, $regex, $variables, $tokens)
+    {
+        $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
+        $route = $r->newInstanceArgs($arguments);
+
+        $compiled = $route->compile();
+        $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
+        $this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)');
+        $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
+        $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
+    }
+
+    public function provideCompileData()
+    {
+        return array(
+            array(
+                'Static route',
+                array('/foo'),
+                '/foo', '#^/foo$#s', array(), array(
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with a variable',
+                array('/foo/{bar}'),
+                '/foo', '#^/foo/(?<bar>[^/]++)$#s', array('bar'), array(
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with a variable that has a default value',
+                array('/foo/{bar}', array('bar' => 'bar')),
+                '/foo', '#^/foo(?:/(?<bar>[^/]++))?$#s', array('bar'), array(
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with several variables',
+                array('/foo/{bar}/{foobar}'),
+                '/foo', '#^/foo/(?<bar>[^/]++)/(?<foobar>[^/]++)$#s', array('bar', 'foobar'), array(
+                    array('variable', '/', '[^/]++', 'foobar'),
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with several variables that have default values',
+                array('/foo/{bar}/{foobar}', array('bar' => 'bar', 'foobar' => '')),
+                '/foo', '#^/foo(?:/(?<bar>[^/]++)(?:/(?<foobar>[^/]++))?)?$#s', array('bar', 'foobar'), array(
+                    array('variable', '/', '[^/]++', 'foobar'),
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with several variables but some of them have no default values',
+                array('/foo/{bar}/{foobar}', array('bar' => 'bar')),
+                '/foo', '#^/foo/(?<bar>[^/]++)/(?<foobar>[^/]++)$#s', array('bar', 'foobar'), array(
+                    array('variable', '/', '[^/]++', 'foobar'),
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('text', '/foo'),
+                )),
+
+            array(
+                'Route with an optional variable as the first segment',
+                array('/{bar}', array('bar' => 'bar')),
+                '', '#^/(?<bar>[^/]++)?$#s', array('bar'), array(
+                    array('variable', '/', '[^/]++', 'bar'),
+                )),
+
+            array(
+                'Route with a requirement of 0',
+                array('/{bar}', array('bar' => null), array('bar' => '0')),
+                '', '#^/(?<bar>0)?$#s', array('bar'), array(
+                    array('variable', '/', '0', 'bar'),
+                )),
+
+            array(
+                'Route with an optional variable as the first segment with requirements',
+                array('/{bar}', array('bar' => 'bar'), array('bar' => '(foo|bar)')),
+                '', '#^/(?<bar>(foo|bar))?$#s', array('bar'), array(
+                    array('variable', '/', '(foo|bar)', 'bar'),
+                )),
+
+            array(
+                'Route with only optional variables',
+                array('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar')),
+                '', '#^/(?<foo>[^/]++)?(?:/(?<bar>[^/]++))?$#s', array('foo', 'bar'), array(
+                    array('variable', '/', '[^/]++', 'bar'),
+                    array('variable', '/', '[^/]++', 'foo'),
+                )),
+
+            array(
+                'Route with a variable in last position',
+                array('/foo-{bar}'),
+                '/foo', '#^/foo\-(?<bar>[^/]++)$#s', array('bar'), array(
+                array('variable', '-', '[^/]++', 'bar'),
+                array('text', '/foo'),
+            )),
+
+            array(
+                'Route with nested placeholders',
+                array('/{static{var}static}'),
+                '/{static', '#^/\{static(?<var>[^/]+)static\}$#s', array('var'), array(
+                array('text', 'static}'),
+                array('variable', '', '[^/]+', 'var'),
+                array('text', '/{static'),
+            )),
+
+            array(
+                'Route without separator between variables',
+                array('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => '(y|Y)')),
+                '', '#^/(?<w>[^/\.]+)(?<x>[^/\.]+)(?<y>(y|Y))(?:(?<z>[^/\.]++)(?:\.(?<_format>[^/]++))?)?$#s', array('w', 'x', 'y', 'z', '_format'), array(
+                array('variable', '.', '[^/]++', '_format'),
+                array('variable', '', '[^/\.]++', 'z'),
+                array('variable', '', '(y|Y)', 'y'),
+                array('variable', '', '[^/\.]+', 'x'),
+                array('variable', '/', '[^/\.]+', 'w'),
+            )),
+
+            array(
+                'Route with a format',
+                array('/foo/{bar}.{_format}'),
+                '/foo', '#^/foo/(?<bar>[^/\.]++)\.(?<_format>[^/]++)$#s', array('bar', '_format'), array(
+                array('variable', '.', '[^/]++', '_format'),
+                array('variable', '/', '[^/\.]++', 'bar'),
+                array('text', '/foo'),
+            )),
+        );
+    }
+
+    /**
+     * @expectedException \LogicException
+     */
+    public function testRouteWithSameVariableTwice()
+    {
+        $route = new Route('/{name}/{name}');
+
+        $compiled = $route->compile();
+    }
+
+    /**
+     * @dataProvider getNumericVariableNames
+     * @expectedException \DomainException
+     */
+    public function testRouteWithNumericVariableName($name)
+    {
+        $route = new Route('/{'. $name . '}');
+        $route->compile();
+    }
+
+    public function getNumericVariableNames()
+    {
+        return array(
+           array('09'),
+           array('123'),
+           array('1e2')
+        );
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/RouteTest.php b/core/includes/Symfony/Component/Routing/Tests/RouteTest.php
new file mode 100644
index 0000000..8a42b72
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/RouteTest.php
@@ -0,0 +1,130 @@
+<?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\Routing\Tests;
+
+use Symfony\Component\Routing\Route;
+
+class RouteTest extends \PHPUnit_Framework_TestCase
+{
+    public function testConstructor()
+    {
+        $route = new Route('/{foo}', array('foo' => 'bar'), array('foo' => '\d+'), array('foo' => 'bar'));
+        $this->assertEquals('/{foo}', $route->getPattern(), '__construct() takes a pattern as its first argument');
+        $this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '__construct() takes defaults as its second argument');
+        $this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '__construct() takes requirements as its third argument');
+        $this->assertEquals('bar', $route->getOption('foo'), '__construct() takes options as its fourth argument');
+    }
+
+    public function testPattern()
+    {
+        $route = new Route('/{foo}');
+        $route->setPattern('/{bar}');
+        $this->assertEquals('/{bar}', $route->getPattern(), '->setPattern() sets the pattern');
+        $route->setPattern('');
+        $this->assertEquals('/', $route->getPattern(), '->setPattern() adds a / at the beginning of the pattern if needed');
+        $route->setPattern('bar');
+        $this->assertEquals('/bar', $route->getPattern(), '->setPattern() adds a / at the beginning of the pattern if needed');
+        $this->assertEquals($route, $route->setPattern(''), '->setPattern() implements a fluent interface');
+    }
+
+    public function testOptions()
+    {
+        $route = new Route('/{foo}');
+        $route->setOptions(array('foo' => 'bar'));
+        $this->assertEquals(array_merge(array(
+        'compiler_class'     => 'Symfony\\Component\\Routing\\RouteCompiler',
+        ), array('foo' => 'bar')), $route->getOptions(), '->setOptions() sets the options');
+        $this->assertEquals($route, $route->setOptions(array()), '->setOptions() implements a fluent interface');
+
+        $route->setOptions(array('foo' => 'foo'));
+        $route->addOptions(array('bar' => 'bar'));
+        $this->assertEquals($route, $route->addOptions(array()), '->addOptions() implements a fluent interface');
+        $this->assertEquals(array('foo' => 'foo', 'bar' => 'bar', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'), $route->getOptions(), '->addDefaults() keep previous defaults');
+    }
+
+    public function testDefaults()
+    {
+        $route = new Route('/{foo}');
+        $route->setDefaults(array('foo' => 'bar'));
+        $this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '->setDefaults() sets the defaults');
+        $this->assertEquals($route, $route->setDefaults(array()), '->setDefaults() implements a fluent interface');
+
+        $route->setDefault('foo', 'bar');
+        $this->assertEquals('bar', $route->getDefault('foo'), '->setDefault() sets a default value');
+
+        $route->setDefault('foo2', 'bar2');
+        $this->assertEquals('bar2', $route->getDefault('foo2'), '->getDefault() return the default value');
+        $this->assertNull($route->getDefault('not_defined'), '->getDefault() return null if default value is not setted');
+
+        $route->setDefault('_controller', $closure = function () { return 'Hello'; });
+        $this->assertEquals($closure, $route->getDefault('_controller'), '->setDefault() sets a default value');
+
+        $route->setDefaults(array('foo' => 'foo'));
+        $route->addDefaults(array('bar' => 'bar'));
+        $this->assertEquals($route, $route->addDefaults(array()), '->addDefaults() implements a fluent interface');
+        $this->assertEquals(array('foo' => 'foo', 'bar' => 'bar'), $route->getDefaults(), '->addDefaults() keep previous defaults');
+    }
+
+    public function testRequirements()
+    {
+        $route = new Route('/{foo}');
+        $route->setRequirements(array('foo' => '\d+'));
+        $this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '->setRequirements() sets the requirements');
+        $this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() returns a requirement');
+        $this->assertNull($route->getRequirement('bar'), '->getRequirement() returns null if a requirement is not defined');
+        $route->setRequirements(array('foo' => '^\d+$'));
+        $this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() removes ^ and $ from the pattern');
+        $this->assertEquals($route, $route->setRequirements(array()), '->setRequirements() implements a fluent interface');
+
+        $route->setRequirements(array('foo' => '\d+'));
+        $route->addRequirements(array('bar' => '\d+'));
+        $this->assertEquals($route, $route->addRequirements(array()), '->addRequirements() implements a fluent interface');
+        $this->assertEquals(array('foo' => '\d+', 'bar' => '\d+'), $route->getRequirements(), '->addRequirement() keep previous requirements');
+    }
+
+    public function testRequirement()
+    {
+        $route = new Route('/{foo}');
+        $route->setRequirement('foo', '^\d+$');
+        $this->assertEquals('\d+', $route->getRequirement('foo'), '->setRequirement() removes ^ and $ from the pattern');
+    }
+
+    /**
+     * @dataProvider getInvalidRequirements
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetInvalidRequirement($req)
+    {
+        $route = new Route('/{foo}');
+        $route->setRequirement('foo', $req);
+    }
+
+    public function getInvalidRequirements()
+    {
+        return array(
+           array(''),
+           array(array()),
+           array('^$'),
+           array('^'),
+           array('$')
+        );
+    }
+
+    public function testCompile()
+    {
+        $route = new Route('/{foo}');
+        $this->assertInstanceOf('Symfony\Component\Routing\CompiledRoute', $compiled = $route->compile(), '->compile() returns a compiled route');
+        $this->assertSame($compiled, $route->compile(), '->compile() only compiled the route once if unchanged');
+        $route->setRequirement('foo', '.*');
+        $this->assertNotSame($compiled, $route->compile(), '->compile() recompiles if the route was modified');
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/Tests/bootstrap.php b/core/includes/Symfony/Component/Routing/Tests/bootstrap.php
new file mode 100644
index 0000000..23303c0
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/Tests/bootstrap.php
@@ -0,0 +1,22 @@
+<?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.
+ */
+
+spl_autoload_register(function ($class) {
+    if (0 === strpos(ltrim($class, '/'), 'Symfony\Component\Routing')) {
+        if (file_exists($file = __DIR__.'/../'.substr(str_replace('\\', '/', $class), strlen('Symfony\Component\Routing')).'.php')) {
+            require_once $file;
+        }
+    }
+});
+
+if (file_exists($loader = __DIR__.'/../vendor/autoload.php')) {
+    require_once $loader;
+}
diff --git a/core/includes/Symfony/Component/Routing/composer.json b/core/includes/Symfony/Component/Routing/composer.json
new file mode 100644
index 0000000..d3f653b
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/composer.json
@@ -0,0 +1,42 @@
+{
+    "name": "symfony/routing",
+    "type": "library",
+    "description": "Symfony Routing 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/config": "2.2.*",
+        "symfony/yaml": "2.2.*",
+        "symfony/http-kernel": "2.2.*",
+        "doctrine/common": ">=2.2,<2.4-dev"
+    },
+    "suggest": {
+        "symfony/config": "2.2.*",
+        "symfony/yaml": "2.2.*",
+        "doctrine/common": ">=2.2,<2.4-dev"
+    },
+    "autoload": {
+        "psr-0": { "Symfony\\Component\\Routing\\": "" }
+    },
+    "target-dir": "Symfony/Component/Routing",
+    "minimum-stability": "dev",
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.2-dev"
+        }
+    }
+}
diff --git a/core/includes/Symfony/Component/Routing/phpunit.xml.dist b/core/includes/Symfony/Component/Routing/phpunit.xml.dist
new file mode 100644
index 0000000..6d25ebf
--- /dev/null
+++ b/core/includes/Symfony/Component/Routing/phpunit.xml.dist
@@ -0,0 +1,29 @@
+<?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="Tests/bootstrap.php"
+>
+    <testsuites>
+        <testsuite name="Symfony Routing Component Test Suite">
+            <directory>./Tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./</directory>
+            <exclude>
+                <directory>./vendor</directory>
+                <directory>./Tests</directory>
+            </exclude>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 856ab0f..8a1694e 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -39,6 +39,10 @@
  * @}
  */
 
+use Drupal\Routing\DrupalRouteCollection;
+use Drupal\Routing\DrupalUrlGenerator;
+use Symfony\Component\Routing\RequestContext;
+
 /**
  * Return status for saving which involved creating a new item.
  */
@@ -1968,6 +1972,7 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
  * @} End of "defgroup format".
  */
 
+
 /**
  * Generates an internal or external URL.
  *
@@ -2032,122 +2037,20 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
  *   A string containing a URL to the given path.
  */
 function url($path = NULL, array $options = array()) {
-  // Merge in defaults.
-  $options += array(
-    'fragment' => '',
-    'query' => array(),
-    'absolute' => FALSE,
-    'alias' => FALSE,
-    'prefix' => ''
-  );
-
-  if (!isset($options['external'])) {
-    // Return an external link if $path contains an allowed absolute URL. Only
-    // call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
-    // before any / ? or #. Note: we could use url_is_external($path) here, but
-    // that would require another function call, and performance inside url() is
-    // critical.
-    $colonpos = strpos($path, ':');
-    $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
-  }
-
-  // Preserve the original path before altering or aliasing.
-  $original_path = $path;
-
-  // Allow other modules to alter the outbound URL and options.
-  drupal_alter('url_outbound', $path, $options, $original_path);
-
-  if (isset($options['fragment']) && $options['fragment'] !== '') {
-    $options['fragment'] = '#' . $options['fragment'];
-  }
-
-  if ($options['external']) {
-    // Split off the fragment.
-    if (strpos($path, '#') !== FALSE) {
-      list($path, $old_fragment) = explode('#', $path, 2);
-      // If $options contains no fragment, take it over from the path.
-      if (isset($old_fragment) && !$options['fragment']) {
-        $options['fragment'] = '#' . $old_fragment;
-      }
-    }
-    // Append the query.
-    if ($options['query']) {
-      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($options['query']);
-    }
-    if (isset($options['https']) && variable_get('https', FALSE)) {
-      if ($options['https'] === TRUE) {
-        $path = str_replace('http://', 'https://', $path);
-      }
-      elseif ($options['https'] === FALSE) {
-        $path = str_replace('https://', 'http://', $path);
-      }
-    }
-    // Reassemble.
-    return $path . $options['fragment'];
-  }
-
-  global $base_url, $base_secure_url, $base_insecure_url;
-
-  // The base_url might be rewritten from the language rewrite in domain mode.
-  if (!isset($options['base_url'])) {
-    if (isset($options['https']) && variable_get('https', FALSE)) {
-      if ($options['https'] === TRUE) {
-        $options['base_url'] = $base_secure_url;
-        $options['absolute'] = TRUE;
-      }
-      elseif ($options['https'] === FALSE) {
-        $options['base_url'] = $base_insecure_url;
-        $options['absolute'] = TRUE;
-      }
-    }
-    else {
-      $options['base_url'] = $base_url;
-    }
-  }
-
-  // The special path '<front>' links to the default front page.
-  if ($path == '<front>') {
-    $path = '';
-  }
-  elseif (!empty($path) && !$options['alias']) {
-    $language = isset($options['language']) && isset($options['language']->language) ? $options['language']->language : '';
-    $alias = drupal_get_path_alias($original_path, $language);
-    if ($alias != $original_path) {
-      $path = $alias;
-    }
-  }
-
-  $base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
-  $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
-
-  // With Clean URLs.
-  if (!empty($GLOBALS['conf']['clean_url'])) {
-    $path = drupal_encode_path($prefix . $path);
-    if ($options['query']) {
-      return $base . $path . '?' . drupal_http_build_query($options['query']) . $options['fragment'];
-    }
-    else {
-      return $base . $path . $options['fragment'];
-    }
-  }
-  // Without Clean URLs.
-  else {
-    $path = $prefix . $path;
-    $query = array();
-    if (!empty($path)) {
-      $query['q'] = $path;
-    }
-    if ($options['query']) {
-      // We do not use array_merge() here to prevent overriding $path via query
-      // parameters.
-      $query += $options['query'];
-    }
-    $query = $query ? ('?' . drupal_http_build_query($query)) : '';
-    $script = isset($options['script']) ? $options['script'] : '';
-    return $base . $script . $query . $options['fragment'];
+  //This is just glue code to make the DrupalUrlGenerator run
+  $generator = &drupal_static(__FUNCTION__ );
+  
+  if ($generator === NULL) {
+    $routes = new DrupalRouteCollection();
+    $context = new RequestContext($_SERVER['REQUEST_URI']);
+    
+    $generator = new DrupalUrlGenerator($routes, $context);
   }
+  
+  return $generator->url($path, $options);
 }
 
+
 /**
  * Return TRUE if a path is external to Drupal (e.g. http://example.com).
  *
-- 
1.7.9.5

