From c85d62c60902086e39fdea3ac5daa7f29f92edeb Mon Sep 17 00:00:00 2001
From: Dries <dries@buytaert.net>
Date: Mon, 24 Oct 2011 14:14:03 -0400
Subject: [PATCH] - Patch #1178246 by Crell: added Symfony2 HttpFoundation
 library to core.

---
 COPYRIGHT.txt                                 |    1 +
 .../ClassLoader/ApcUniversalClassLoader.php   |   96 ++
 .../ClassLoader/ClassCollectionLoader.php     |  222 +++
 .../ClassLoader/DebugUniversalClassLoader.php |   62 +
 .../Symfony/Component/ClassLoader/LICENSE     |   19 +
 .../Component/ClassLoader/MapClassLoader.php  |   76 +
 .../ClassLoader/UniversalClassLoader.php      |  265 ++++
 .../Component/ClassLoader/composer.json       |   22 +
 .../HttpFoundation/ApacheRequest.php          |   51 +
 .../Component/HttpFoundation/Cookie.php       |  207 +++
 .../File/Exception/AccessDeniedException.php  |   30 +
 .../File/Exception/FileException.php          |   21 +
 .../File/Exception/FileNotFoundException.php  |   30 +
 .../Exception/UnexpectedTypeException.php     |   20 +
 .../File/Exception/UploadException.php        |   21 +
 .../Component/HttpFoundation/File/File.php    |  544 ++++++++
 .../MimeType/ContentTypeMimeTypeGuesser.php   |   62 +
 .../MimeType/FileBinaryMimeTypeGuesser.php    |   71 +
 .../File/MimeType/FileinfoMimeTypeGuesser.php |   59 +
 .../File/MimeType/MimeTypeGuesser.php         |  125 ++
 .../MimeType/MimeTypeGuesserInterface.php     |   30 +
 .../HttpFoundation/File/UploadedFile.php      |  225 +++
 .../Component/HttpFoundation/FileBag.php      |  157 +++
 .../Component/HttpFoundation/HeaderBag.php    |  306 +++++
 .../Symfony/Component/HttpFoundation/LICENSE  |   19 +
 .../Component/HttpFoundation/ParameterBag.php |  245 ++++
 .../HttpFoundation/RedirectResponse.php       |   60 +
 .../Component/HttpFoundation/Request.php      | 1217 +++++++++++++++++
 .../HttpFoundation/RequestMatcher.php         |  177 +++
 .../RequestMatcherInterface.php               |   33 +
 .../Component/HttpFoundation/Response.php     |  891 ++++++++++++
 .../HttpFoundation/ResponseHeaderBag.php      |  238 ++++
 .../Component/HttpFoundation/ServerBag.php    |   43 +
 .../Component/HttpFoundation/Session.php      |  405 ++++++
 .../SessionStorage/ArraySessionStorage.php    |   58 +
 .../FilesystemSessionStorage.php              |  174 +++
 .../SessionStorage/NativeSessionStorage.php   |  180 +++
 .../SessionStorage/PdoSessionStorage.php      |  263 ++++
 .../SessionStorageInterface.php               |   97 ++
 .../Component/HttpFoundation/composer.json    |   22 +
 includes/bootstrap.inc                        |   32 +
 modules/simpletest/simpletest.info            |    1 +
 modules/simpletest/tests/symfony.test         |   31 +
 43 files changed, 6908 insertions(+)
 create mode 100644 includes/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php
 create mode 100644 includes/Symfony/Component/ClassLoader/ClassCollectionLoader.php
 create mode 100644 includes/Symfony/Component/ClassLoader/DebugUniversalClassLoader.php
 create mode 100644 includes/Symfony/Component/ClassLoader/LICENSE
 create mode 100644 includes/Symfony/Component/ClassLoader/MapClassLoader.php
 create mode 100644 includes/Symfony/Component/ClassLoader/UniversalClassLoader.php
 create mode 100644 includes/Symfony/Component/ClassLoader/composer.json
 create mode 100644 includes/Symfony/Component/HttpFoundation/ApacheRequest.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/Cookie.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/Exception/FileException.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/Exception/UploadException.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/File.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/MimeType/ContentTypeMimeTypeGuesser.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/File/UploadedFile.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/FileBag.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/HeaderBag.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/LICENSE
 create mode 100644 includes/Symfony/Component/HttpFoundation/ParameterBag.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/RedirectResponse.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/Request.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/RequestMatcher.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/RequestMatcherInterface.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/Response.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/ServerBag.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/Session.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php
 create mode 100644 includes/Symfony/Component/HttpFoundation/composer.json
 create mode 100644 modules/simpletest/tests/symfony.test

diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index e98347441a00..18b074e754a4 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -23,3 +23,4 @@ license, including:
 
   jQuery - Copyright (c) 2008 - 2009 John Resig
 
+  Symfony2 - Copyright (c) 2004 - 2011 Fabien Potencier
diff --git a/includes/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php b/includes/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php
new file mode 100644
index 000000000000..278f510e5861
--- /dev/null
+++ b/includes/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php
@@ -0,0 +1,96 @@
+<?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\ClassLoader;
+
+/**
+ * ApcUniversalClassLoader implements a "universal" autoloader cached in APC for PHP 5.3.
+ *
+ * It is able to load classes that use either:
+ *
+ *  * The technical interoperability standards for PHP 5.3 namespaces and
+ *    class names (http://groups.google.com/group/php-standards/web/psr-0-final-proposal);
+ *
+ *  * The PEAR naming convention for classes (http://pear.php.net/).
+ *
+ * Classes from a sub-namespace or a sub-hierarchy of PEAR classes can be
+ * looked for in a list of locations to ease the vendoring of a sub-set of
+ * classes for large projects.
+ *
+ * Example usage:
+ *
+ *     require 'vendor/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
+ *     require 'vendor/symfony/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
+ *
+ *     use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
+ *
+ *     $loader = new ApcUniversalClassLoader('apc.prefix.');
+ *
+ *     // register classes with namespaces
+ *     $loader->registerNamespaces(array(
+ *         'Symfony\Component' => __DIR__.'/component',
+ *         'Symfony'           => __DIR__.'/framework',
+ *         'Sensio'            => array(__DIR__.'/src', __DIR__.'/vendor'),
+ *     ));
+ *
+ *     // register a library using the PEAR naming convention
+ *     $loader->registerPrefixes(array(
+ *         'Swift_' => __DIR__.'/Swift',
+ *     ));
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Kris Wallsmith <kris@symfony.com>
+ *
+ * @api
+ */
+class ApcUniversalClassLoader extends UniversalClassLoader
+{
+    private $prefix;
+
+    /**
+     * Constructor.
+     *
+     * @param string $prefix A prefix to create a namespace in APC
+     *
+     * @api
+     */
+    public function __construct($prefix)
+    {
+        if (!extension_loaded('apc')) {
+            throw new \RuntimeException('Unable to use ApcUniversalClassLoader as APC is not enabled.');
+        }
+
+        $this->prefix = $prefix;
+    }
+
+    /**
+     * Finds a file by class name while caching lookups to APC.
+     *
+     * @param string $class A class name to resolve to file
+     */
+    public function findFile($class)
+    {
+        if (false === $file = apc_fetch($this->prefix.$class)) {
+            apc_store($this->prefix.$class, $file = parent::findFile($class));
+        }
+
+        return $file;
+    }
+}
diff --git a/includes/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/includes/Symfony/Component/ClassLoader/ClassCollectionLoader.php
new file mode 100644
index 000000000000..da777f299166
--- /dev/null
+++ b/includes/Symfony/Component/ClassLoader/ClassCollectionLoader.php
@@ -0,0 +1,222 @@
+<?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\ClassLoader;
+
+/**
+ * ClassCollectionLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ClassCollectionLoader
+{
+    static private $loaded;
+
+    /**
+     * Loads a list of classes and caches them in one big file.
+     *
+     * @param array   $classes    An array of classes to load
+     * @param string  $cacheDir   A cache directory
+     * @param string  $name       The cache name prefix
+     * @param Boolean $autoReload Whether to flush the cache when the cache is stale or not
+     * @param Boolean $adaptive   Whether to remove already declared classes or not
+     * @param string  $extension  File extension of the resulting file
+     *
+     * @throws \InvalidArgumentException When class can't be loaded
+     */
+    static public function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php')
+    {
+        // each $name can only be loaded once per PHP process
+        if (isset(self::$loaded[$name])) {
+            return;
+        }
+
+        self::$loaded[$name] = true;
+
+        if ($adaptive) {
+            // don't include already declared classes
+            $classes = array_diff($classes, get_declared_classes(), get_declared_interfaces());
+
+            // the cache is different depending on which classes are already declared
+            $name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5);
+        }
+
+        $cache = $cacheDir.'/'.$name.$extension;
+
+        // auto-reload
+        $reload = false;
+        if ($autoReload) {
+            $metadata = $cacheDir.'/'.$name.$extension.'.meta';
+            if (!file_exists($metadata) || !file_exists($cache)) {
+                $reload = true;
+            } else {
+                $time = filemtime($cache);
+                $meta = unserialize(file_get_contents($metadata));
+
+                if ($meta[1] != $classes) {
+                    $reload = true;
+                } else {
+                    foreach ($meta[0] as $resource) {
+                        if (!file_exists($resource) || filemtime($resource) > $time) {
+                            $reload = true;
+
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!$reload && file_exists($cache)) {
+            require_once $cache;
+
+            return;
+        }
+
+        $files = array();
+        $content = '';
+        foreach ($classes as $class) {
+            if (!class_exists($class) && !interface_exists($class) && (!function_exists('trait_exists') || !trait_exists($class))) {
+                throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
+            }
+
+            $r = new \ReflectionClass($class);
+            $files[] = $r->getFileName();
+
+            $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName()));
+
+            // add namespace declaration for global code
+            if (!$r->inNamespace()) {
+                $c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n";
+            } else {
+                $c = self::fixNamespaceDeclarations('<?php '.$c);
+                $c = preg_replace('/^\s*<\?php/', '', $c);
+            }
+
+            $content .= $c;
+        }
+
+        // cache the core classes
+        if (!is_dir(dirname($cache))) {
+            mkdir(dirname($cache), 0777, true);
+        }
+        self::writeCacheFile($cache, '<?php '.$content);
+
+        if ($autoReload) {
+            // save the resources
+            self::writeCacheFile($metadata, serialize(array($files, $classes)));
+        }
+    }
+
+    /**
+     * Adds brackets around each namespace if it's not already the case.
+     *
+     * @param string $source Namespace string
+     *
+     * @return string Namespaces with brackets
+     */
+    static public function fixNamespaceDeclarations($source)
+    {
+        if (!function_exists('token_get_all')) {
+            return $source;
+        }
+
+        $output = '';
+        $inNamespace = false;
+        $tokens = token_get_all($source);
+
+        for ($i = 0, $max = count($tokens); $i < $max; $i++) {
+            $token = $tokens[$i];
+            if (is_string($token)) {
+                $output .= $token;
+            } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
+                // strip comments
+                continue;
+            } elseif (T_NAMESPACE === $token[0]) {
+                if ($inNamespace) {
+                    $output .= "}\n";
+                }
+                $output .= $token[1];
+
+                // namespace name and whitespaces
+                while (($t = $tokens[++$i]) && is_array($t) && in_array($t[0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
+                    $output .= $t[1];
+                }
+                if (is_string($t) && '{' === $t) {
+                    $inNamespace = false;
+                    --$i;
+                } else {
+                    $output .= "\n{";
+                    $inNamespace = true;
+                }
+            } else {
+                $output .= $token[1];
+            }
+        }
+
+        if ($inNamespace) {
+            $output .= "}\n";
+        }
+
+        return $output;
+    }
+
+    /**
+     * Writes a cache file.
+     *
+     * @param string $file Filename
+     * @param string $content Temporary file content
+     *
+     * @throws \RuntimeException when a cache file cannot be written
+     */
+    static private function writeCacheFile($file, $content)
+    {
+        $tmpFile = tempnam(dirname($file), basename($file));
+        if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
+            chmod($file, 0644);
+
+            return;
+        }
+
+        throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
+    }
+
+    /**
+     * Removes comments from a PHP source string.
+     *
+     * We don't use the PHP php_strip_whitespace() function
+     * as we want the content to be readable and well-formatted.
+     *
+     * @param string $source A PHP string
+     *
+     * @return string The PHP string with the comments removed
+     */
+    static private function stripComments($source)
+    {
+        if (!function_exists('token_get_all')) {
+            return $source;
+        }
+
+        $output = '';
+        foreach (token_get_all($source) as $token) {
+            if (is_string($token)) {
+                $output .= $token;
+            } elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
+                $output .= $token[1];
+            }
+        }
+
+        // replace multiple new lines with a single newline
+        $output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
+
+        return $output;
+    }
+}
diff --git a/includes/Symfony/Component/ClassLoader/DebugUniversalClassLoader.php b/includes/Symfony/Component/ClassLoader/DebugUniversalClassLoader.php
new file mode 100644
index 000000000000..8a958e01f96c
--- /dev/null
+++ b/includes/Symfony/Component/ClassLoader/DebugUniversalClassLoader.php
@@ -0,0 +1,62 @@
+<?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\ClassLoader;
+
+/**
+ * Checks that the class is actually declared in the included file.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class DebugUniversalClassLoader extends UniversalClassLoader
+{
+    /**
+     * Replaces all regular UniversalClassLoader instances by a DebugUniversalClassLoader ones.
+     */
+    static public function enable()
+    {
+        if (!is_array($functions = spl_autoload_functions())) {
+            return;
+        }
+
+        foreach ($functions as $function) {
+            spl_autoload_unregister($function);
+        }
+
+        foreach ($functions as $function) {
+            if (is_array($function) && $function[0] instanceof UniversalClassLoader) {
+                $loader = new static();
+                $loader->registerNamespaceFallbacks($function[0]->getNamespaceFallbacks());
+                $loader->registerPrefixFallbacks($function[0]->getPrefixFallbacks());
+                $loader->registerNamespaces($function[0]->getNamespaces());
+                $loader->registerPrefixes($function[0]->getPrefixes());
+
+                $function[0] = $loader;
+            }
+
+            spl_autoload_register($function);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            require $file;
+
+            if (!class_exists($class, false) && !interface_exists($class, false) && (!function_exists('trait_exists') || !trait_exists($class, false))) {
+                throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
+            }
+        }
+    }
+}
diff --git a/includes/Symfony/Component/ClassLoader/LICENSE b/includes/Symfony/Component/ClassLoader/LICENSE
new file mode 100644
index 000000000000..89df4481b950
--- /dev/null
+++ b/includes/Symfony/Component/ClassLoader/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2011 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/includes/Symfony/Component/ClassLoader/MapClassLoader.php b/includes/Symfony/Component/ClassLoader/MapClassLoader.php
new file mode 100644
index 000000000000..cf17d42642cb
--- /dev/null
+++ b/includes/Symfony/Component/ClassLoader/MapClassLoader.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\ClassLoader;
+
+/**
+ * A class loader that uses a mapping file to look up paths.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class MapClassLoader
+{
+    private $map = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $map A map where keys are classes and values the absolute file path
+     */
+    public function __construct(array $map)
+    {
+        $this->map = $map;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param Boolean $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param string $class The name of the class
+     */
+    public function loadClass($class)
+    {
+        if ('\\' === $class[0]) {
+            $class = substr($class, 1);
+        }
+
+        if (isset($this->map[$class])) {
+            require $this->map[$class];
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|null The path, if found
+     */
+    public function findFile($class)
+    {
+        if ('\\' === $class[0]) {
+            $class = substr($class, 1);
+        }
+
+        if (isset($this->map[$class])) {
+            return $this->map[$class];
+        }
+    }
+}
diff --git a/includes/Symfony/Component/ClassLoader/UniversalClassLoader.php b/includes/Symfony/Component/ClassLoader/UniversalClassLoader.php
new file mode 100644
index 000000000000..d296b94d5804
--- /dev/null
+++ b/includes/Symfony/Component/ClassLoader/UniversalClassLoader.php
@@ -0,0 +1,265 @@
+<?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\ClassLoader;
+
+/**
+ * UniversalClassLoader implements a "universal" autoloader for PHP 5.3.
+ *
+ * It is able to load classes that use either:
+ *
+ *  * The technical interoperability standards for PHP 5.3 namespaces and
+ *    class names (http://groups.google.com/group/php-standards/web/psr-0-final-proposal);
+ *
+ *  * The PEAR naming convention for classes (http://pear.php.net/).
+ *
+ * Classes from a sub-namespace or a sub-hierarchy of PEAR classes can be
+ * looked for in a list of locations to ease the vendoring of a sub-set of
+ * classes for large projects.
+ *
+ * Example usage:
+ *
+ *     $loader = new UniversalClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->registerNamespaces(array(
+ *         'Symfony\Component' => __DIR__.'/component',
+ *         'Symfony'           => __DIR__.'/framework',
+ *         'Sensio'            => array(__DIR__.'/src', __DIR__.'/vendor'),
+ *     ));
+ *
+ *     // register a library using the PEAR naming convention
+ *     $loader->registerPrefixes(array(
+ *         'Swift_' => __DIR__.'/Swift',
+ *     ));
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class UniversalClassLoader
+{
+    private $namespaces = array();
+    private $prefixes = array();
+    private $namespaceFallbacks = array();
+    private $prefixFallbacks = array();
+
+    /**
+     * Gets the configured namespaces.
+     *
+     * @return array A hash with namespaces as keys and directories as values
+     */
+    public function getNamespaces()
+    {
+        return $this->namespaces;
+    }
+
+    /**
+     * Gets the configured class prefixes.
+     *
+     * @return array A hash with class prefixes as keys and directories as values
+     */
+    public function getPrefixes()
+    {
+        return $this->prefixes;
+    }
+
+    /**
+     * Gets the directory(ies) to use as a fallback for namespaces.
+     *
+     * @return array An array of directories
+     */
+    public function getNamespaceFallbacks()
+    {
+        return $this->namespaceFallbacks;
+    }
+
+    /**
+     * Gets the directory(ies) to use as a fallback for class prefixes.
+     *
+     * @return array An array of directories
+     */
+    public function getPrefixFallbacks()
+    {
+        return $this->prefixFallbacks;
+    }
+
+    /**
+     * Registers the directory to use as a fallback for namespaces.
+     *
+     * @param array $dirs An array of directories
+     *
+     * @api
+     */
+    public function registerNamespaceFallbacks(array $dirs)
+    {
+        $this->namespaceFallbacks = $dirs;
+    }
+
+    /**
+     * Registers the directory to use as a fallback for class prefixes.
+     *
+     * @param array $dirs An array of directories
+     *
+     * @api
+     */
+    public function registerPrefixFallbacks(array $dirs)
+    {
+        $this->prefixFallbacks = $dirs;
+    }
+
+    /**
+     * Registers an array of namespaces
+     *
+     * @param array $namespaces An array of namespaces (namespaces as keys and locations as values)
+     *
+     * @api
+     */
+    public function registerNamespaces(array $namespaces)
+    {
+        foreach ($namespaces as $namespace => $locations) {
+            $this->namespaces[$namespace] = (array) $locations;
+        }
+    }
+
+    /**
+     * Registers a namespace.
+     *
+     * @param string       $namespace The namespace
+     * @param array|string $paths     The location(s) of the namespace
+     *
+     * @api
+     */
+    public function registerNamespace($namespace, $paths)
+    {
+        $this->namespaces[$namespace] = (array) $paths;
+    }
+
+    /**
+     * Registers an array of classes using the PEAR naming convention.
+     *
+     * @param array $classes An array of classes (prefixes as keys and locations as values)
+     *
+     * @api
+     */
+    public function registerPrefixes(array $classes)
+    {
+        foreach ($classes as $prefix => $locations) {
+            $this->prefixes[$prefix] = (array) $locations;
+        }
+    }
+
+    /**
+     * Registers a set of classes using the PEAR naming convention.
+     *
+     * @param string       $prefix  The classes prefix
+     * @param array|string $paths   The location(s) of the classes
+     *
+     * @api
+     */
+    public function registerPrefix($prefix, $paths)
+    {
+        $this->prefixes[$prefix] = (array) $paths;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param Boolean $prepend Whether to prepend the autoloader or not
+     *
+     * @api
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param string $class The name of the class
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            require $file;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|null The path, if found
+     */
+    public function findFile($class)
+    {
+        if ('\\' == $class[0]) {
+            $class = substr($class, 1);
+        }
+
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $namespace = substr($class, 0, $pos);
+            foreach ($this->namespaces as $ns => $dirs) {
+                if (0 !== strpos($namespace, $ns)) {
+                    continue;
+                }
+
+                foreach ($dirs as $dir) {
+                    $className = substr($class, $pos + 1);
+                    $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $className).'.php';
+                    if (file_exists($file)) {
+                        return $file;
+                    }
+                }
+            }
+
+            foreach ($this->namespaceFallbacks as $dir) {
+                $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
+                if (file_exists($file)) {
+                    return $file;
+                }
+            }
+        } else {
+            // PEAR-like class name
+            foreach ($this->prefixes as $prefix => $dirs) {
+                if (0 !== strpos($class, $prefix)) {
+                    continue;
+                }
+
+                foreach ($dirs as $dir) {
+                    $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php';
+                    if (file_exists($file)) {
+                        return $file;
+                    }
+                }
+            }
+
+            foreach ($this->prefixFallbacks as $dir) {
+                $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php';
+                if (file_exists($file)) {
+                    return $file;
+                }
+            }
+        }
+    }
+}
diff --git a/includes/Symfony/Component/ClassLoader/composer.json b/includes/Symfony/Component/ClassLoader/composer.json
new file mode 100644
index 000000000000..35b573e5bb82
--- /dev/null
+++ b/includes/Symfony/Component/ClassLoader/composer.json
@@ -0,0 +1,22 @@
+{
+    "name": "symfony/class-loader",
+    "type": "library",
+    "description": "Symfony ClassLoader Component",
+    "keywords": [],
+    "homepage": "http://symfony.com",
+    "version": "2.0.4",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "http://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.2"
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/ApacheRequest.php b/includes/Symfony/Component/HttpFoundation/ApacheRequest.php
new file mode 100644
index 000000000000..27215819e499
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/ApacheRequest.php
@@ -0,0 +1,51 @@
+<?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\HttpFoundation;
+
+/**
+ * Request represents an HTTP request from an Apache server.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ApacheRequest extends Request
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function prepareRequestUri()
+    {
+        return $this->server->get('REQUEST_URI');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function prepareBaseUrl()
+    {
+        $baseUrl = $this->server->get('SCRIPT_NAME');
+
+        if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) {
+            // assume mod_rewrite
+            return rtrim(dirname($baseUrl), '/\\');
+        }
+
+        return $baseUrl;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function preparePathInfo()
+    {
+        return $this->server->get('PATH_INFO');
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/Cookie.php b/includes/Symfony/Component/HttpFoundation/Cookie.php
new file mode 100644
index 000000000000..8392812ebe52
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/Cookie.php
@@ -0,0 +1,207 @@
+<?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\HttpFoundation;
+
+/**
+ * Represents a cookie
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ *
+ * @api
+ */
+class Cookie
+{
+    protected $name;
+    protected $value;
+    protected $domain;
+    protected $expire;
+    protected $path;
+    protected $secure;
+    protected $httpOnly;
+
+    /**
+     * Constructor.
+     *
+     * @param string                    $name       The name of the cookie
+     * @param string                    $value      The value of the cookie
+     * @param integer|string|\DateTime  $expire     The time the cookie expires
+     * @param string                    $path       The path on the server in which the cookie will be available on
+     * @param string                    $domain     The domain that the cookie is available to
+     * @param Boolean                   $secure     Whether the cookie should only be transmitted over a secure HTTPS connection from the client
+     * @param Boolean                   $httpOnly   Whether the cookie will be made accessible only through the HTTP protocol
+     *
+     * @api
+     */
+    public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true)
+    {
+        // from PHP source code
+        if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
+            throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
+        }
+
+        if (preg_match("/[,; \t\r\n\013\014]/", $value)) {
+            throw new \InvalidArgumentException(sprintf('The cookie value "%s" contains invalid characters.', $value));
+        }
+
+        if (empty($name)) {
+            throw new \InvalidArgumentException('The cookie name cannot be empty.');
+        }
+
+        // convert expiration time to a Unix timestamp
+        if ($expire instanceof \DateTime) {
+            $expire = $expire->format('U');
+        } elseif (!is_numeric($expire)) {
+            $expire = strtotime($expire);
+
+            if (false === $expire || -1 === $expire) {
+                throw new \InvalidArgumentException('The cookie expiration time is not valid.');
+            }
+        }
+
+        $this->name = $name;
+        $this->value = $value;
+        $this->domain = $domain;
+        $this->expire = $expire;
+        $this->path = empty($path) ? '/' : $path;
+        $this->secure = (Boolean) $secure;
+        $this->httpOnly = (Boolean) $httpOnly;
+    }
+
+    public function __toString()
+    {
+        $str = urlencode($this->getName()).'=';
+
+        if ('' === (string) $this->getValue()) {
+            $str .= 'deleted; expires='.gmdate("D, d-M-Y H:i:s T", time() - 31536001);
+        } else {
+            $str .= urlencode($this->getValue());
+
+            if ($this->getExpiresTime() !== 0) {
+                $str .= '; expires='.gmdate("D, d-M-Y H:i:s T", $this->getExpiresTime());
+            }
+        }
+
+        if ('/' !== $this->path) {
+            $str .= '; path='.$this->path;
+        }
+
+        if (null !== $this->getDomain()) {
+            $str .= '; domain='.$this->getDomain();
+        }
+
+        if (true === $this->isSecure()) {
+            $str .= '; secure';
+        }
+
+        if (true === $this->isHttpOnly()) {
+            $str .= '; httponly';
+        }
+
+        return $str;
+    }
+
+    /**
+     * Gets the name of the cookie.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the value of the cookie.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Gets the domain that the cookie is available to.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getDomain()
+    {
+        return $this->domain;
+    }
+
+    /**
+     * Gets the time the cookie expires.
+     *
+     * @return integer
+     *
+     * @api
+     */
+    public function getExpiresTime()
+    {
+        return $this->expire;
+    }
+
+    /**
+     * Gets the path on the server in which the cookie will be available on.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client.
+     *
+     * @return Boolean
+     *
+     * @api
+     */
+    public function isSecure()
+    {
+        return $this->secure;
+    }
+
+    /**
+     * Checks whether the cookie will be made accessible only through the HTTP protocol.
+     *
+     * @return Boolean
+     *
+     * @api
+     */
+    public function isHttpOnly()
+    {
+        return $this->httpOnly;
+    }
+
+    /**
+     * Whether this cookie is about to be cleared
+     *
+     * @return Boolean
+     *
+     * @api
+     */
+    public function isCleared()
+    {
+        return $this->expire < time();
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php b/includes/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php
new file mode 100644
index 000000000000..9c7fe6812a31
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.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\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when the access on a file was denied.
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class AccessDeniedException extends FileException
+{
+    /**
+     * Constructor.
+     *
+     * @param string $path  The path to the accessed file
+     */
+    public function __construct($path)
+    {
+        parent::__construct(sprintf('The file %s could not be accessed', $path));
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/Exception/FileException.php b/includes/Symfony/Component/HttpFoundation/File/Exception/FileException.php
new file mode 100644
index 000000000000..43c6cc8998c5
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/Exception/FileException.php
@@ -0,0 +1,21 @@
+<?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\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when an error occurred in the component File
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class FileException extends \RuntimeException
+{
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php b/includes/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php
new file mode 100644
index 000000000000..5b1aef8e2b29
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.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\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when a file was not found
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class FileNotFoundException extends FileException
+{
+    /**
+     * Constructor.
+     *
+     * @param string $path  The path to the file that was not found
+     */
+    public function __construct($path)
+    {
+        parent::__construct(sprintf('The file "%s" does not exist', $path));
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php b/includes/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php
new file mode 100644
index 000000000000..0444b8778218
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php
@@ -0,0 +1,20 @@
+<?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\HttpFoundation\File\Exception;
+
+class UnexpectedTypeException extends FileException
+{
+    public function __construct($value, $expectedType)
+    {
+        parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/Exception/UploadException.php b/includes/Symfony/Component/HttpFoundation/File/Exception/UploadException.php
new file mode 100644
index 000000000000..694e864d1c56
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/Exception/UploadException.php
@@ -0,0 +1,21 @@
+<?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\HttpFoundation\File\Exception;
+
+/**
+ * Thrown when an error occurred during file upload
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class UploadException extends FileException
+{
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/File.php b/includes/Symfony/Component/HttpFoundation/File/File.php
new file mode 100644
index 000000000000..3a900fdcef61
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/File.php
@@ -0,0 +1,544 @@
+<?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\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
+
+/**
+ * A file in the file system.
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ *
+ * @api
+ */
+class File extends \SplFileInfo
+{
+    /**
+     * A map of mime types and their default extensions.
+     *
+     * @var array
+     */
+    static protected $defaultExtensions = array(
+        'application/andrew-inset' => 'ez',
+        'application/appledouble' => 'base64',
+        'application/applefile' => 'base64',
+        'application/commonground' => 'dp',
+        'application/cprplayer' => 'pqi',
+        'application/dsptype' => 'tsp',
+        'application/excel' => 'xls',
+        'application/font-tdpfr' => 'pfr',
+        'application/futuresplash' => 'spl',
+        'application/hstu' => 'stk',
+        'application/hyperstudio' => 'stk',
+        'application/javascript' => 'js',
+        'application/mac-binhex40' => 'hqx',
+        'application/mac-compactpro' => 'cpt',
+        'application/mbed' => 'mbd',
+        'application/mirage' => 'mfp',
+        'application/msword' => 'doc',
+        'application/ocsp-request' => 'orq',
+        'application/ocsp-response' => 'ors',
+        'application/octet-stream' => 'bin',
+        'application/oda' => 'oda',
+        'application/ogg' => 'ogg',
+        'application/pdf' => 'pdf',
+        'application/x-pdf' => 'pdf',
+        'application/pgp-encrypted' => '7bit',
+        'application/pgp-keys' => '7bit',
+        'application/pgp-signature' => 'sig',
+        'application/pkcs10' => 'p10',
+        'application/pkcs7-mime' => 'p7m',
+        'application/pkcs7-signature' => 'p7s',
+        'application/pkix-cert' => 'cer',
+        'application/pkix-crl' => 'crl',
+        'application/pkix-pkipath' => 'pkipath',
+        'application/pkixcmp' => 'pki',
+        'application/postscript' => 'ps',
+        'application/presentations' => 'shw',
+        'application/prs.cww' => 'cw',
+        'application/prs.nprend' => 'rnd',
+        'application/quest' => 'qrt',
+        'application/rtf' => 'rtf',
+        'application/sgml-open-catalog' => 'soc',
+        'application/sieve' => 'siv',
+        'application/smil' => 'smi',
+        'application/toolbook' => 'tbk',
+        'application/vnd.3gpp.pic-bw-large' => 'plb',
+        'application/vnd.3gpp.pic-bw-small' => 'psb',
+        'application/vnd.3gpp.pic-bw-var' => 'pvb',
+        'application/vnd.3gpp.sms' => 'sms',
+        'application/vnd.acucorp' => 'atc',
+        'application/vnd.adobe.xfdf' => 'xfdf',
+        'application/vnd.amiga.amu' => 'ami',
+        'application/vnd.blueice.multipass' => 'mpm',
+        'application/vnd.cinderella' => 'cdy',
+        'application/vnd.cosmocaller' => 'cmc',
+        'application/vnd.criticaltools.wbs+xml' => 'wbs',
+        'application/vnd.curl' => 'curl',
+        'application/vnd.data-vision.rdz' => 'rdz',
+        'application/vnd.dreamfactory' => 'dfac',
+        'application/vnd.fsc.weblaunch' => 'fsc',
+        'application/vnd.genomatix.tuxedo' => 'txd',
+        'application/vnd.hbci' => 'hbci',
+        'application/vnd.hhe.lesson-player' => 'les',
+        'application/vnd.hp-hpgl' => 'plt',
+        'application/vnd.ibm.electronic-media' => 'emm',
+        'application/vnd.ibm.rights-management' => 'irm',
+        'application/vnd.ibm.secure-container' => 'sc',
+        'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
+        'application/vnd.irepository.package+xml' => 'irp',
+        'application/vnd.jisp' => 'jisp',
+        'application/vnd.kde.karbon' => 'karbon',
+        'application/vnd.kde.kchart' => 'chrt',
+        'application/vnd.kde.kformula' => 'kfo',
+        'application/vnd.kde.kivio' => 'flw',
+        'application/vnd.kde.kontour' => 'kon',
+        'application/vnd.kde.kpresenter' => 'kpr',
+        'application/vnd.kde.kspread' => 'ksp',
+        'application/vnd.kde.kword' => 'kwd',
+        'application/vnd.kenameapp' => 'htke',
+        'application/vnd.kidspiration' => 'kia',
+        'application/vnd.kinar' => 'kne',
+        'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
+        'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
+        'application/vnd.lotus-1-2-3' => 'wks',
+        'application/vnd.mcd' => 'mcd',
+        'application/vnd.mfmp' => 'mfm',
+        'application/vnd.micrografx.flo' => 'flo',
+        'application/vnd.micrografx.igx' => 'igx',
+        'application/vnd.mif' => 'mif',
+        'application/vnd.mophun.application' => 'mpn',
+        'application/vnd.mophun.certificate' => 'mpc',
+        'application/vnd.mozilla.xul+xml' => 'xul',
+        'application/vnd.ms-artgalry' => 'cil',
+        'application/vnd.ms-asf' => 'asf',
+        'application/vnd.ms-excel' => 'xls',
+        'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
+        'application/vnd.ms-lrm' => 'lrm',
+        'application/vnd.ms-powerpoint' => 'ppt',
+        'application/vnd.ms-project' => 'mpp',
+        'application/vnd.ms-tnef' => 'base64',
+        'application/vnd.ms-works' => 'base64',
+        'application/vnd.ms-wpl' => 'wpl',
+        'application/vnd.mseq' => 'mseq',
+        'application/vnd.nervana' => 'ent',
+        'application/vnd.nokia.radio-preset' => 'rpst',
+        'application/vnd.nokia.radio-presets' => 'rpss',
+        'application/vnd.oasis.opendocument.text' => 'odt',
+        'application/vnd.oasis.opendocument.text-template' => 'ott',
+        'application/vnd.oasis.opendocument.text-web' => 'oth',
+        'application/vnd.oasis.opendocument.text-master' => 'odm',
+        'application/vnd.oasis.opendocument.graphics' => 'odg',
+        'application/vnd.oasis.opendocument.graphics-template' => 'otg',
+        'application/vnd.oasis.opendocument.presentation' => 'odp',
+        'application/vnd.oasis.opendocument.presentation-template' => 'otp',
+        'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
+        'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
+        'application/vnd.oasis.opendocument.chart' => 'odc',
+        'application/vnd.oasis.opendocument.formula' => 'odf',
+        'application/vnd.oasis.opendocument.database' => 'odb',
+        'application/vnd.oasis.opendocument.image' => 'odi',
+        'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
+        'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
+        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
+        'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
+        'application/vnd.palm' => 'prc',
+        'application/vnd.picsel' => 'efif',
+        'application/vnd.pvi.ptid1' => 'pti',
+        'application/vnd.quark.quarkxpress' => 'qxd',
+        'application/vnd.sealed.doc' => 'sdoc',
+        'application/vnd.sealed.eml' => 'seml',
+        'application/vnd.sealed.mht' => 'smht',
+        'application/vnd.sealed.ppt' => 'sppt',
+        'application/vnd.sealed.xls' => 'sxls',
+        'application/vnd.sealedmedia.softseal.html' => 'stml',
+        'application/vnd.sealedmedia.softseal.pdf' => 'spdf',
+        'application/vnd.seemail' => 'see',
+        'application/vnd.smaf' => 'mmf',
+        'application/vnd.sun.xml.calc' => 'sxc',
+        'application/vnd.sun.xml.calc.template' => 'stc',
+        'application/vnd.sun.xml.draw' => 'sxd',
+        'application/vnd.sun.xml.draw.template' => 'std',
+        'application/vnd.sun.xml.impress' => 'sxi',
+        'application/vnd.sun.xml.impress.template' => 'sti',
+        'application/vnd.sun.xml.math' => 'sxm',
+        'application/vnd.sun.xml.writer' => 'sxw',
+        'application/vnd.sun.xml.writer.global' => 'sxg',
+        'application/vnd.sun.xml.writer.template' => 'stw',
+        'application/vnd.sus-calendar' => 'sus',
+        'application/vnd.vidsoft.vidconference' => 'vsc',
+        'application/vnd.visio' => 'vsd',
+        'application/vnd.visionary' => 'vis',
+        'application/vnd.wap.sic' => 'sic',
+        'application/vnd.wap.slc' => 'slc',
+        'application/vnd.wap.wbxml' => 'wbxml',
+        'application/vnd.wap.wmlc' => 'wmlc',
+        'application/vnd.wap.wmlscriptc' => 'wmlsc',
+        'application/vnd.webturbo' => 'wtb',
+        'application/vnd.wordperfect' => 'wpd',
+        'application/vnd.wqd' => 'wqd',
+        'application/vnd.wv.csp+wbxml' => 'wv',
+        'application/vnd.wv.csp+xml' => '8bit',
+        'application/vnd.wv.ssp+xml' => '8bit',
+        'application/vnd.yamaha.hv-dic' => 'hvd',
+        'application/vnd.yamaha.hv-script' => 'hvs',
+        'application/vnd.yamaha.hv-voice' => 'hvp',
+        'application/vnd.yamaha.smaf-audio' => 'saf',
+        'application/vnd.yamaha.smaf-phrase' => 'spf',
+        'application/vocaltec-media-desc' => 'vmd',
+        'application/vocaltec-media-file' => 'vmf',
+        'application/vocaltec-talker' => 'vtk',
+        'application/watcherinfo+xml' => 'wif',
+        'application/wordperfect5.1' => 'wp5',
+        'application/x-123' => 'wk',
+        'application/x-7th_level_event' => '7ls',
+        'application/x-authorware-bin' => 'aab',
+        'application/x-authorware-map' => 'aam',
+        'application/x-authorware-seg' => 'aas',
+        'application/x-bcpio' => 'bcpio',
+        'application/x-bleeper' => 'bleep',
+        'application/x-bzip2' => 'bz2',
+        'application/x-cdlink' => 'vcd',
+        'application/x-chat' => 'chat',
+        'application/x-chess-pgn' => 'pgn',
+        'application/x-compress' => 'z',
+        'application/x-cpio' => 'cpio',
+        'application/x-cprplayer' => 'pqf',
+        'application/x-csh' => 'csh',
+        'application/x-cu-seeme' => 'csm',
+        'application/x-cult3d-object' => 'co',
+        'application/x-debian-package' => 'deb',
+        'application/x-director' => 'dcr',
+        'application/x-dvi' => 'dvi',
+        'application/x-envoy' => 'evy',
+        'application/x-futuresplash' => 'spl',
+        'application/x-gtar' => 'gtar',
+        'application/x-gzip' => 'gz',
+        'application/x-hdf' => 'hdf',
+        'application/x-hep' => 'hep',
+        'application/x-html+ruby' => 'rhtml',
+        'application/x-httpd-miva' => 'mv',
+        'application/x-httpd-php' => 'phtml',
+        'application/x-ica' => 'ica',
+        'application/x-imagemap' => 'imagemap',
+        'application/x-ipix' => 'ipx',
+        'application/x-ipscript' => 'ips',
+        'application/x-java-archive' => 'jar',
+        'application/x-java-jnlp-file' => 'jnlp',
+        'application/x-java-serialized-object' => 'ser',
+        'application/x-java-vm' => 'class',
+        'application/x-javascript' => 'js',
+        'application/x-koan' => 'skp',
+        'application/x-latex' => 'latex',
+        'application/x-mac-compactpro' => 'cpt',
+        'application/x-maker' => 'frm',
+        'application/x-mathcad' => 'mcd',
+        'application/x-midi' => 'mid',
+        'application/x-mif' => 'mif',
+        'application/x-msaccess' => 'mda',
+        'application/x-msdos-program' => 'com',
+        'application/x-msdownload' => 'base64',
+        'application/x-msexcel' => 'xls',
+        'application/x-msword' => 'doc',
+        'application/x-netcdf' => 'nc',
+        'application/x-ns-proxy-autoconfig' => 'pac',
+        'application/x-pagemaker' => 'pm5',
+        'application/x-perl' => 'pl',
+        'application/x-pn-realmedia' => 'rp',
+        'application/x-python' => 'py',
+        'application/x-quicktimeplayer' => 'qtl',
+        'application/x-rar-compressed' => 'rar',
+        'application/x-ruby' => 'rb',
+        'application/x-sh' => 'sh',
+        'application/x-shar' => 'shar',
+        'application/x-shockwave-flash' => 'swf',
+        'application/x-sprite' => 'spr',
+        'application/x-spss' => 'sav',
+        'application/x-spt' => 'spt',
+        'application/x-stuffit' => 'sit',
+        'application/x-sv4cpio' => 'sv4cpio',
+        'application/x-sv4crc' => 'sv4crc',
+        'application/x-tar' => 'tar',
+        'application/x-tcl' => 'tcl',
+        'application/x-tex' => 'tex',
+        'application/x-texinfo' => 'texinfo',
+        'application/x-troff' => 't',
+        'application/x-troff-man' => 'man',
+        'application/x-troff-me' => 'me',
+        'application/x-troff-ms' => 'ms',
+        'application/x-twinvq' => 'vqf',
+        'application/x-twinvq-plugin' => 'vqe',
+        'application/x-ustar' => 'ustar',
+        'application/x-vmsbackup' => 'bck',
+        'application/x-wais-source' => 'src',
+        'application/x-wingz' => 'wz',
+        'application/x-word' => 'base64',
+        'application/x-wordperfect6.1' => 'wp6',
+        'application/x-x509-ca-cert' => 'crt',
+        'application/x-zip-compressed' => 'zip',
+        'application/xhtml+xml' => 'xhtml',
+        'application/zip' => 'zip',
+        'audio/3gpp' => '3gpp',
+        'audio/amr' => 'amr',
+        'audio/amr-wb' => 'awb',
+        'audio/basic' => 'au',
+        'audio/evrc' => 'evc',
+        'audio/l16' => 'l16',
+        'audio/midi' => 'mid',
+        'audio/mpeg' => 'mp3',
+        'audio/prs.sid' => 'sid',
+        'audio/qcelp' => 'qcp',
+        'audio/smv' => 'smv',
+        'audio/vnd.audiokoz' => 'koz',
+        'audio/vnd.digital-winds' => 'eol',
+        'audio/vnd.everad.plj' => 'plj',
+        'audio/vnd.lucent.voice' => 'lvp',
+        'audio/vnd.nokia.mobile-xmf' => 'mxmf',
+        'audio/vnd.nortel.vbk' => 'vbk',
+        'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
+        'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
+        'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
+        'audio/vnd.sealedmedia.softseal.mpeg' => 'smp3',
+        'audio/voxware' => 'vox',
+        'audio/x-aiff' => 'aif',
+        'audio/x-mid' => 'mid',
+        'audio/x-midi' => 'mid',
+        'audio/x-mpeg' => 'mp2',
+        'audio/x-mpegurl' => 'mpu',
+        'audio/x-pn-realaudio' => 'rm',
+        'audio/x-pn-realaudio-plugin' => 'rpm',
+        'audio/x-realaudio' => 'ra',
+        'audio/x-wav' => 'wav',
+        'chemical/x-csml' => 'csm',
+        'chemical/x-embl-dl-nucleotide' => 'emb',
+        'chemical/x-gaussian-cube' => 'cube',
+        'chemical/x-gaussian-input' => 'gau',
+        'chemical/x-jcamp-dx' => 'jdx',
+        'chemical/x-mdl-molfile' => 'mol',
+        'chemical/x-mdl-rxnfile' => 'rxn',
+        'chemical/x-mdl-tgf' => 'tgf',
+        'chemical/x-mopac-input' => 'mop',
+        'chemical/x-pdb' => 'pdb',
+        'chemical/x-rasmol' => 'scr',
+        'chemical/x-xyz' => 'xyz',
+        'drawing/dwf' => 'dwf',
+        'drawing/x-dwf' => 'dwf',
+        'i-world/i-vrml' => 'ivr',
+        'image/bmp' => 'bmp',
+        'image/cewavelet' => 'wif',
+        'image/cis-cod' => 'cod',
+        'image/fif' => 'fif',
+        'image/gif' => 'gif',
+        'image/ief' => 'ief',
+        'image/jp2' => 'jp2',
+        'image/jpeg' => 'jpg',
+        'image/jpm' => 'jpm',
+        'image/jpx' => 'jpf',
+        'image/pict' => 'pic',
+        'image/pjpeg' => 'jpg',
+        'image/png' => 'png',
+        'image/targa' => 'tga',
+        'image/tiff' => 'tif',
+        'image/vn-svf' => 'svf',
+        'image/vnd.dgn' => 'dgn',
+        'image/vnd.djvu' => 'djvu',
+        'image/vnd.dwg' => 'dwg',
+        'image/vnd.glocalgraphics.pgb' => 'pgb',
+        'image/vnd.microsoft.icon' => 'ico',
+        'image/vnd.ms-modi' => 'mdi',
+        'image/vnd.sealed.png' => 'spng',
+        'image/vnd.sealedmedia.softseal.gif' => 'sgif',
+        'image/vnd.sealedmedia.softseal.jpg' => 'sjpg',
+        'image/vnd.wap.wbmp' => 'wbmp',
+        'image/x-bmp' => 'bmp',
+        'image/x-cmu-raster' => 'ras',
+        'image/x-freehand' => 'fh4',
+        'image/x-png' => 'png',
+        'image/x-portable-anymap' => 'pnm',
+        'image/x-portable-bitmap' => 'pbm',
+        'image/x-portable-graymap' => 'pgm',
+        'image/x-portable-pixmap' => 'ppm',
+        'image/x-rgb' => 'rgb',
+        'image/x-xbitmap' => 'xbm',
+        'image/x-xpixmap' => 'xpm',
+        'image/x-xwindowdump' => 'xwd',
+        'message/external-body' => '8bit',
+        'message/news' => '8bit',
+        'message/partial' => '8bit',
+        'message/rfc822' => '8bit',
+        'model/iges' => 'igs',
+        'model/mesh' => 'msh',
+        'model/vnd.parasolid.transmit.binary' => 'x_b',
+        'model/vnd.parasolid.transmit.text' => 'x_t',
+        'model/vrml' => 'wrl',
+        'multipart/alternative' => '8bit',
+        'multipart/appledouble' => '8bit',
+        'multipart/digest' => '8bit',
+        'multipart/mixed' => '8bit',
+        'multipart/parallel' => '8bit',
+        'text/comma-separated-values' => 'csv',
+        'text/css' => 'css',
+        'text/html' => 'html',
+        'text/plain' => 'txt',
+        'text/prs.fallenstein.rst' => 'rst',
+        'text/richtext' => 'rtx',
+        'text/rtf' => 'rtf',
+        'text/sgml' => 'sgml',
+        'text/tab-separated-values' => 'tsv',
+        'text/vnd.net2phone.commcenter.command' => 'ccc',
+        'text/vnd.sun.j2me.app-descriptor' => 'jad',
+        'text/vnd.wap.si' => 'si',
+        'text/vnd.wap.sl' => 'sl',
+        'text/vnd.wap.wml' => 'wml',
+        'text/vnd.wap.wmlscript' => 'wmls',
+        'text/x-hdml' => 'hdml',
+        'text/x-setext' => 'etx',
+        'text/x-sgml' => 'sgml',
+        'text/x-speech' => 'talk',
+        'text/x-vcalendar' => 'vcs',
+        'text/x-vcard' => 'vcf',
+        'text/xml' => 'xml',
+        'ulead/vrml' => 'uvr',
+        'video/3gpp' => '3gp',
+        'video/dl' => 'dl',
+        'video/gl' => 'gl',
+        'video/mj2' => 'mj2',
+        'video/mpeg' => 'mpeg',
+        'video/quicktime' => 'mov',
+        'video/vdo' => 'vdo',
+        'video/vivo' => 'viv',
+        'video/vnd.fvt' => 'fvt',
+        'video/vnd.mpegurl' => 'mxu',
+        'video/vnd.nokia.interleaved-multimedia' => 'nim',
+        'video/vnd.objectvideo' => 'mp4',
+        'video/vnd.sealed.mpeg1' => 's11',
+        'video/vnd.sealed.mpeg4' => 'smpg',
+        'video/vnd.sealed.swf' => 'sswf',
+        'video/vnd.sealedmedia.softseal.mov' => 'smov',
+        'video/vnd.vivo' => 'vivo',
+        'video/x-fli' => 'fli',
+        'video/x-ms-asf' => 'asf',
+        'video/x-ms-wmv' => 'wmv',
+        'video/x-msvideo' => 'avi',
+        'video/x-sgi-movie' => 'movie',
+        'x-chemical/x-pdb' => 'pdb',
+        'x-chemical/x-xyz' => 'xyz',
+        'x-conference/x-cooltalk' => 'ice',
+        'x-drawing/dwf' => 'dwf',
+        'x-world/x-d96' => 'd',
+        'x-world/x-svr' => 'svr',
+        'x-world/x-vream' => 'vrw',
+        'x-world/x-vrml' => 'wrl',
+    );
+
+    /**
+     * Constructs a new file from the given path.
+     *
+     * @param string $path The path to the file
+     *
+     * @throws FileNotFoundException If the given path is not a file
+     *
+     * @api
+     */
+    public function __construct($path)
+    {
+        if (!is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        parent::__construct($path);
+    }
+
+    /**
+     * Returns the extension based on the mime type.
+     *
+     * If the mime type is unknown, returns null.
+     *
+     * @return string|null The guessed extension or null if it cannot be guessed
+     *
+     * @api
+     */
+    public function guessExtension()
+    {
+        $type = $this->getMimeType();
+
+        return isset(static::$defaultExtensions[$type]) ? static::$defaultExtensions[$type] : null;
+    }
+
+    /**
+     * Returns the mime type of the file.
+     *
+     * The mime type is guessed using the functions finfo(), mime_content_type()
+     * and the system binary "file" (in this order), depending on which of those
+     * is available on the current operating system.
+     *
+     * @return string|null The guessed mime type (i.e. "application/pdf")
+     *
+     * @api
+     */
+    public function getMimeType()
+    {
+        $guesser = MimeTypeGuesser::getInstance();
+
+        return $guesser->guess($this->getPathname());
+    }
+
+    /**
+     * Returns the extension of the file.
+     *
+     * \SplFileInfo::getExtension() is not available before PHP 5.3.6
+     *
+     * @return string The extension
+     *
+     * @api
+     */
+    public function getExtension()
+    {
+        return pathinfo($this->getBasename(), PATHINFO_EXTENSION);
+    }
+
+    /**
+     * Moves the file to a new location.
+     *
+     * @param string $directory The destination folder
+     * @param string $name      The new file name
+     *
+     * @return File A File object representing the new file
+     *
+     * @throws FileException if the target file could not be created
+     *
+     * @api
+     */
+    public function move($directory, $name = null)
+    {
+        if (!is_dir($directory)) {
+            if (false === @mkdir($directory, 0777, true)) {
+                throw new FileException(sprintf('Unable to create the "%s" directory', $directory));
+            }
+        } elseif (!is_writable($directory)) {
+            throw new FileException(sprintf('Unable to write in the "%s" directory', $directory));
+        }
+
+        $target = $directory.DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : basename($name));
+
+        if (!@rename($this->getPathname(), $target)) {
+            $error = error_get_last();
+            throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message'])));
+        }
+
+        chmod($target, 0666);
+
+        return new File($target);
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/MimeType/ContentTypeMimeTypeGuesser.php b/includes/Symfony/Component/HttpFoundation/File/MimeType/ContentTypeMimeTypeGuesser.php
new file mode 100644
index 000000000000..fb900b2bc28d
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/MimeType/ContentTypeMimeTypeGuesser.php
@@ -0,0 +1,62 @@
+<?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\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type using the PHP function mime_content_type().
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class ContentTypeMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+    /**
+     * Returns whether this guesser is supported on the current OS/PHP setup
+     *
+     * @return Boolean
+     */
+    static public function isSupported()
+    {
+        return function_exists('mime_content_type');
+    }
+
+    /**
+     * Guesses the mime type of the file with the given path
+     *
+     * @see MimeTypeGuesserInterface::guess()
+     */
+    public function guess($path)
+    {
+        if (!is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        if (!is_readable($path)) {
+            throw new AccessDeniedException($path);
+        }
+
+        if (!self::isSupported()) {
+            return null;
+        }
+
+        $type = mime_content_type($path);
+
+        // remove charset (added as of PHP 5.3)
+        if (false !== $pos = strpos($type, ';')) {
+            $type = substr($type, 0, $pos);
+        }
+
+        return $type;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/includes/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php
new file mode 100644
index 000000000000..1b869f22cb3e
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php
@@ -0,0 +1,71 @@
+<?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\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type with the binary "file" (only available on *nix)
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+    /**
+     * Returns whether this guesser is supported on the current OS
+     *
+     * @return Boolean
+     */
+    static public function isSupported()
+    {
+        return !strstr(PHP_OS, 'WIN');
+    }
+    /**
+     * Guesses the mime type of the file with the given path
+     *
+     * @see MimeTypeGuesserInterface::guess()
+     */
+    public function guess($path)
+    {
+        if (!is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        if (!is_readable($path)) {
+            throw new AccessDeniedException($path);
+        }
+
+        if (!self::isSupported()) {
+            return null;
+        }
+
+        ob_start();
+
+        // need to use --mime instead of -i. see #6641
+        passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($path)), $return);
+        if ($return > 0) {
+            ob_end_clean();
+
+            return null;
+        }
+
+        $type = trim(ob_get_clean());
+
+        if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-]+)#i', $type, $match)) {
+            // it's not a type, but an error message
+            return null;
+        }
+
+        return $match[1];
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php b/includes/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php
new file mode 100644
index 000000000000..45d5a086eda3
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.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\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * Guesses the mime type using the PECL extension FileInfo
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+    /**
+     * Returns whether this guesser is supported on the current OS/PHP setup
+     *
+     * @return Boolean
+     */
+    static public function isSupported()
+    {
+        return function_exists('finfo_open');
+    }
+
+    /**
+     * Guesses the mime type of the file with the given path
+     *
+     * @see MimeTypeGuesserInterface::guess()
+     */
+    public function guess($path)
+    {
+        if (!is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        if (!is_readable($path)) {
+            throw new AccessDeniedException($path);
+        }
+
+        if (!self::isSupported()) {
+            return null;
+        }
+
+        if (!$finfo = new \finfo(FILEINFO_MIME_TYPE)) {
+            return null;
+        }
+
+        return $finfo->file($path);
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php b/includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php
new file mode 100644
index 000000000000..23dd46324d7b
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php
@@ -0,0 +1,125 @@
+<?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\HttpFoundation\File\MimeType;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+
+/**
+ * A singleton mime type guesser.
+ *
+ * By default, all mime type guessers provided by the framework are installed
+ * (if available on the current OS/PHP setup). You can register custom
+ * guessers by calling the register() method on the singleton instance.
+ *
+ * <code>
+ * $guesser = MimeTypeGuesser::getInstance();
+ * $guesser->register(new MyCustomMimeTypeGuesser());
+ * </code>
+ *
+ * The last registered guesser is preferred over previously registered ones.
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class MimeTypeGuesser implements MimeTypeGuesserInterface
+{
+    /**
+     * The singleton instance
+     * @var MimeTypeGuesser
+     */
+    static private $instance = null;
+
+    /**
+     * All registered MimeTypeGuesserInterface instances
+     * @var array
+     */
+    protected $guessers = array();
+
+    /**
+     * Returns the singleton instance
+     *
+     * @return MimeTypeGuesser
+     */
+    static public function getInstance()
+    {
+        if (null === self::$instance) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * Registers all natively provided mime type guessers
+     */
+    private function __construct()
+    {
+        if (FileBinaryMimeTypeGuesser::isSupported()) {
+            $this->register(new FileBinaryMimeTypeGuesser());
+        }
+
+        if (ContentTypeMimeTypeGuesser::isSupported()) {
+            $this->register(new ContentTypeMimeTypeGuesser());
+        }
+
+        if (FileinfoMimeTypeGuesser::isSupported()) {
+            $this->register(new FileinfoMimeTypeGuesser());
+        }
+    }
+
+    /**
+     * Registers a new mime type guesser
+     *
+     * When guessing, this guesser is preferred over previously registered ones.
+     *
+     * @param MimeTypeGuesserInterface $guesser
+     */
+    public function register(MimeTypeGuesserInterface $guesser)
+    {
+        array_unshift($this->guessers, $guesser);
+    }
+
+    /**
+     * Tries to guess the mime type of the given file
+     *
+     * The file is passed to each registered mime type guesser in reverse order
+     * of their registration (last registered is queried first). Once a guesser
+     * returns a value that is not NULL, this method terminates and returns the
+     * value.
+     *
+     * @param  string $path   The path to the file
+     * @return string         The mime type or NULL, if none could be guessed
+     * @throws FileException  If the file does not exist
+     */
+    public function guess($path)
+    {
+        if (!is_file($path)) {
+            throw new FileNotFoundException($path);
+        }
+
+        if (!is_readable($path)) {
+            throw new AccessDeniedException($path);
+        }
+
+        $mimeType = null;
+
+        foreach ($this->guessers as $guesser) {
+            $mimeType = $guesser->guess($path);
+
+            if (null !== $mimeType) {
+                break;
+            }
+        }
+
+        return $mimeType;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php b/includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php
new file mode 100644
index 000000000000..c11158396fe4
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.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\HttpFoundation\File\MimeType;
+
+/**
+ * Guesses the mime type of a file
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+interface MimeTypeGuesserInterface
+{
+    /**
+     * Guesses the mime type of the file with the given path
+     *
+     * @param  string $path   The path to the file
+     * @return string         The mime type or NULL, if none could be guessed
+     * @throws FileNotFoundException  If the file does not exist
+     * @throws AccessDeniedException  If the file could not be read
+     */
+    function guess($path);
+}
diff --git a/includes/Symfony/Component/HttpFoundation/File/UploadedFile.php b/includes/Symfony/Component/HttpFoundation/File/UploadedFile.php
new file mode 100644
index 000000000000..936ed7057955
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/File/UploadedFile.php
@@ -0,0 +1,225 @@
+<?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\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+
+/**
+ * A file uploaded through a form.
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ * @author Florian Eckerstorfer <florian@eckerstorfer.org>
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class UploadedFile extends File
+{
+    /**
+     * Whether the test mode is activated.
+     *
+     * Local files are used in test mode hence the code should not enforce HTTP uploads.
+     *
+     * @var Boolean
+     */
+    private $test = false;
+
+    /**
+     * The original name of the uploaded file.
+     *
+     * @var string
+     */
+    private $originalName;
+
+    /**
+     * The mime type provided by the uploader.
+     *
+     * @var string
+     */
+    private $mimeType;
+
+    /**
+     * The file size provided by the uploader.
+     *
+     * @var string
+     */
+    private $size;
+
+    /**
+     * The UPLOAD_ERR_XXX constant provided by the uploader.
+     *
+     * @var integer
+     */
+    private $error;
+
+    /**
+     * Accepts the information of the uploaded file as provided by the PHP global $_FILES.
+     *
+     * The file object is only created when the uploaded file is valid (i.e. when the
+     * isValid() method returns true). Otherwise the only methods that could be called
+     * on an UploadedFile instance are:
+     *
+     *   * getClientOriginalName,
+     *   * getClientMimeType,
+     *   * isValid,
+     *   * getError.
+     *
+     * Calling any other method on an non-valid instance will cause an unpredictable result.
+     *
+     * @param string  $path         The full temporary path to the file
+     * @param string  $originalName The original file name
+     * @param string  $mimeType     The type of the file as provided by PHP
+     * @param integer $size         The file size
+     * @param integer $error        The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants)
+     * @param Boolean $test         Whether the test mode is active
+     *
+     * @throws FileException         If file_uploads is disabled
+     * @throws FileNotFoundException If the file does not exist
+     *
+     * @api
+     */
+    public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false)
+    {
+        if (!ini_get('file_uploads')) {
+            throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path')));
+        }
+
+        $this->originalName = basename($originalName);
+        $this->mimeType = $mimeType ?: 'application/octet-stream';
+        $this->size = $size;
+        $this->error = $error ?: UPLOAD_ERR_OK;
+        $this->test = (Boolean) $test;
+
+        if (UPLOAD_ERR_OK === $this->error) {
+            parent::__construct($path);
+        }
+    }
+
+    /**
+     * Returns the original file name.
+     *
+     * It is extracted from the request from which the file has been uploaded.
+     * Then is should not be considered as a safe value.
+     *
+     * @return string|null The original name
+     *
+     * @api
+     */
+    public function getClientOriginalName()
+    {
+        return $this->originalName;
+    }
+
+    /**
+     * Returns the file mime type.
+     *
+     * It is extracted from the request from which the file has been uploaded.
+     * Then is should not be considered as a safe value.
+     *
+     * @return string|null The mime type
+     *
+     * @api
+     */
+    public function getClientMimeType()
+    {
+        return $this->mimeType;
+    }
+
+    /**
+     * Returns the file size.
+     *
+     * It is extracted from the request from which the file has been uploaded.
+     * Then is should not be considered as a safe value.
+     *
+     * @return integer|null The file size
+     *
+     * @api
+     */
+    public function getClientSize()
+    {
+        return $this->size;
+    }
+
+    /**
+     * Returns the upload error.
+     *
+     * If the upload was successful, the constant UPLOAD_ERR_OK is returned.
+     * Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
+     *
+     * @return integer The upload error
+     *
+     * @api
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * Returns whether the file was uploaded successfully.
+     *
+     * @return Boolean  True if no error occurred during uploading
+     *
+     * @api
+     */
+    public function isValid()
+    {
+        return $this->error === UPLOAD_ERR_OK;
+    }
+
+    /**
+     * Moves the file to a new location.
+     *
+     * @param string $directory The destination folder
+     * @param string $name      The new file name
+     *
+     * @return File A File object representing the new file
+     *
+     * @throws FileException if the file has not been uploaded via Http
+     *
+     * @api
+     */
+    public function move($directory, $name = null)
+    {
+        if ($this->isValid() && ($this->test || is_uploaded_file($this->getPathname()))) {
+            return parent::move($directory, $name);
+        }
+
+        throw new FileException(sprintf('The file "%s" has not been uploaded via Http', $this->getPathname()));
+    }
+
+    /**
+     * Returns the maximum size of an uploaded file as configured in php.ini
+     *
+     * @return type The maximum size of an uploaded file in bytes
+     */
+    static public function getMaxFilesize()
+    {
+        $max = trim(ini_get('upload_max_filesize'));
+
+        if ('' === $max) {
+            return PHP_INT_MAX;
+        }
+
+        switch (strtolower(substr($max, -1))) {
+            case 'g':
+                $max *= 1024;
+            case 'm':
+                $max *= 1024;
+            case 'k':
+                $max *= 1024;
+        }
+
+        return (integer) $max;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/FileBag.php b/includes/Symfony/Component/HttpFoundation/FileBag.php
new file mode 100644
index 000000000000..602cff2b3cec
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/FileBag.php
@@ -0,0 +1,157 @@
+<?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\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+/**
+ * FileBag is a container for HTTP headers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ *
+ * @api
+ */
+class FileBag extends ParameterBag
+{
+    static private $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
+
+    /**
+     * Constructor.
+     *
+     * @param array $parameters An array of HTTP files
+     *
+     * @api
+     */
+    public function __construct(array $parameters = array())
+    {
+        $this->replace($parameters);
+    }
+
+    /**
+     * (non-PHPdoc)
+     * @see Symfony\Component\HttpFoundation\ParameterBag::replace()
+     *
+     * @api
+     */
+    public function replace(array $files = array())
+    {
+        $this->parameters = array();
+        $this->add($files);
+    }
+
+    /**
+     * (non-PHPdoc)
+     * @see Symfony\Component\HttpFoundation\ParameterBag::set()
+     *
+     * @api
+     */
+    public function set($key, $value)
+    {
+        if (is_array($value) || $value instanceof UploadedFile) {
+            parent::set($key, $this->convertFileInformation($value));
+        } else {
+            throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
+        }
+    }
+
+    /**
+     * (non-PHPdoc)
+     * @see Symfony\Component\HttpFoundation\ParameterBag::add()
+     *
+     * @api
+     */
+    public function add(array $files = array())
+    {
+        foreach ($files as $key => $file) {
+            $this->set($key, $file);
+        }
+    }
+
+    /**
+     * Converts uploaded files to UploadedFile instances.
+     *
+     * @param  array|UploadedFile $file A (multi-dimensional) array of uploaded file information
+     *
+     * @return array A (multi-dimensional) array of UploadedFile instances
+     */
+    protected function convertFileInformation($file)
+    {
+        if ($file instanceof UploadedFile) {
+            return $file;
+        }
+
+        $file = $this->fixPhpFilesArray($file);
+        if (is_array($file)) {
+            $keys = array_keys($file);
+            sort($keys);
+
+            if ($keys == self::$fileKeys) {
+                if (UPLOAD_ERR_NO_FILE == $file['error']) {
+                    $file = null;
+                } else {
+                    $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']);
+                }
+            } else {
+                $file = array_map(array($this, 'convertFileInformation'), $file);
+            }
+        }
+
+        return $file;
+    }
+
+    /**
+     * Fixes a malformed PHP $_FILES array.
+     *
+     * PHP has a bug that the format of the $_FILES array differs, depending on
+     * whether the uploaded file fields had normal field names or array-like
+     * field names ("normal" vs. "parent[child]").
+     *
+     * This method fixes the array to look like the "normal" $_FILES array.
+     *
+     * It's safe to pass an already converted array, in which case this method
+     * just returns the original array unmodified.
+     *
+     * @param  array $data
+     * @return array
+     */
+    protected function fixPhpFilesArray($data)
+    {
+        if (!is_array($data)) {
+            return $data;
+        }
+
+        $keys = array_keys($data);
+        sort($keys);
+
+        if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) {
+            return $data;
+        }
+
+        $files = $data;
+        foreach (self::$fileKeys as $k) {
+            unset($files[$k]);
+        }
+
+        foreach (array_keys($data['name']) as $key) {
+            $files[$key] = $this->fixPhpFilesArray(array(
+                'error'    => $data['error'][$key],
+                'name'     => $data['name'][$key],
+                'type'     => $data['type'][$key],
+                'tmp_name' => $data['tmp_name'][$key],
+                'size'     => $data['size'][$key]
+            ));
+        }
+
+        return $files;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/HeaderBag.php b/includes/Symfony/Component/HttpFoundation/HeaderBag.php
new file mode 100644
index 000000000000..f614b094fd26
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/HeaderBag.php
@@ -0,0 +1,306 @@
+<?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\HttpFoundation;
+
+/**
+ * HeaderBag is a container for HTTP headers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class HeaderBag
+{
+    protected $headers;
+    protected $cacheControl;
+
+    /**
+     * Constructor.
+     *
+     * @param array $headers An array of HTTP headers
+     *
+     * @api
+     */
+    public function __construct(array $headers = array())
+    {
+        $this->cacheControl = array();
+        $this->headers = array();
+        foreach ($headers as $key => $values) {
+            $this->set($key, $values);
+        }
+    }
+
+    /**
+     * Returns the headers as a string.
+     *
+     * @return string The headers
+     */
+    public function __toString()
+    {
+        if (!$this->headers) {
+            return '';
+        }
+
+        $beautifier = function ($name) {
+            return preg_replace_callback('/\-(.)/', function ($match) { return '-'.strtoupper($match[1]); }, ucfirst($name));
+        };
+
+        $max = max(array_map('strlen', array_keys($this->headers))) + 1;
+        $content = '';
+        ksort($this->headers);
+        foreach ($this->headers as $name => $values) {
+            foreach ($values as $value) {
+                $content .= sprintf("%-{$max}s %s\r\n", $beautifier($name).':', $value);
+            }
+        }
+
+        return $content;
+    }
+
+    /**
+     * Returns the headers.
+     *
+     * @return array An array of headers
+     *
+     * @api
+     */
+    public function all()
+    {
+        return $this->headers;
+    }
+
+    /**
+     * Returns the parameter keys.
+     *
+     * @return array An array of parameter keys
+     *
+     * @api
+     */
+    public function keys()
+    {
+        return array_keys($this->headers);
+    }
+
+    /**
+     * Replaces the current HTTP headers by a new set.
+     *
+     * @param array  $headers An array of HTTP headers
+     *
+     * @api
+     */
+    public function replace(array $headers = array())
+    {
+        $this->headers = array();
+        $this->add($headers);
+    }
+
+    /**
+     * Adds new headers the current HTTP headers set.
+     *
+     * @param array  $headers An array of HTTP headers
+     *
+     * @api
+     */
+    public function add(array $headers)
+    {
+        foreach ($headers as $key => $values) {
+            $this->set($key, $values);
+        }
+    }
+
+    /**
+     * Returns a header value by name.
+     *
+     * @param string  $key     The header name
+     * @param mixed   $default The default value
+     * @param Boolean $first   Whether to return the first value or all header values
+     *
+     * @return string|array The first header value if $first is true, an array of values otherwise
+     *
+     * @api
+     */
+    public function get($key, $default = null, $first = true)
+    {
+        $key = strtr(strtolower($key), '_', '-');
+
+        if (!array_key_exists($key, $this->headers)) {
+            if (null === $default) {
+                return $first ? null : array();
+            }
+
+            return $first ? $default : array($default);
+        }
+
+        if ($first) {
+            return count($this->headers[$key]) ? $this->headers[$key][0] : $default;
+        }
+
+        return $this->headers[$key];
+    }
+
+    /**
+     * Sets a header by name.
+     *
+     * @param string       $key     The key
+     * @param string|array $values  The value or an array of values
+     * @param Boolean      $replace Whether to replace the actual value of not (true by default)
+     *
+     * @api
+     */
+    public function set($key, $values, $replace = true)
+    {
+        $key = strtr(strtolower($key), '_', '-');
+
+        $values = (array) $values;
+
+        if (true === $replace || !isset($this->headers[$key])) {
+            $this->headers[$key] = $values;
+        } else {
+            $this->headers[$key] = array_merge($this->headers[$key], $values);
+        }
+
+        if ('cache-control' === $key) {
+            $this->cacheControl = $this->parseCacheControl($values[0]);
+        }
+    }
+
+    /**
+     * Returns true if the HTTP header is defined.
+     *
+     * @param string $key The HTTP header
+     *
+     * @return Boolean true if the parameter exists, false otherwise
+     *
+     * @api
+     */
+    public function has($key)
+    {
+        return array_key_exists(strtr(strtolower($key), '_', '-'), $this->headers);
+    }
+
+    /**
+     * Returns true if the given HTTP header contains the given value.
+     *
+     * @param string $key   The HTTP header name
+     * @param string $value The HTTP value
+     *
+     * @return Boolean true if the value is contained in the header, false otherwise
+     *
+     * @api
+     */
+    public function contains($key, $value)
+    {
+        return in_array($value, $this->get($key, null, false));
+    }
+
+    /**
+     * Removes a header.
+     *
+     * @param string $key The HTTP header name
+     *
+     * @api
+     */
+    public function remove($key)
+    {
+        $key = strtr(strtolower($key), '_', '-');
+
+        unset($this->headers[$key]);
+
+        if ('cache-control' === $key) {
+            $this->cacheControl = array();
+        }
+    }
+
+    /**
+     * Returns the HTTP header value converted to a date.
+     *
+     * @param string    $key     The parameter key
+     * @param \DateTime $default The default value
+     *
+     * @return \DateTime The filtered value
+     *
+     * @api
+     */
+    public function getDate($key, \DateTime $default = null)
+    {
+        if (null === $value = $this->get($key)) {
+            return $default;
+        }
+
+        if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) {
+            throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
+        }
+
+        return $date;
+    }
+
+    public function addCacheControlDirective($key, $value = true)
+    {
+        $this->cacheControl[$key] = $value;
+
+        $this->set('Cache-Control', $this->getCacheControlHeader());
+    }
+
+    public function hasCacheControlDirective($key)
+    {
+        return array_key_exists($key, $this->cacheControl);
+    }
+
+    public function getCacheControlDirective($key)
+    {
+        return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
+    }
+
+    public function removeCacheControlDirective($key)
+    {
+        unset($this->cacheControl[$key]);
+
+        $this->set('Cache-Control', $this->getCacheControlHeader());
+    }
+
+    protected function getCacheControlHeader()
+    {
+        $parts = array();
+        ksort($this->cacheControl);
+        foreach ($this->cacheControl as $key => $value) {
+            if (true === $value) {
+                $parts[] = $key;
+            } else {
+                if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
+                    $value = '"'.$value.'"';
+                }
+
+                $parts[] = "$key=$value";
+            }
+        }
+
+        return implode(', ', $parts);
+    }
+
+    /**
+     * Parses a Cache-Control HTTP header.
+     *
+     * @param string $header The value of the Cache-Control HTTP header
+     *
+     * @return array An array representing the attribute values
+     */
+    protected function parseCacheControl($header)
+    {
+        $cacheControl = array();
+        preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER);
+        foreach ($matches as $match) {
+            $cacheControl[strtolower($match[1])] = isset($match[2]) && $match[2] ? $match[2] : (isset($match[3]) ? $match[3] : true);
+        }
+
+        return $cacheControl;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/LICENSE b/includes/Symfony/Component/HttpFoundation/LICENSE
new file mode 100644
index 000000000000..89df4481b950
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2011 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/includes/Symfony/Component/HttpFoundation/ParameterBag.php b/includes/Symfony/Component/HttpFoundation/ParameterBag.php
new file mode 100644
index 000000000000..3a38b5fab334
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/ParameterBag.php
@@ -0,0 +1,245 @@
+<?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\HttpFoundation;
+
+/**
+ * ParameterBag is a container for key/value pairs.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class ParameterBag
+{
+    protected $parameters;
+
+    /**
+     * Constructor.
+     *
+     * @param array $parameters An array of parameters
+     *
+     * @api
+     */
+    public function __construct(array $parameters = array())
+    {
+        $this->parameters = $parameters;
+    }
+
+    /**
+     * Returns the parameters.
+     *
+     * @return array An array of parameters
+     *
+     * @api
+     */
+    public function all()
+    {
+        return $this->parameters;
+    }
+
+    /**
+     * Returns the parameter keys.
+     *
+     * @return array An array of parameter keys
+     *
+     * @api
+     */
+    public function keys()
+    {
+        return array_keys($this->parameters);
+    }
+
+    /**
+     * Replaces the current parameters by a new set.
+     *
+     * @param array $parameters An array of parameters
+     *
+     * @api
+     */
+    public function replace(array $parameters = array())
+    {
+        $this->parameters = $parameters;
+    }
+
+    /**
+     * Adds parameters.
+     *
+     * @param array $parameters An array of parameters
+     *
+     * @api
+     */
+    public function add(array $parameters = array())
+    {
+        $this->parameters = array_replace($this->parameters, $parameters);
+    }
+
+    /**
+     * Returns a parameter by name.
+     *
+     * @param string  $path    The key
+     * @param mixed   $default The default value
+     * @param boolean $deep
+     *
+     * @api
+     */
+    public function get($path, $default = null, $deep = false)
+    {
+        if (!$deep || false === $pos = strpos($path, '[')) {
+            return array_key_exists($path, $this->parameters) ? $this->parameters[$path] : $default;
+        }
+
+        $root = substr($path, 0, $pos);
+        if (!array_key_exists($root, $this->parameters)) {
+            return $default;
+        }
+
+        $value = $this->parameters[$root];
+        $currentKey = null;
+        for ($i=$pos,$c=strlen($path); $i<$c; $i++) {
+            $char = $path[$i];
+
+            if ('[' === $char) {
+                if (null !== $currentKey) {
+                    throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "[" at position %d.', $i));
+                }
+
+                $currentKey = '';
+            } else if (']' === $char) {
+                if (null === $currentKey) {
+                    throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "]" at position %d.', $i));
+                }
+
+                if (!is_array($value) || !array_key_exists($currentKey, $value)) {
+                    return $default;
+                }
+
+                $value = $value[$currentKey];
+                $currentKey = null;
+            } else {
+                if (null === $currentKey) {
+                    throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "%s" at position %d.', $char, $i));
+                }
+
+                $currentKey .= $char;
+            }
+        }
+
+        if (null !== $currentKey) {
+            throw new \InvalidArgumentException(sprintf('Malformed path. Path must end with "]".'));
+        }
+
+        return $value;
+    }
+
+    /**
+     * Sets a parameter by name.
+     *
+     * @param string $key   The key
+     * @param mixed  $value The value
+     *
+     * @api
+     */
+    public function set($key, $value)
+    {
+        $this->parameters[$key] = $value;
+    }
+
+    /**
+     * Returns true if the parameter is defined.
+     *
+     * @param string $key The key
+     *
+     * @return Boolean true if the parameter exists, false otherwise
+     *
+     * @api
+     */
+    public function has($key)
+    {
+        return array_key_exists($key, $this->parameters);
+    }
+
+    /**
+     * Removes a parameter.
+     *
+     * @param string $key The key
+     *
+     * @api
+     */
+    public function remove($key)
+    {
+        unset($this->parameters[$key]);
+    }
+
+    /**
+     * Returns the alphabetic characters of the parameter value.
+     *
+     * @param string  $key     The parameter key
+     * @param mixed   $default The default value
+     * @param boolean $deep
+     *
+     * @return string The filtered value
+     *
+     * @api
+     */
+    public function getAlpha($key, $default = '', $deep = false)
+    {
+        return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default, $deep));
+    }
+
+    /**
+     * Returns the alphabetic characters and digits of the parameter value.
+     *
+     * @param string  $key     The parameter key
+     * @param mixed   $default The default value
+     * @param boolean $deep
+     *
+     * @return string The filtered value
+     *
+     * @api
+     */
+    public function getAlnum($key, $default = '', $deep = false)
+    {
+        return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default, $deep));
+    }
+
+    /**
+     * Returns the digits of the parameter value.
+     *
+     * @param string  $key     The parameter key
+     * @param mixed   $default The default value
+     * @param boolean $deep
+     *
+     * @return string The filtered value
+     *
+     * @api
+     */
+    public function getDigits($key, $default = '', $deep = false)
+    {
+        return preg_replace('/[^[:digit:]]/', '', $this->get($key, $default, $deep));
+    }
+
+    /**
+     * Returns the parameter value converted to integer.
+     *
+     * @param string  $key     The parameter key
+     * @param mixed   $default The default value
+     * @param boolean $deep
+     *
+     * @return string The filtered value
+     *
+     * @api
+     */
+    public function getInt($key, $default = 0, $deep = false)
+    {
+        return (int) $this->get($key, $default, $deep);
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/RedirectResponse.php b/includes/Symfony/Component/HttpFoundation/RedirectResponse.php
new file mode 100644
index 000000000000..3318b12bb389
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/RedirectResponse.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\HttpFoundation;
+
+/**
+ * RedirectResponse represents an HTTP response doing a redirect.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class RedirectResponse extends Response
+{
+    /**
+     * Creates a redirect response so that it conforms to the rules defined for a redirect status code.
+     *
+     * @param string  $url    The URL to redirect to
+     * @param integer $status The status code (302 by default)
+     *
+     * @see http://tools.ietf.org/html/rfc2616#section-10.3
+     *
+     * @api
+     */
+    public function __construct($url, $status = 302)
+    {
+        if (empty($url)) {
+            throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
+        }
+
+        parent::__construct(
+            sprintf('<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+        <meta http-equiv="refresh" content="1;url=%1$s" />
+
+        <title>Redirecting to %1$s</title>
+    </head>
+    <body>
+        Redirecting to <a href="%1$s">%1$s</a>.
+    </body>
+</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')),
+            $status,
+            array('Location' => $url)
+        );
+
+        if (!$this->isRedirect()) {
+            throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
+        }
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/Request.php b/includes/Symfony/Component/HttpFoundation/Request.php
new file mode 100644
index 000000000000..e66abb7484ea
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/Request.php
@@ -0,0 +1,1217 @@
+<?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\HttpFoundation;
+
+/**
+ * Request represents an HTTP request.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Request
+{
+    static protected $trustProxy = false;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\ParameterBag
+     *
+     * @api
+     */
+    public $attributes;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\ParameterBag
+     *
+     * @api
+     */
+    public $request;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\ParameterBag
+     *
+     * @api
+     */
+    public $query;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\ServerBag
+     *
+     * @api
+     */
+    public $server;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\FileBag
+     *
+     * @api
+     */
+    public $files;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\ParameterBag
+     *
+     * @api
+     */
+    public $cookies;
+
+    /**
+     * @var \Symfony\Component\HttpFoundation\HeaderBag
+     *
+     * @api
+     */
+    public $headers;
+
+    protected $content;
+    protected $languages;
+    protected $charsets;
+    protected $acceptableContentTypes;
+    protected $pathInfo;
+    protected $requestUri;
+    protected $baseUrl;
+    protected $basePath;
+    protected $method;
+    protected $format;
+    protected $session;
+
+    static protected $formats;
+
+    /**
+     * Constructor.
+     *
+     * @param array  $query      The GET parameters
+     * @param array  $request    The POST parameters
+     * @param array  $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+     * @param array  $cookies    The COOKIE parameters
+     * @param array  $files      The FILES parameters
+     * @param array  $server     The SERVER parameters
+     * @param string $content    The raw body data
+     *
+     * @api
+     */
+    public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+    {
+        $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
+    }
+
+    /**
+     * Sets the parameters for this request.
+     *
+     * This method also re-initializes all properties.
+     *
+     * @param array  $query      The GET parameters
+     * @param array  $request    The POST parameters
+     * @param array  $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+     * @param array  $cookies    The COOKIE parameters
+     * @param array  $files      The FILES parameters
+     * @param array  $server     The SERVER parameters
+     * @param string $content    The raw body data
+     *
+     * @api
+     */
+    public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+    {
+        $this->request = new ParameterBag($request);
+        $this->query = new ParameterBag($query);
+        $this->attributes = new ParameterBag($attributes);
+        $this->cookies = new ParameterBag($cookies);
+        $this->files = new FileBag($files);
+        $this->server = new ServerBag($server);
+        $this->headers = new HeaderBag($this->server->getHeaders());
+
+        $this->content = $content;
+        $this->languages = null;
+        $this->charsets = null;
+        $this->acceptableContentTypes = null;
+        $this->pathInfo = null;
+        $this->requestUri = null;
+        $this->baseUrl = null;
+        $this->basePath = null;
+        $this->method = null;
+        $this->format = null;
+    }
+
+    /**
+     * Creates a new request with values from PHP's super globals.
+     *
+     * @return Request A new request
+     *
+     * @api
+     */
+    static public function createFromGlobals()
+    {
+        $request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);
+
+        if (0 === strpos($request->server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
+            && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE'))
+        ) {
+            parse_str($request->getContent(), $data);
+            $request->request = new ParameterBag($data);
+        }
+
+        return $request;
+    }
+
+    /**
+     * Creates a Request based on a given URI and configuration.
+     *
+     * @param string $uri        The URI
+     * @param string $method     The HTTP method
+     * @param array  $parameters The request (GET) or query (POST) parameters
+     * @param array  $cookies    The request cookies ($_COOKIE)
+     * @param array  $files      The request files ($_FILES)
+     * @param array  $server     The server parameters ($_SERVER)
+     * @param string $content    The raw body data
+     *
+     * @return Request A Request instance
+     *
+     * @api
+     */
+    static public function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
+    {
+        $defaults = array(
+            'SERVER_NAME'          => 'localhost',
+            'SERVER_PORT'          => 80,
+            'HTTP_HOST'            => 'localhost',
+            'HTTP_USER_AGENT'      => 'Symfony/2.X',
+            'HTTP_ACCEPT'          => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+            'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
+            'HTTP_ACCEPT_CHARSET'  => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
+            'REMOTE_ADDR'          => '127.0.0.1',
+            'SCRIPT_NAME'          => '',
+            'SCRIPT_FILENAME'      => '',
+            'SERVER_PROTOCOL'      => 'HTTP/1.1',
+            'REQUEST_TIME'         => time(),
+        );
+
+        $components = parse_url($uri);
+        if (isset($components['host'])) {
+            $defaults['SERVER_NAME'] = $components['host'];
+            $defaults['HTTP_HOST'] = $components['host'];
+        }
+
+        if (isset($components['scheme'])) {
+            if ('https' === $components['scheme']) {
+                $defaults['HTTPS'] = 'on';
+                $defaults['SERVER_PORT'] = 443;
+            }
+        }
+
+        if (isset($components['port'])) {
+            $defaults['SERVER_PORT'] = $components['port'];
+            $defaults['HTTP_HOST'] = $defaults['HTTP_HOST'].':'.$components['port'];
+        }
+
+        if (!isset($components['path'])) {
+            $components['path'] = '';
+        }
+
+        if (in_array(strtoupper($method), array('POST', 'PUT', 'DELETE'))) {
+            $request = $parameters;
+            $query = array();
+            $defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+        } else {
+            $request = array();
+            $query = $parameters;
+            if (false !== $pos = strpos($uri, '?')) {
+                $qs = substr($uri, $pos + 1);
+                parse_str($qs, $params);
+
+                $query = array_merge($params, $query);
+            }
+        }
+
+        $queryString = isset($components['query']) ? html_entity_decode($components['query']) : '';
+        parse_str($queryString, $qs);
+        if (is_array($qs)) {
+            $query = array_replace($qs, $query);
+        }
+
+        $uri = $components['path'].($queryString ? '?'.$queryString : '');
+
+        $server = array_replace($defaults, $server, array(
+            'REQUEST_METHOD'       => strtoupper($method),
+            'PATH_INFO'            => '',
+            'REQUEST_URI'          => $uri,
+            'QUERY_STRING'         => $queryString,
+        ));
+
+        return new static($query, $request, array(), $cookies, $files, $server, $content);
+    }
+
+    /**
+     * Clones a request and overrides some of its parameters.
+     *
+     * @param array $query      The GET parameters
+     * @param array $request    The POST parameters
+     * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+     * @param array $cookies    The COOKIE parameters
+     * @param array $files      The FILES parameters
+     * @param array $server     The SERVER parameters
+     *
+     * @api
+     */
+    public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
+    {
+        $dup = clone $this;
+        if ($query !== null) {
+            $dup->query = new ParameterBag($query);
+        }
+        if ($request !== null) {
+            $dup->request = new ParameterBag($request);
+        }
+        if ($attributes !== null) {
+            $dup->attributes = new ParameterBag($attributes);
+        }
+        if ($cookies !== null) {
+            $dup->cookies = new ParameterBag($cookies);
+        }
+        if ($files !== null) {
+            $dup->files = new FileBag($files);
+        }
+        if ($server !== null) {
+            $dup->server = new ServerBag($server);
+            $dup->headers = new HeaderBag($dup->server->getHeaders());
+        }
+        $dup->languages = null;
+        $dup->charsets = null;
+        $dup->acceptableContentTypes = null;
+        $dup->pathInfo = null;
+        $dup->requestUri = null;
+        $dup->baseUrl = null;
+        $dup->basePath = null;
+        $dup->method = null;
+        $dup->format = null;
+
+        return $dup;
+    }
+
+    /**
+     * Clones the current request.
+     *
+     * Note that the session is not cloned as duplicated requests
+     * are most of the time sub-requests of the main one.
+     */
+    public function __clone()
+    {
+        $this->query      = clone $this->query;
+        $this->request    = clone $this->request;
+        $this->attributes = clone $this->attributes;
+        $this->cookies    = clone $this->cookies;
+        $this->files      = clone $this->files;
+        $this->server     = clone $this->server;
+        $this->headers    = clone $this->headers;
+    }
+
+    /**
+     * Returns the request as a string.
+     *
+     * @return string The request
+     */
+    public function __toString()
+    {
+        return
+            sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
+            $this->headers."\r\n".
+            $this->getContent();
+    }
+
+    /**
+     * Overrides the PHP global variables according to this request instance.
+     *
+     * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE, and $_FILES.
+     *
+     * @api
+     */
+    public function overrideGlobals()
+    {
+        $_GET = $this->query->all();
+        $_POST = $this->request->all();
+        $_SERVER = $this->server->all();
+        $_COOKIE = $this->cookies->all();
+        // FIXME: populate $_FILES
+
+        foreach ($this->headers->all() as $key => $value) {
+            $key = strtoupper(str_replace('-', '_', $key));
+            if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
+                $_SERVER[$key] = implode(', ', $value);
+            } else {
+                $_SERVER['HTTP_'.$key] = implode(', ', $value);
+            }
+        }
+
+        // FIXME: should read variables_order and request_order
+        // to know which globals to merge and in which order
+        $_REQUEST = array_merge($_GET, $_POST);
+    }
+
+    /**
+     * Trusts $_SERVER entries coming from proxies.
+     *
+     * You should only call this method if your application
+     * is hosted behind a reverse proxy that you manage.
+     *
+     * @api
+     */
+    static public function trustProxyData()
+    {
+        self::$trustProxy = true;
+    }
+
+    /**
+     * Gets a "parameter" value.
+     *
+     * This method is mainly useful for libraries that want to provide some flexibility.
+     *
+     * Order of precedence: GET, PATH, POST, COOKIE
+     * Avoid using this method in controllers:
+     *  * slow
+     *  * prefer to get from a "named" source
+     *
+     * @param string    $key        the key
+     * @param mixed     $default    the default value
+     * @param type      $deep       is parameter deep in multidimensional array
+     *
+     * @return mixed
+     */
+    public function get($key, $default = null, $deep = false)
+    {
+        return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep);
+    }
+
+    /**
+     * Gets the Session.
+     *
+     * @return Session|null The session
+     *
+     * @api
+     */
+    public function getSession()
+    {
+        return $this->session;
+    }
+
+    /**
+     * Whether the request contains a Session which was started in one of the
+     * previous requests.
+     *
+     * @return boolean
+     *
+     * @api
+     */
+    public function hasPreviousSession()
+    {
+        // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
+        return $this->cookies->has(session_name()) && null !== $this->session;
+    }
+
+    /**
+     * Whether the request contains a Session object.
+     *
+     * @return boolean
+     *
+     * @api
+     */
+    public function hasSession()
+    {
+        return null !== $this->session;
+    }
+
+    /**
+     * Sets the Session.
+     *
+     * @param Session $session The Session
+     *
+     * @api
+     */
+    public function setSession(Session $session)
+    {
+        $this->session = $session;
+    }
+
+    /**
+     * Returns the client IP address.
+     *
+     * @param  Boolean $proxy Whether the current request has been made behind a proxy or not
+     *
+     * @return string The client IP address
+     *
+     * @api
+     */
+    public function getClientIp($proxy = false)
+    {
+        if ($proxy) {
+            if ($this->server->has('HTTP_CLIENT_IP')) {
+                return $this->server->get('HTTP_CLIENT_IP');
+            } elseif (self::$trustProxy && $this->server->has('HTTP_X_FORWARDED_FOR')) {
+                return $this->server->get('HTTP_X_FORWARDED_FOR');
+            }
+        }
+
+        return $this->server->get('REMOTE_ADDR');
+    }
+
+    /**
+     * Returns current script name.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getScriptName()
+    {
+        return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
+    }
+
+    /**
+     * Returns the path being requested relative to the executed script.
+     *
+     * The path info always starts with a /.
+     *
+     * Suppose this request is instantiated from /mysite on localhost:
+     *
+     *  * http://localhost/mysite              returns an empty string
+     *  * http://localhost/mysite/about        returns '/about'
+     *  * http://localhost/mysite/about?var=1  returns '/about'
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getPathInfo()
+    {
+        if (null === $this->pathInfo) {
+            $this->pathInfo = $this->preparePathInfo();
+        }
+
+        return $this->pathInfo;
+    }
+
+    /**
+     * Returns the root path from which this request is executed.
+     *
+     * Suppose that an index.php file instantiates this request object:
+     *
+     *  * http://localhost/index.php        returns an empty string
+     *  * http://localhost/index.php/page   returns an empty string
+     *  * http://localhost/web/index.php    return '/web'
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getBasePath()
+    {
+        if (null === $this->basePath) {
+            $this->basePath = $this->prepareBasePath();
+        }
+
+        return $this->basePath;
+    }
+
+    /**
+     * Returns the root url from which this request is executed.
+     *
+     * The base URL never ends with a /.
+     *
+     * This is similar to getBasePath(), except that it also includes the
+     * script filename (e.g. index.php) if one exists.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getBaseUrl()
+    {
+        if (null === $this->baseUrl) {
+            $this->baseUrl = $this->prepareBaseUrl();
+        }
+
+        return $this->baseUrl;
+    }
+
+    /**
+     * Gets the request's scheme.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getScheme()
+    {
+        return $this->isSecure() ? 'https' : 'http';
+    }
+
+    /**
+     * Returns the port on which the request is made.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getPort()
+    {
+        return $this->headers->get('X-Forwarded-Port') ?: $this->server->get('SERVER_PORT');
+    }
+
+    /**
+     * Returns the HTTP host being requested.
+     *
+     * The port name will be appended to the host if it's non-standard.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getHttpHost()
+    {
+        $scheme = $this->getScheme();
+        $port   = $this->getPort();
+
+        if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
+            return $this->getHost();
+        }
+
+        return $this->getHost().':'.$port;
+    }
+
+    /**
+     * Returns the requested URI.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getRequestUri()
+    {
+        if (null === $this->requestUri) {
+            $this->requestUri = $this->prepareRequestUri();
+        }
+
+        return $this->requestUri;
+    }
+
+    /**
+     * Generates a normalized URI for the Request.
+     *
+     * @return string A normalized URI for the Request
+     *
+     * @see getQueryString()
+     *
+     * @api
+     */
+    public function getUri()
+    {
+        $qs = $this->getQueryString();
+        if (null !== $qs) {
+            $qs = '?'.$qs;
+        }
+
+        return $this->getScheme().'://'.$this->getHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
+    }
+
+    /**
+     * Generates a normalized URI for the given path.
+     *
+     * @param string $path A path to use instead of the current one
+     *
+     * @return string The normalized URI for the path
+     *
+     * @api
+     */
+    public function getUriForPath($path)
+    {
+        return $this->getScheme().'://'.$this->getHttpHost().$this->getBaseUrl().$path;
+    }
+
+    /**
+     * Generates the normalized query string for the Request.
+     *
+     * It builds a normalized query string, where keys/value pairs are alphabetized
+     * and have consistent escaping.
+     *
+     * @return string A normalized query string for the Request
+     *
+     * @api
+     */
+    public function getQueryString()
+    {
+        if (!$qs = $this->server->get('QUERY_STRING')) {
+            return null;
+        }
+
+        $parts = array();
+        $order = array();
+
+        foreach (explode('&', $qs) as $segment) {
+            if (false === strpos($segment, '=')) {
+                $parts[] = $segment;
+                $order[] = $segment;
+            } else {
+                $tmp = explode('=', rawurldecode($segment), 2);
+                $parts[] = rawurlencode($tmp[0]).'='.rawurlencode($tmp[1]);
+                $order[] = $tmp[0];
+            }
+        }
+        array_multisort($order, SORT_ASC, $parts);
+
+        return implode('&', $parts);
+    }
+
+    /**
+     * Checks whether the request is secure or not.
+     *
+     * @return Boolean
+     *
+     * @api
+     */
+    public function isSecure()
+    {
+        return (
+            (strtolower($this->server->get('HTTPS')) == 'on' || $this->server->get('HTTPS') == 1)
+            ||
+            (self::$trustProxy && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1)
+            ||
+            (self::$trustProxy && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https')
+        );
+    }
+
+    /**
+     * Returns the host name.
+     *
+     * @return string
+     *
+     * @api
+     */
+    public function getHost()
+    {
+        if (self::$trustProxy && $host = $this->headers->get('X_FORWARDED_HOST')) {
+            $elements = explode(',', $host);
+
+            $host = trim($elements[count($elements) - 1]);
+        } else {
+            if (!$host = $this->headers->get('HOST')) {
+                if (!$host = $this->server->get('SERVER_NAME')) {
+                    $host = $this->server->get('SERVER_ADDR', '');
+                }
+            }
+        }
+
+        // Remove port number from host
+        $host = preg_replace('/:\d+$/', '', $host);
+
+        return trim($host);
+    }
+
+    /**
+     * Sets the request method.
+     *
+     * @param string $method
+     *
+     * @api
+     */
+    public function setMethod($method)
+    {
+        $this->method = null;
+        $this->server->set('REQUEST_METHOD', $method);
+    }
+
+    /**
+     * Gets the request method.
+     *
+     * The method is always an uppercased string.
+     *
+     * @return string The request method
+     *
+     * @api
+     */
+    public function getMethod()
+    {
+        if (null === $this->method) {
+            $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+            if ('POST' === $this->method) {
+                $this->method = strtoupper($this->headers->get('X-HTTP-METHOD-OVERRIDE', $this->request->get('_method', 'POST')));
+            }
+        }
+
+        return $this->method;
+    }
+
+    /**
+     * Gets the mime type associated with the format.
+     *
+     * @param  string $format  The format
+     *
+     * @return string The associated mime type (null if not found)
+     *
+     * @api
+     */
+    public function getMimeType($format)
+    {
+        if (null === static::$formats) {
+            static::initializeFormats();
+        }
+
+        return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
+    }
+
+    /**
+     * Gets the format associated with the mime type.
+     *
+     * @param  string $mimeType  The associated mime type
+     *
+     * @return string The format (null if not found)
+     *
+     * @api
+     */
+    public function getFormat($mimeType)
+    {
+        if (false !== $pos = strpos($mimeType, ';')) {
+            $mimeType = substr($mimeType, 0, $pos);
+        }
+
+        if (null === static::$formats) {
+            static::initializeFormats();
+        }
+
+        foreach (static::$formats as $format => $mimeTypes) {
+            if (in_array($mimeType, (array) $mimeTypes)) {
+                return $format;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Associates a format with mime types.
+     *
+     * @param string       $format     The format
+     * @param string|array $mimeTypes  The associated mime types (the preferred one must be the first as it will be used as the content type)
+     *
+     * @api
+     */
+    public function setFormat($format, $mimeTypes)
+    {
+        if (null === static::$formats) {
+            static::initializeFormats();
+        }
+
+        static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
+    }
+
+    /**
+     * Gets the request format.
+     *
+     * Here is the process to determine the format:
+     *
+     *  * format defined by the user (with setRequestFormat())
+     *  * _format request parameter
+     *  * $default
+     *
+     * @param string  $default     The default format
+     *
+     * @return string The request format
+     *
+     * @api
+     */
+    public function getRequestFormat($default = 'html')
+    {
+        if (null === $this->format) {
+            $this->format = $this->get('_format', $default);
+        }
+
+        return $this->format;
+    }
+
+    /**
+     * Sets the request format.
+     *
+     * @param string $format The request format.
+     *
+     * @api
+     */
+    public function setRequestFormat($format)
+    {
+        $this->format = $format;
+    }
+
+    /**
+     * Checks whether the method is safe or not.
+     *
+     * @return Boolean
+     *
+     * @api
+     */
+    public function isMethodSafe()
+    {
+        return in_array($this->getMethod(), array('GET', 'HEAD'));
+    }
+
+    /**
+     * Returns the request body content.
+     *
+     * @param  Boolean $asResource If true, a resource will be returned
+     *
+     * @return string|resource The request body content or a resource to read the body stream.
+     */
+    public function getContent($asResource = false)
+    {
+        if (false === $this->content || (true === $asResource && null !== $this->content)) {
+            throw new \LogicException('getContent() can only be called once when using the resource return type.');
+        }
+
+        if (true === $asResource) {
+            $this->content = false;
+
+            return fopen('php://input', 'rb');
+        }
+
+        if (null === $this->content) {
+            $this->content = file_get_contents('php://input');
+        }
+
+        return $this->content;
+    }
+
+    /**
+     * Gets the Etags.
+     *
+     * @return array The entity tags
+     */
+    public function getETags()
+    {
+        return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
+    }
+
+    public function isNoCache()
+    {
+        return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
+    }
+
+    /**
+     * Returns the preferred language.
+     *
+     * @param  array  $locales  An array of ordered available locales
+     *
+     * @return string The preferred locale
+     *
+     * @api
+     */
+    public function getPreferredLanguage(array $locales = null)
+    {
+        $preferredLanguages = $this->getLanguages();
+
+        if (null === $locales) {
+            return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
+        }
+
+        if (!$preferredLanguages) {
+            return $locales[0];
+        }
+
+        $preferredLanguages = array_values(array_intersect($preferredLanguages, $locales));
+
+        return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
+    }
+
+    /**
+     * Gets a list of languages acceptable by the client browser.
+     *
+     * @return array Languages ordered in the user browser preferences
+     *
+     * @api
+     */
+    public function getLanguages()
+    {
+        if (null !== $this->languages) {
+            return $this->languages;
+        }
+
+        $languages = $this->splitHttpAcceptHeader($this->headers->get('Accept-Language'));
+        $this->languages = array();
+        foreach ($languages as $lang => $q) {
+            if (strstr($lang, '-')) {
+                $codes = explode('-', $lang);
+                if ($codes[0] == 'i') {
+                    // Language not listed in ISO 639 that are not variants
+                    // of any listed language, which can be registered with the
+                    // i-prefix, such as i-cherokee
+                    if (count($codes) > 1) {
+                        $lang = $codes[1];
+                    }
+                } else {
+                    for ($i = 0, $max = count($codes); $i < $max; $i++) {
+                        if ($i == 0) {
+                            $lang = strtolower($codes[0]);
+                        } else {
+                            $lang .= '_'.strtoupper($codes[$i]);
+                        }
+                    }
+                }
+            }
+
+            $this->languages[] = $lang;
+        }
+
+        return $this->languages;
+    }
+
+    /**
+     * Gets a list of charsets acceptable by the client browser.
+     *
+     * @return array List of charsets in preferable order
+     *
+     * @api
+     */
+    public function getCharsets()
+    {
+        if (null !== $this->charsets) {
+            return $this->charsets;
+        }
+
+        return $this->charsets = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept-Charset')));
+    }
+
+    /**
+     * Gets a list of content types acceptable by the client browser
+     *
+     * @return array List of content types in preferable order
+     *
+     * @api
+     */
+    public function getAcceptableContentTypes()
+    {
+        if (null !== $this->acceptableContentTypes) {
+            return $this->acceptableContentTypes;
+        }
+
+        return $this->acceptableContentTypes = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept')));
+    }
+
+    /**
+     * Returns true if the request is a XMLHttpRequest.
+     *
+     * It works if your JavaScript library set an X-Requested-With HTTP header.
+     * It is known to work with Prototype, Mootools, jQuery.
+     *
+     * @return Boolean true if the request is an XMLHttpRequest, false otherwise
+     *
+     * @api
+     */
+    public function isXmlHttpRequest()
+    {
+        return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
+    }
+
+    /**
+     * Splits an Accept-* HTTP header.
+     *
+     * @param string $header  Header to split
+     */
+    public function splitHttpAcceptHeader($header)
+    {
+        if (!$header) {
+            return array();
+        }
+
+        $values = array();
+        foreach (array_filter(explode(',', $header)) as $value) {
+            // Cut off any q-value that might come after a semi-colon
+            if ($pos = strpos($value, ';')) {
+                $q     = (float) trim(substr($value, strpos($value, '=') + 1));
+                $value = trim(substr($value, 0, $pos));
+            } else {
+                $q = 1;
+            }
+
+            if (0 < $q) {
+                $values[trim($value)] = $q;
+            }
+        }
+
+        arsort($values);
+        reset($values);
+
+        return $values;
+    }
+
+    /*
+     * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
+     *
+     * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+     *
+     * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+     */
+
+    protected function prepareRequestUri()
+    {
+        $requestUri = '';
+
+        if ($this->headers->has('X_REWRITE_URL')) {
+            // check this first so IIS will catch
+            $requestUri = $this->headers->get('X_REWRITE_URL');
+        } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') {
+            // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem)
+            $requestUri = $this->server->get('UNENCODED_URL');
+        } elseif ($this->server->has('REQUEST_URI')) {
+            $requestUri = $this->server->get('REQUEST_URI');
+            // HTTP proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path
+            $schemeAndHttpHost = $this->getScheme().'://'.$this->getHttpHost();
+            if (strpos($requestUri, $schemeAndHttpHost) === 0) {
+                $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
+            }
+        } elseif ($this->server->has('ORIG_PATH_INFO')) {
+            // IIS 5.0, PHP as CGI
+            $requestUri = $this->server->get('ORIG_PATH_INFO');
+            if ($this->server->get('QUERY_STRING')) {
+                $requestUri .= '?'.$this->server->get('QUERY_STRING');
+            }
+        }
+
+        return $requestUri;
+    }
+
+    protected function prepareBaseUrl()
+    {
+        $filename = basename($this->server->get('SCRIPT_FILENAME'));
+
+        if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
+            $baseUrl = $this->server->get('SCRIPT_NAME');
+        } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
+            $baseUrl = $this->server->get('PHP_SELF');
+        } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
+            $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
+        } else {
+            // Backtrack up the script_filename to find the portion matching
+            // php_self
+            $path    = $this->server->get('PHP_SELF', '');
+            $file    = $this->server->get('SCRIPT_FILENAME', '');
+            $segs    = explode('/', trim($file, '/'));
+            $segs    = array_reverse($segs);
+            $index   = 0;
+            $last    = count($segs);
+            $baseUrl = '';
+            do {
+                $seg     = $segs[$index];
+                $baseUrl = '/'.$seg.$baseUrl;
+                ++$index;
+            } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
+        }
+
+        // Does the baseUrl have anything in common with the request_uri?
+        $requestUri = $this->getRequestUri();
+
+        if ($baseUrl && 0 === strpos($requestUri, $baseUrl)) {
+            // full $baseUrl matches
+            return $baseUrl;
+        }
+
+        if ($baseUrl && 0 === strpos($requestUri, dirname($baseUrl))) {
+            // directory portion of $baseUrl matches
+            return rtrim(dirname($baseUrl), '/');
+        }
+
+        $truncatedRequestUri = $requestUri;
+        if (($pos = strpos($requestUri, '?')) !== false) {
+            $truncatedRequestUri = substr($requestUri, 0, $pos);
+        }
+
+        $basename = basename($baseUrl);
+        if (empty($basename) || !strpos($truncatedRequestUri, $basename)) {
+            // no match whatsoever; set it blank
+            return '';
+        }
+
+        // If using mod_rewrite or ISAPI_Rewrite strip the script filename
+        // out of baseUrl. $pos !== 0 makes sure it is not matching a value
+        // from PATH_INFO or QUERY_STRING
+        if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) {
+            $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
+        }
+
+        return rtrim($baseUrl, '/');
+    }
+
+    /**
+     * Prepares base path.
+     *
+     * @return string base path
+     */
+    protected function prepareBasePath()
+    {
+        $filename = basename($this->server->get('SCRIPT_FILENAME'));
+        $baseUrl = $this->getBaseUrl();
+        if (empty($baseUrl)) {
+            return '';
+        }
+
+        if (basename($baseUrl) === $filename) {
+            $basePath = dirname($baseUrl);
+        } else {
+            $basePath = $baseUrl;
+        }
+
+        if ('\\' === DIRECTORY_SEPARATOR) {
+            $basePath = str_replace('\\', '/', $basePath);
+        }
+
+        return rtrim($basePath, '/');
+    }
+
+    /**
+     * Prepares path info.
+     *
+     * @return string path info
+     */
+    protected function preparePathInfo()
+    {
+        $baseUrl = $this->getBaseUrl();
+
+        if (null === ($requestUri = $this->getRequestUri())) {
+            return '/';
+        }
+
+        $pathInfo = '/';
+
+        // Remove the query string from REQUEST_URI
+        if ($pos = strpos($requestUri, '?')) {
+            $requestUri = substr($requestUri, 0, $pos);
+        }
+
+        if ((null !== $baseUrl) && (false === ($pathInfo = substr(urldecode($requestUri), strlen(urldecode($baseUrl)))))) {
+            // If substr() returns false then PATH_INFO is set to an empty string
+            return '/';
+        } elseif (null === $baseUrl) {
+            return $requestUri;
+        }
+
+        return (string) $pathInfo;
+    }
+
+    /**
+     * Initializes HTTP request formats.
+     */
+    static protected function initializeFormats()
+    {
+        static::$formats = array(
+            'html' => array('text/html', 'application/xhtml+xml'),
+            'txt'  => array('text/plain'),
+            'js'   => array('application/javascript', 'application/x-javascript', 'text/javascript'),
+            'css'  => array('text/css'),
+            'json' => array('application/json', 'application/x-json'),
+            'xml'  => array('text/xml', 'application/xml', 'application/x-xml'),
+            'rdf'  => array('application/rdf+xml'),
+            'atom' => array('application/atom+xml'),
+        );
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/RequestMatcher.php b/includes/Symfony/Component/HttpFoundation/RequestMatcher.php
new file mode 100644
index 000000000000..52ba0784bbca
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/RequestMatcher.php
@@ -0,0 +1,177 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RequestMatcher compares a pre-defined set of checks against a Request instance.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class RequestMatcher implements RequestMatcherInterface
+{
+    private $path;
+    private $host;
+    private $methods;
+    private $ip;
+    private $attributes;
+
+    public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array())
+    {
+        $this->path = $path;
+        $this->host = $host;
+        $this->methods = $methods;
+        $this->ip = $ip;
+        $this->attributes = $attributes;
+    }
+
+    /**
+     * Adds a check for the URL host name.
+     *
+     * @param string $regexp A Regexp
+     */
+    public function matchHost($regexp)
+    {
+        $this->host = $regexp;
+    }
+
+    /**
+     * Adds a check for the URL path info.
+     *
+     * @param string $regexp A Regexp
+     */
+    public function matchPath($regexp)
+    {
+        $this->path = $regexp;
+    }
+
+    /**
+     * Adds a check for the client IP.
+     *
+     * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+     */
+    public function matchIp($ip)
+    {
+        $this->ip = $ip;
+    }
+
+    /**
+     * Adds a check for the HTTP method.
+     *
+     * @param string|array $method An HTTP method or an array of HTTP methods
+     */
+    public function matchMethod($method)
+    {
+        $this->methods = array_map('strtoupper', is_array($method) ? $method : array($method));
+    }
+
+    /**
+     * Adds a check for request attribute.
+     *
+     * @param string $key    The request attribute name
+     * @param string $regexp A Regexp
+     */
+    public function matchAttribute($key, $regexp)
+    {
+        $this->attributes[$key] = $regexp;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function matches(Request $request)
+    {
+        if (null !== $this->methods && !in_array($request->getMethod(), $this->methods)) {
+            return false;
+        }
+
+        foreach ($this->attributes as $key => $pattern) {
+            if (!preg_match('#'.str_replace('#', '\\#', $pattern).'#', $request->attributes->get($key))) {
+                return false;
+            }
+        }
+
+        if (null !== $this->path) {
+            $path = str_replace('#', '\\#', $this->path);
+
+            if (!preg_match('#'.$path.'#', $request->getPathInfo())) {
+                return false;
+            }
+        }
+
+        if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#', $request->getHost())) {
+            return false;
+        }
+
+        if (null !== $this->ip && !$this->checkIp($request->getClientIp(), $this->ip)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    protected function checkIp($requestIp, $ip)
+    {
+        // IPv6 address
+        if (false !== strpos($requestIp, ':')) {
+            return $this->checkIp6($requestIp, $ip);
+        } else {
+            return $this->checkIp4($requestIp, $ip);
+        }
+    }
+
+    protected function checkIp4($requestIp, $ip)
+    {
+        if (false !== strpos($ip, '/')) {
+            list($address, $netmask) = explode('/', $ip);
+
+            if ($netmask < 1 || $netmask > 32) {
+                return false;
+            }
+        } else {
+            $address = $ip;
+            $netmask = 32;
+        }
+
+        return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
+    }
+
+    /**
+     * @author David Soria Parra <dsp at php dot net>
+     * @see https://github.com/dsp/v6tools
+     */
+    protected function checkIp6($requestIp, $ip)
+    {
+        if (!defined('AF_INET6')) {
+            throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
+        }
+
+        list($address, $netmask) = explode('/', $ip);
+
+        $bytes_addr = unpack("n*", inet_pton($address));
+        $bytes_test = unpack("n*", inet_pton($requestIp));
+
+        for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) {
+            $left = $netmask - 16 * ($i-1);
+            $left = ($left <= 16) ?: 16;
+            $mask = ~(0xffff >> $left) & 0xffff;
+            if (($bytes_addr[$i] & $mask) != ($bytes_test[$i] & $mask)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/RequestMatcherInterface.php b/includes/Symfony/Component/HttpFoundation/RequestMatcherInterface.php
new file mode 100644
index 000000000000..506ec7940153
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/RequestMatcherInterface.php
@@ -0,0 +1,33 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RequestMatcherInterface is an interface for strategies to match a Request.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface RequestMatcherInterface
+{
+    /**
+     * Decides whether the rule(s) implemented by the strategy matches the supplied request.
+     *
+     * @param  Request $request The request to check for a match
+     *
+     * @return Boolean true if the request matches, false otherwise
+     *
+     * @api
+     */
+    function matches(Request $request);
+}
diff --git a/includes/Symfony/Component/HttpFoundation/Response.php b/includes/Symfony/Component/HttpFoundation/Response.php
new file mode 100644
index 000000000000..e8a7d5624498
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/Response.php
@@ -0,0 +1,891 @@
+<?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\HttpFoundation;
+
+/**
+ * Response represents an HTTP response.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Response
+{
+    /**
+     * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
+     */
+    public $headers;
+
+    protected $content;
+    protected $version;
+    protected $statusCode;
+    protected $statusText;
+    protected $charset;
+
+    static public $statusTexts = array(
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        307 => 'Temporary Redirect',
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Timeout',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Long',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested Range Not Satisfiable',
+        417 => 'Expectation Failed',
+        418 => 'I\'m a teapot',
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Timeout',
+        505 => 'HTTP Version Not Supported',
+    );
+
+    /**
+     * Constructor.
+     *
+     * @param string  $content The response content
+     * @param integer $status  The response status code
+     * @param array   $headers An array of response headers
+     *
+     * @api
+     */
+    public function __construct($content = '', $status = 200, $headers = array())
+    {
+        $this->headers = new ResponseHeaderBag($headers);
+        $this->setContent($content);
+        $this->setStatusCode($status);
+        $this->setProtocolVersion('1.0');
+        if (!$this->headers->has('Date')) {
+            $this->setDate(new \DateTime(null, new \DateTimeZone('UTC')));
+        }
+    }
+
+    /**
+     * Returns the response content as it will be sent (with the headers).
+     *
+     * @return string The response content
+     */
+    public function __toString()
+    {
+        $this->prepare();
+
+        return
+            sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
+            $this->headers."\r\n".
+            $this->getContent();
+    }
+
+    /**
+     * Clones the current Response instance.
+     */
+    public function __clone()
+    {
+        $this->headers = clone $this->headers;
+    }
+
+    /**
+     * Prepares the Response before it is sent to the client.
+     *
+     * This method tweaks the Response to ensure that it is
+     * compliant with RFC 2616.
+     */
+    public function prepare()
+    {
+        if ($this->isInformational() || in_array($this->statusCode, array(204, 304))) {
+            $this->setContent('');
+        }
+
+        // Fix Content-Type
+        $charset = $this->charset ?: 'UTF-8';
+        if (!$this->headers->has('Content-Type')) {
+            $this->headers->set('Content-Type', 'text/html; charset='.$charset);
+        } elseif ('text/' === substr($this->headers->get('Content-Type'), 0, 5) && false === strpos($this->headers->get('Content-Type'), 'charset')) {
+            // add the charset
+            $this->headers->set('Content-Type', $this->headers->get('Content-Type').'; charset='.$charset);
+        }
+
+        // Fix Content-Length
+        if ($this->headers->has('Transfer-Encoding')) {
+            $this->headers->remove('Content-Length');
+        }
+    }
+
+    /**
+     * Sends HTTP headers.
+     */
+    public function sendHeaders()
+    {
+        // headers have already been sent by the developer
+        if (headers_sent()) {
+            return;
+        }
+
+        $this->prepare();
+
+        // status
+        header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText));
+
+        // headers
+        foreach ($this->headers->all() as $name => $values) {
+            foreach ($values as $value) {
+                header($name.': '.$value, false);
+            }
+        }
+
+        // cookies
+        foreach ($this->headers->getCookies() as $cookie) {
+            setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
+        }
+    }
+
+    /**
+     * Sends content for the current web response.
+     */
+    public function sendContent()
+    {
+        echo $this->content;
+    }
+
+    /**
+     * Sends HTTP headers and content.
+     *
+     * @api
+     */
+    public function send()
+    {
+        $this->sendHeaders();
+        $this->sendContent();
+
+        if (function_exists('fastcgi_finish_request')) {
+            fastcgi_finish_request();
+        }
+    }
+
+    /**
+     * Sets the response content
+     *
+     * Valid types are strings, numbers, and objects that implement a __toString() method.
+     *
+     * @param mixed $content
+     *
+     * @api
+     */
+    public function setContent($content)
+    {
+        if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {
+            throw new \UnexpectedValueException('The Response content must be a string or object implementing __toString(), "'.gettype($content).'" given.');
+        }
+
+        $this->content = (string) $content;
+    }
+
+    /**
+     * Gets the current response content
+     *
+     * @return string Content
+     *
+     * @api
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    /**
+     * Sets the HTTP protocol version (1.0 or 1.1).
+     *
+     * @param string $version The HTTP protocol version
+     *
+     * @api
+     */
+    public function setProtocolVersion($version)
+    {
+        $this->version = $version;
+    }
+
+    /**
+     * Gets the HTTP protocol version.
+     *
+     * @return string The HTTP protocol version
+     *
+     * @api
+     */
+    public function getProtocolVersion()
+    {
+        return $this->version;
+    }
+
+    /**
+     * Sets response status code.
+     *
+     * @param integer $code HTTP status code
+     * @param string  $text HTTP status text
+     *
+     * @throws \InvalidArgumentException When the HTTP status code is not valid
+     *
+     * @api
+     */
+    public function setStatusCode($code, $text = null)
+    {
+        $this->statusCode = (int) $code;
+        if ($this->isInvalid()) {
+            throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
+        }
+
+        $this->statusText = false === $text ? '' : (null === $text ? self::$statusTexts[$this->statusCode] : $text);
+    }
+
+    /**
+     * Retrieves status code for the current web response.
+     *
+     * @return string Status code
+     *
+     * @api
+     */
+    public function getStatusCode()
+    {
+        return $this->statusCode;
+    }
+
+    /**
+     * Sets response charset.
+     *
+     * @param string $charset Character set
+     *
+     * @api
+     */
+    public function setCharset($charset)
+    {
+        $this->charset = $charset;
+    }
+
+    /**
+     * Retrieves the response charset.
+     *
+     * @return string Character set
+     *
+     * @api
+     */
+    public function getCharset()
+    {
+        return $this->charset;
+    }
+
+    /**
+     * Returns true if the response is worth caching under any circumstance.
+     *
+     * Responses marked "private" with an explicit Cache-Control directive are
+     * considered uncacheable.
+     *
+     * Responses with neither a freshness lifetime (Expires, max-age) nor cache
+     * validator (Last-Modified, ETag) are considered uncacheable.
+     *
+     * @return Boolean true if the response is worth caching, false otherwise
+     *
+     * @api
+     */
+    public function isCacheable()
+    {
+        if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
+            return false;
+        }
+
+        if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
+            return false;
+        }
+
+        return $this->isValidateable() || $this->isFresh();
+    }
+
+    /**
+     * Returns true if the response is "fresh".
+     *
+     * Fresh responses may be served from cache without any interaction with the
+     * origin. A response is considered fresh when it includes a Cache-Control/max-age
+     * indicator or Expiration header and the calculated age is less than the freshness lifetime.
+     *
+     * @return Boolean true if the response is fresh, false otherwise
+     *
+     * @api
+     */
+    public function isFresh()
+    {
+        return $this->getTtl() > 0;
+    }
+
+    /**
+     * Returns true if the response includes headers that can be used to validate
+     * the response with the origin server using a conditional GET request.
+     *
+     * @return Boolean true if the response is validateable, false otherwise
+     *
+     * @api
+     */
+    public function isValidateable()
+    {
+        return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
+    }
+
+    /**
+     * Marks the response as "private".
+     *
+     * It makes the response ineligible for serving other clients.
+     *
+     * @api
+     */
+    public function setPrivate()
+    {
+        $this->headers->removeCacheControlDirective('public');
+        $this->headers->addCacheControlDirective('private');
+    }
+
+    /**
+     * Marks the response as "public".
+     *
+     * It makes the response eligible for serving other clients.
+     *
+     * @api
+     */
+    public function setPublic()
+    {
+        $this->headers->addCacheControlDirective('public');
+        $this->headers->removeCacheControlDirective('private');
+    }
+
+    /**
+     * Returns true if the response must be revalidated by caches.
+     *
+     * This method indicates that the response must not be served stale by a
+     * cache in any circumstance without first revalidating with the origin.
+     * When present, the TTL of the response should not be overridden to be
+     * greater than the value provided by the origin.
+     *
+     * @return Boolean true if the response must be revalidated by a cache, false otherwise
+     *
+     * @api
+     */
+    public function mustRevalidate()
+    {
+        return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('must-proxy-revalidate');
+    }
+
+    /**
+     * Returns the Date header as a DateTime instance.
+     *
+     * @return \DateTime A \DateTime instance
+     *
+     * @throws \RuntimeException when the header is not parseable
+     *
+     * @api
+     */
+    public function getDate()
+    {
+        return $this->headers->getDate('Date');
+    }
+
+    /**
+     * Sets the Date header.
+     *
+     * @param \DateTime $date A \DateTime instance
+     *
+     * @api
+     */
+    public function setDate(\DateTime $date)
+    {
+        $date->setTimezone(new \DateTimeZone('UTC'));
+        $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
+    }
+
+    /**
+     * Returns the age of the response.
+     *
+     * @return integer The age of the response in seconds
+     */
+    public function getAge()
+    {
+        if ($age = $this->headers->get('Age')) {
+            return $age;
+        }
+
+        return max(time() - $this->getDate()->format('U'), 0);
+    }
+
+    /**
+     * Marks the response stale by setting the Age header to be equal to the maximum age of the response.
+     *
+     * @api
+     */
+    public function expire()
+    {
+        if ($this->isFresh()) {
+            $this->headers->set('Age', $this->getMaxAge());
+        }
+    }
+
+    /**
+     * Returns the value of the Expires header as a DateTime instance.
+     *
+     * @return \DateTime A DateTime instance
+     *
+     * @api
+     */
+    public function getExpires()
+    {
+        return $this->headers->getDate('Expires');
+    }
+
+    /**
+     * Sets the Expires HTTP header with a \DateTime instance.
+     *
+     * If passed a null value, it removes the header.
+     *
+     * @param \DateTime $date A \DateTime instance
+     *
+     * @api
+     */
+    public function setExpires(\DateTime $date = null)
+    {
+        if (null === $date) {
+            $this->headers->remove('Expires');
+        } else {
+            $date = clone $date;
+            $date->setTimezone(new \DateTimeZone('UTC'));
+            $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
+        }
+    }
+
+    /**
+     * Sets the number of seconds after the time specified in the response's Date
+     * header when the the response should no longer be considered fresh.
+     *
+     * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
+     * back on an expires header. It returns null when no maximum age can be established.
+     *
+     * @return integer|null Number of seconds
+     *
+     * @api
+     */
+    public function getMaxAge()
+    {
+        if ($age = $this->headers->getCacheControlDirective('s-maxage')) {
+            return $age;
+        }
+
+        if ($age = $this->headers->getCacheControlDirective('max-age')) {
+            return $age;
+        }
+
+        if (null !== $this->getExpires()) {
+            return $this->getExpires()->format('U') - $this->getDate()->format('U');
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the number of seconds after which the response should no longer be considered fresh.
+     *
+     * This methods sets the Cache-Control max-age directive.
+     *
+     * @param integer $value A number of seconds
+     *
+     * @api
+     */
+    public function setMaxAge($value)
+    {
+        $this->headers->addCacheControlDirective('max-age', $value);
+    }
+
+    /**
+     * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
+     *
+     * This methods sets the Cache-Control s-maxage directive.
+     *
+     * @param integer $value A number of seconds
+     *
+     * @api
+     */
+    public function setSharedMaxAge($value)
+    {
+        $this->setPublic();
+        $this->headers->addCacheControlDirective('s-maxage', $value);
+    }
+
+    /**
+     * Returns the response's time-to-live in seconds.
+     *
+     * It returns null when no freshness information is present in the response.
+     *
+     * When the responses TTL is <= 0, the response may not be served from cache without first
+     * revalidating with the origin.
+     *
+     * @return integer The TTL in seconds
+     *
+     * @api
+     */
+    public function getTtl()
+    {
+        if ($maxAge = $this->getMaxAge()) {
+            return $maxAge - $this->getAge();
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the response's time-to-live for shared caches.
+     *
+     * This method adjusts the Cache-Control/s-maxage directive.
+     *
+     * @param integer $seconds The number of seconds
+     *
+     * @api
+     */
+    public function setTtl($seconds)
+    {
+        $this->setSharedMaxAge($this->getAge() + $seconds);
+    }
+
+    /**
+     * Sets the response's time-to-live for private/client caches.
+     *
+     * This method adjusts the Cache-Control/max-age directive.
+     *
+     * @param integer $seconds The number of seconds
+     *
+     * @api
+     */
+    public function setClientTtl($seconds)
+    {
+        $this->setMaxAge($this->getAge() + $seconds);
+    }
+
+    /**
+     * Returns the Last-Modified HTTP header as a DateTime instance.
+     *
+     * @return \DateTime A DateTime instance
+     *
+     * @api
+     */
+    public function getLastModified()
+    {
+        return $this->headers->getDate('Last-Modified');
+    }
+
+    /**
+     * Sets the Last-Modified HTTP header with a \DateTime instance.
+     *
+     * If passed a null value, it removes the header.
+     *
+     * @param \DateTime $date A \DateTime instance
+     *
+     * @api
+     */
+    public function setLastModified(\DateTime $date = null)
+    {
+        if (null === $date) {
+            $this->headers->remove('Last-Modified');
+        } else {
+            $date = clone $date;
+            $date->setTimezone(new \DateTimeZone('UTC'));
+            $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
+        }
+    }
+
+    /**
+     * Returns the literal value of ETag HTTP header.
+     *
+     * @return string The ETag HTTP header
+     *
+     * @api
+     */
+    public function getEtag()
+    {
+        return $this->headers->get('ETag');
+    }
+
+    /**
+     * Sets the ETag value.
+     *
+     * @param string  $etag The ETag unique identifier
+     * @param Boolean $weak Whether you want a weak ETag or not
+     *
+     * @api
+     */
+    public function setEtag($etag = null, $weak = false)
+    {
+        if (null === $etag) {
+            $this->headers->remove('Etag');
+        } else {
+            if (0 !== strpos($etag, '"')) {
+                $etag = '"'.$etag.'"';
+            }
+
+            $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
+        }
+    }
+
+    /**
+     * Sets Response cache headers (validation and/or expiration).
+     *
+     * Available options are: etag, last_modified, max_age, s_maxage, private, and public.
+     *
+     * @param array $options An array of cache options
+     *
+     * @api
+     */
+    public function setCache(array $options)
+    {
+        if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) {
+            throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_keys($diff))));
+        }
+
+        if (isset($options['etag'])) {
+            $this->setEtag($options['etag']);
+        }
+
+        if (isset($options['last_modified'])) {
+            $this->setLastModified($options['last_modified']);
+        }
+
+        if (isset($options['max_age'])) {
+            $this->setMaxAge($options['max_age']);
+        }
+
+        if (isset($options['s_maxage'])) {
+            $this->setSharedMaxAge($options['s_maxage']);
+        }
+
+        if (isset($options['public'])) {
+            if ($options['public']) {
+                $this->setPublic();
+            } else {
+                $this->setPrivate();
+            }
+        }
+
+        if (isset($options['private'])) {
+            if ($options['private']) {
+                $this->setPrivate();
+            } else {
+                $this->setPublic();
+            }
+        }
+    }
+
+    /**
+     * Modifies the response so that it conforms to the rules defined for a 304 status code.
+     *
+     * This sets the status, removes the body, and discards any headers
+     * that MUST NOT be included in 304 responses.
+     *
+     * @see http://tools.ietf.org/html/rfc2616#section-10.3.5
+     *
+     * @api
+     */
+    public function setNotModified()
+    {
+        $this->setStatusCode(304);
+        $this->setContent(null);
+
+        // remove headers that MUST NOT be included with 304 Not Modified responses
+        foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
+            $this->headers->remove($header);
+        }
+    }
+
+    /**
+     * Returns true if the response includes a Vary header.
+     *
+     * @return true if the response includes a Vary header, false otherwise
+     *
+     * @api
+     */
+    public function hasVary()
+    {
+        return (Boolean) $this->headers->get('Vary');
+    }
+
+    /**
+     * Returns an array of header names given in the Vary header.
+     *
+     * @return array An array of Vary names
+     *
+     * @api
+     */
+    public function getVary()
+    {
+        if (!$vary = $this->headers->get('Vary')) {
+            return array();
+        }
+
+        return is_array($vary) ? $vary : preg_split('/[\s,]+/', $vary);
+    }
+
+    /**
+     * Sets the Vary header.
+     *
+     * @param string|array $headers
+     * @param Boolean      $replace Whether to replace the actual value of not (true by default)
+     *
+     * @api
+     */
+    public function setVary($headers, $replace = true)
+    {
+        $this->headers->set('Vary', $headers, $replace);
+    }
+
+    /**
+     * Determines if the Response validators (ETag, Last-Modified) matches
+     * a conditional value specified in the Request.
+     *
+     * If the Response is not modified, it sets the status code to 304 and
+     * remove the actual content by calling the setNotModified() method.
+     *
+     * @param Request $request A Request instance
+     *
+     * @return Boolean true if the Response validators matches the Request, false otherwise
+     *
+     * @api
+     */
+    public function isNotModified(Request $request)
+    {
+        $lastModified = $request->headers->get('If-Modified-Since');
+        $notModified = false;
+        if ($etags = $request->getEtags()) {
+            $notModified = (in_array($this->getEtag(), $etags) || in_array('*', $etags)) && (!$lastModified || $this->headers->get('Last-Modified') == $lastModified);
+        } elseif ($lastModified) {
+            $notModified = $lastModified == $this->headers->get('Last-Modified');
+        }
+
+        if ($notModified) {
+            $this->setNotModified();
+        }
+
+        return $notModified;
+    }
+
+    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+    /**
+     * @api
+     */
+    public function isInvalid()
+    {
+        return $this->statusCode < 100 || $this->statusCode >= 600;
+    }
+
+    /**
+     * @api
+     */
+    public function isInformational()
+    {
+        return $this->statusCode >= 100 && $this->statusCode < 200;
+    }
+
+    /**
+     * @api
+     */
+    public function isSuccessful()
+    {
+        return $this->statusCode >= 200 && $this->statusCode < 300;
+    }
+
+    /**
+     * @api
+     */
+    public function isRedirection()
+    {
+        return $this->statusCode >= 300 && $this->statusCode < 400;
+    }
+
+    /**
+     * @api
+     */
+    public function isClientError()
+    {
+        return $this->statusCode >= 400 && $this->statusCode < 500;
+    }
+
+    /**
+     * @api
+     */
+    public function isServerError()
+    {
+        return $this->statusCode >= 500 && $this->statusCode < 600;
+    }
+
+    /**
+     * @api
+     */
+    public function isOk()
+    {
+        return 200 === $this->statusCode;
+    }
+
+    /**
+     * @api
+     */
+    public function isForbidden()
+    {
+        return 403 === $this->statusCode;
+    }
+
+    /**
+     * @api
+     */
+    public function isNotFound()
+    {
+        return 404 === $this->statusCode;
+    }
+
+    /**
+     * @api
+     */
+    public function isRedirect($location = null)
+    {
+        return in_array($this->statusCode, array(201, 301, 302, 303, 307)) && (null === $location ?: $location == $this->headers->get('Location'));
+    }
+
+    /**
+     * @api
+     */
+    public function isEmpty()
+    {
+        return in_array($this->statusCode, array(201, 204, 304));
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/includes/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
new file mode 100644
index 000000000000..f243dcc9f6bb
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
@@ -0,0 +1,238 @@
+<?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\HttpFoundation;
+
+/**
+ * ResponseHeaderBag is a container for Response HTTP headers.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class ResponseHeaderBag extends HeaderBag
+{
+    const COOKIES_FLAT  = 'flat';
+    const COOKIES_ARRAY = 'array';
+
+    protected $computedCacheControl = array();
+    protected $cookies              = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $headers An array of HTTP headers
+     *
+     * @api
+     */
+    public function __construct(array $headers = array())
+    {
+        parent::__construct($headers);
+
+        if (!isset($this->headers['cache-control'])) {
+            $this->set('cache-control', '');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __toString()
+    {
+        $cookies = '';
+        foreach ($this->getCookies() as $cookie) {
+            $cookies .= 'Set-Cookie: '.$cookie."\r\n";
+        }
+
+        return parent::__toString().$cookies;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function replace(array $headers = array())
+    {
+        parent::replace($headers);
+
+        if (!isset($this->headers['cache-control'])) {
+            $this->set('cache-control', '');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function set($key, $values, $replace = true)
+    {
+        parent::set($key, $values, $replace);
+
+        // ensure the cache-control header has sensible defaults
+        if (in_array(strtr(strtolower($key), '_', '-'), array('cache-control', 'etag', 'last-modified', 'expires'))) {
+            $computed = $this->computeCacheControlValue();
+            $this->headers['cache-control'] = array($computed);
+            $this->computedCacheControl = $this->parseCacheControl($computed);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @api
+     */
+    public function remove($key)
+    {
+        parent::remove($key);
+
+        if ('cache-control' === strtr(strtolower($key), '_', '-')) {
+            $this->computedCacheControl = array();
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hasCacheControlDirective($key)
+    {
+        return array_key_exists($key, $this->computedCacheControl);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCacheControlDirective($key)
+    {
+        return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
+    }
+
+    /**
+     * Sets a cookie.
+     *
+     * @param Cookie $cookie
+     * @return void
+     *
+     * @api
+     */
+    public function setCookie(Cookie $cookie)
+    {
+        $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
+    }
+
+    /**
+     * Removes a cookie from the array, but does not unset it in the browser
+     *
+     * @param string $name
+     * @param string $path
+     * @param string $domain
+     * @return void
+     *
+     * @api
+     */
+    public function removeCookie($name, $path = '/', $domain = null)
+    {
+        if (null === $path) {
+            $path = '/';
+        }
+
+        unset($this->cookies[$domain][$path][$name]);
+
+        if (empty($this->cookies[$domain][$path])) {
+            unset($this->cookies[$domain][$path]);
+
+            if (empty($this->cookies[$domain])) {
+                unset($this->cookies[$domain]);
+            }
+        }
+    }
+
+    /**
+     * Returns an array with all cookies
+     *
+     * @param string $format
+     *
+     * @throws \InvalidArgumentException When the $format is invalid
+     *
+     * @return array
+     *
+     * @api
+     */
+    public function getCookies($format = self::COOKIES_FLAT)
+    {
+        if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
+            throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
+        }
+
+        if (self::COOKIES_ARRAY === $format) {
+            return $this->cookies;
+        }
+
+        $flattenedCookies = array();
+        foreach ($this->cookies as $path) {
+            foreach ($path as $cookies) {
+                foreach ($cookies as $cookie) {
+                    $flattenedCookies[] = $cookie;
+                }
+            }
+        }
+
+        return $flattenedCookies;
+    }
+
+    /**
+     * Clears a cookie in the browser
+     *
+     * @param string $name
+     * @param string $path
+     * @param string $domain
+     * @return void
+     *
+     * @api
+     */
+    public function clearCookie($name, $path = '/', $domain = null)
+    {
+        $this->setCookie(new Cookie($name, null, 1, $path, $domain));
+    }
+
+    /**
+     * Returns the calculated value of the cache-control header.
+     *
+     * This considers several other headers and calculates or modifies the
+     * cache-control header to a sensible, conservative value.
+     *
+     * @return string
+     */
+    protected function computeCacheControlValue()
+    {
+        if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
+            return 'no-cache';
+        }
+
+        if (!$this->cacheControl) {
+            // conservative by default
+            return 'private, must-revalidate';
+        }
+
+        $header = $this->getCacheControlHeader();
+        if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
+            return $header;
+        }
+
+        // public if s-maxage is defined, private otherwise
+        if (!isset($this->cacheControl['s-maxage'])) {
+            return $header.', private';
+        }
+
+        return $header;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/ServerBag.php b/includes/Symfony/Component/HttpFoundation/ServerBag.php
new file mode 100644
index 000000000000..02db3b181907
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/ServerBag.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\HttpFoundation;
+
+/**
+ * ServerBag is a container for HTTP headers from the $_SERVER variable.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+class ServerBag extends ParameterBag
+{
+    public function getHeaders()
+    {
+        $headers = array();
+        foreach ($this->parameters as $key => $value) {
+            if ('HTTP_' === substr($key, 0, 5)) {
+                $headers[substr($key, 5)] = $value;
+            }
+            // CONTENT_* are not prefixed with HTTP_
+            elseif (in_array($key, array('CONTENT_LENGTH', 'CONTENT_MD5', 'CONTENT_TYPE'))) {
+                $headers[$key] = $this->parameters[$key];
+            }
+        }
+
+        // PHP_AUTH_USER/PHP_AUTH_PW
+        if (isset($this->parameters['PHP_AUTH_USER'])) {
+            $pass = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
+            $headers['AUTHORIZATION'] = 'Basic '.base64_encode($this->parameters['PHP_AUTH_USER'].':'.$pass);
+        }
+
+        return $headers;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/Session.php b/includes/Symfony/Component/HttpFoundation/Session.php
new file mode 100644
index 000000000000..a835ef7da5e7
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/Session.php
@@ -0,0 +1,405 @@
+<?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\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\SessionStorage\SessionStorageInterface;
+
+/**
+ * Session.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class Session implements \Serializable
+{
+    protected $storage;
+    protected $started;
+    protected $attributes;
+    protected $flashes;
+    protected $oldFlashes;
+    protected $locale;
+    protected $defaultLocale;
+    protected $closed;
+
+    /**
+     * Constructor.
+     *
+     * @param SessionStorageInterface $storage       A SessionStorageInterface instance
+     * @param string                  $defaultLocale The default locale
+     */
+    public function __construct(SessionStorageInterface $storage, $defaultLocale = 'en')
+    {
+        $this->storage = $storage;
+        $this->defaultLocale = $defaultLocale;
+        $this->locale = $defaultLocale;
+        $this->flashes = array();
+        $this->oldFlashes = array();
+        $this->attributes = array();
+        $this->setPhpDefaultLocale($this->defaultLocale);
+        $this->started = false;
+        $this->closed = false;
+    }
+
+    /**
+     * Starts the session storage.
+     *
+     * @api
+     */
+    public function start()
+    {
+        if (true === $this->started) {
+            return;
+        }
+
+        $this->storage->start();
+
+        $attributes = $this->storage->read('_symfony2');
+
+        if (isset($attributes['attributes'])) {
+            $this->attributes = $attributes['attributes'];
+            $this->flashes = $attributes['flashes'];
+            $this->locale = $attributes['locale'];
+            $this->setPhpDefaultLocale($this->locale);
+
+            // flag current flash messages to be removed at shutdown
+            $this->oldFlashes = $this->flashes;
+        }
+
+        $this->started = true;
+    }
+
+    /**
+     * Checks if an attribute is defined.
+     *
+     * @param string $name The attribute name
+     *
+     * @return Boolean true if the attribute is defined, false otherwise
+     *
+     * @api
+     */
+    public function has($name)
+    {
+        return array_key_exists($name, $this->attributes);
+    }
+
+    /**
+     * Returns an attribute.
+     *
+     * @param string $name    The attribute name
+     * @param mixed  $default The default value
+     *
+     * @return mixed
+     *
+     * @api
+     */
+    public function get($name, $default = null)
+    {
+        return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+    }
+
+    /**
+     * Sets an attribute.
+     *
+     * @param string $name
+     * @param mixed  $value
+     *
+     * @api
+     */
+    public function set($name, $value)
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        $this->attributes[$name] = $value;
+    }
+
+    /**
+     * Returns attributes.
+     *
+     * @return array Attributes
+     *
+     * @api
+     */
+    public function all()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Sets attributes.
+     *
+     * @param array $attributes Attributes
+     *
+     * @api
+     */
+    public function replace(array $attributes)
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        $this->attributes = $attributes;
+    }
+
+    /**
+     * Removes an attribute.
+     *
+     * @param string $name
+     *
+     * @api
+     */
+    public function remove($name)
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        if (array_key_exists($name, $this->attributes)) {
+            unset($this->attributes[$name]);
+        }
+    }
+
+    /**
+     * Clears all attributes.
+     *
+     * @api
+     */
+    public function clear()
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        $this->attributes = array();
+        $this->flashes = array();
+        $this->setPhpDefaultLocale($this->locale = $this->defaultLocale);
+    }
+
+    /**
+     * Invalidates the current session.
+     *
+     * @api
+     */
+    public function invalidate()
+    {
+        $this->clear();
+        $this->storage->regenerate(true);
+    }
+
+    /**
+     * Migrates the current session to a new session id while maintaining all
+     * session attributes.
+     *
+     * @api
+     */
+    public function migrate()
+    {
+        $this->storage->regenerate();
+    }
+
+    /**
+     * Returns the session ID
+     *
+     * @return mixed  The session ID
+     *
+     * @api
+     */
+    public function getId()
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        return $this->storage->getId();
+    }
+
+    /**
+     * Returns the locale
+     *
+     * @return string
+     */
+    public function getLocale()
+    {
+        return $this->locale;
+    }
+
+    /**
+     * Sets the locale.
+     *
+     * @param string $locale
+     */
+    public function setLocale($locale)
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        $this->setPhpDefaultLocale($this->locale = $locale);
+    }
+
+    /**
+     * Gets the flash messages.
+     *
+     * @return array
+     */
+    public function getFlashes()
+    {
+        return $this->flashes;
+    }
+
+    /**
+     * Sets the flash messages.
+     *
+     * @param array $values
+     */
+    public function setFlashes($values)
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        $this->flashes = $values;
+        $this->oldFlashes = array();
+    }
+
+    /**
+     * Gets a flash message.
+     *
+     * @param string      $name
+     * @param string|null $default
+     *
+     * @return string
+     */
+    public function getFlash($name, $default = null)
+    {
+        return array_key_exists($name, $this->flashes) ? $this->flashes[$name] : $default;
+    }
+
+    /**
+     * Sets a flash message.
+     *
+     * @param string $name
+     * @param string $value
+     */
+    public function setFlash($name, $value)
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        $this->flashes[$name] = $value;
+        unset($this->oldFlashes[$name]);
+    }
+
+    /**
+     * Checks whether a flash message exists.
+     *
+     * @param string $name
+     *
+     * @return Boolean
+     */
+    public function hasFlash($name)
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        return array_key_exists($name, $this->flashes);
+    }
+
+    /**
+     * Removes a flash message.
+     *
+     * @param string $name
+     */
+    public function removeFlash($name)
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        unset($this->flashes[$name]);
+    }
+
+    /**
+     * Removes the flash messages.
+     */
+    public function clearFlashes()
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        $this->flashes = array();
+        $this->oldFlashes = array();
+    }
+
+    public function save()
+    {
+        if (false === $this->started) {
+            $this->start();
+        }
+
+        $this->flashes = array_diff_key($this->flashes, $this->oldFlashes);
+
+        $this->storage->write('_symfony2', array(
+            'attributes' => $this->attributes,
+            'flashes'    => $this->flashes,
+            'locale'     => $this->locale,
+        ));
+    }
+
+    /**
+     * This method should be called when you don't want the session to be saved
+     * when the Session object is garbaged collected (useful for instance when
+     * you want to simulate the interaction of several users/sessions in a single
+     * PHP process).
+     */
+    public function close()
+    {
+        $this->closed = true;
+    }
+
+    public function __destruct()
+    {
+        if (true === $this->started && !$this->closed) {
+            $this->save();
+        }
+    }
+
+    public function serialize()
+    {
+        return serialize(array($this->storage, $this->defaultLocale));
+    }
+
+    public function unserialize($serialized)
+    {
+        list($this->storage, $this->defaultLocale) = unserialize($serialized);
+        $this->attributes = array();
+        $this->started = false;
+    }
+
+    private function setPhpDefaultLocale($locale)
+    {
+        // if either the class Locale doesn't exist, or an exception is thrown when
+        // setting the default locale, the intl module is not installed, and
+        // the call can be ignored:
+        try {
+            if (class_exists('Locale', false)) {
+                \Locale::setDefault($locale);
+            }
+        } catch (\Exception $e) {
+        }
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php b/includes/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php
new file mode 100644
index 000000000000..62aac40982a3
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.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\HttpFoundation\SessionStorage;
+
+/**
+ * ArraySessionStorage mocks the session for unit tests.
+ *
+ * When doing functional testing, you should use FilesystemSessionStorage instead.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ */
+
+class ArraySessionStorage implements SessionStorageInterface
+{
+    private $data = array();
+
+    public function read($key, $default = null)
+    {
+        return array_key_exists($key, $this->data) ? $this->data[$key] : $default;
+    }
+
+    public function regenerate($destroy = false)
+    {
+        if ($destroy) {
+            $this->data = array();
+        }
+
+        return true;
+    }
+
+    public function remove($key)
+    {
+        unset($this->data[$key]);
+    }
+
+    public function start()
+    {
+    }
+
+    public function getId()
+    {
+    }
+
+    public function write($key, $data)
+    {
+        $this->data[$key] = $data;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php b/includes/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php
new file mode 100644
index 000000000000..87abd01bcde2
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php
@@ -0,0 +1,174 @@
+<?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\HttpFoundation\SessionStorage;
+
+/**
+ * FilesystemSessionStorage simulates sessions for functional tests.
+ *
+ * This storage does not start the session (session_start())
+ * as it is not "available" when running tests on the command line.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class FilesystemSessionStorage extends NativeSessionStorage
+{
+    private $path;
+    private $data;
+    private $started;
+
+    /**
+     * Constructor.
+     */
+    public function __construct($path, array $options = array())
+    {
+        $this->path = $path;
+        $this->started = false;
+
+        parent::__construct($options);
+    }
+
+    /**
+     * Starts the session.
+     *
+     * @api
+     */
+    public function start()
+    {
+        if ($this->started) {
+            return;
+        }
+
+        session_set_cookie_params(
+            $this->options['lifetime'],
+            $this->options['path'],
+            $this->options['domain'],
+            $this->options['secure'],
+            $this->options['httponly']
+        );
+
+        if (!ini_get('session.use_cookies') && isset($this->options['id']) && $this->options['id'] && $this->options['id'] != session_id()) {
+            session_id($this->options['id']);
+        }
+
+        if (!session_id()) {
+            session_id(hash('md5', uniqid(mt_rand(), true)));
+        }
+
+        $file = $this->path.'/'.session_id().'.session';
+
+        $this->data = file_exists($file) ? unserialize(file_get_contents($file)) : array();
+        $this->started = true;
+    }
+
+    /**
+     * Returns the session ID
+     *
+     * @return mixed  The session ID
+     *
+     * @throws \RuntimeException If the session was not started yet
+     *
+     * @api
+     */
+    public function getId()
+    {
+        if (!$this->started) {
+            throw new \RuntimeException('The session must be started before reading its ID');
+        }
+
+        return session_id();
+    }
+
+    /**
+     * Reads data from this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param  string $key  A unique key identifying your data
+     *
+     * @return mixed Data associated with the key
+     *
+     * @throws \RuntimeException If an error occurs while reading data from this storage
+     *
+     * @api
+     */
+    public function read($key, $default = null)
+    {
+        return array_key_exists($key, $this->data) ? $this->data[$key] : $default;
+    }
+
+    /**
+     * Removes data from this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param  string $key  A unique key identifying your data
+     *
+     * @return mixed Data associated with the key
+     *
+     * @throws \RuntimeException If an error occurs while removing data from this storage
+     *
+     * @api
+     */
+    public function remove($key)
+    {
+        $retval = $this->data[$key];
+
+        unset($this->data[$key]);
+
+        return $retval;
+    }
+
+    /**
+     * Writes data to this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param  string $key   A unique key identifying your data
+     * @param  mixed  $data  Data associated with your key
+     *
+     * @throws \RuntimeException If an error occurs while writing to this storage
+     *
+     * @api
+     */
+    public function write($key, $data)
+    {
+        $this->data[$key] = $data;
+
+        if (!is_dir($this->path)) {
+            mkdir($this->path, 0777, true);
+        }
+
+        file_put_contents($this->path.'/'.session_id().'.session', serialize($this->data));
+    }
+
+    /**
+     * Regenerates id that represents this storage.
+     *
+     * @param  Boolean $destroy Destroy session when regenerating?
+     *
+     * @return Boolean True if session regenerated, false if error
+     *
+     * @throws \RuntimeException If an error occurs while regenerating this storage
+     *
+     * @api
+     */
+    public function regenerate($destroy = false)
+    {
+        if ($destroy) {
+            $this->data = array();
+        }
+
+        return true;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php b/includes/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php
new file mode 100644
index 000000000000..b759f7411a0a
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php
@@ -0,0 +1,180 @@
+<?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\HttpFoundation\SessionStorage;
+
+/**
+ * NativeSessionStorage.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+class NativeSessionStorage implements SessionStorageInterface
+{
+    static protected $sessionIdRegenerated = false;
+    static protected $sessionStarted       = false;
+
+    protected $options;
+
+    /**
+     * Available options:
+     *
+     *  * name:     The cookie name (null [omitted] by default)
+     *  * id:       The session id (null [omitted] by default)
+     *  * lifetime: Cookie lifetime
+     *  * path:     Cookie path
+     *  * domain:   Cookie domain
+     *  * secure:   Cookie secure
+     *  * httponly: Cookie http only
+     *
+     * The default values for most options are those returned by the session_get_cookie_params() function
+     *
+     * @param array $options  An associative array of session options
+     */
+    public function __construct(array $options = array())
+    {
+        $cookieDefaults = session_get_cookie_params();
+
+        $this->options = array_merge(array(
+            'lifetime' => $cookieDefaults['lifetime'],
+            'path'     => $cookieDefaults['path'],
+            'domain'   => $cookieDefaults['domain'],
+            'secure'   => $cookieDefaults['secure'],
+            'httponly' => isset($cookieDefaults['httponly']) ? $cookieDefaults['httponly'] : false,
+        ), $options);
+
+        // Skip setting new session name if user don't want it
+        if (isset($this->options['name'])) {
+            session_name($this->options['name']);
+        }
+    }
+
+    /**
+     * Starts the session.
+     *
+     * @api
+     */
+    public function start()
+    {
+        if (self::$sessionStarted) {
+            return;
+        }
+
+        session_set_cookie_params(
+            $this->options['lifetime'],
+            $this->options['path'],
+            $this->options['domain'],
+            $this->options['secure'],
+            $this->options['httponly']
+        );
+
+        // disable native cache limiter as this is managed by HeaderBag directly
+        session_cache_limiter(false);
+
+        if (!ini_get('session.use_cookies') && isset($this->options['id']) && $this->options['id'] && $this->options['id'] != session_id()) {
+            session_id($this->options['id']);
+        }
+
+        session_start();
+
+        self::$sessionStarted = true;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @api
+     */
+    public function getId()
+    {
+        if (!self::$sessionStarted) {
+            throw new \RuntimeException('The session must be started before reading its ID');
+        }
+
+        return session_id();
+    }
+
+    /**
+     * Reads data from this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param string $key     A unique key identifying your data
+     * @param string $default Default value
+     *
+     * @return mixed Data associated with the key
+     *
+     * @api
+     */
+    public function read($key, $default = null)
+    {
+        return array_key_exists($key, $_SESSION) ? $_SESSION[$key] : $default;
+    }
+
+    /**
+     * Removes data from this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param  string $key  A unique key identifying your data
+     *
+     * @return mixed Data associated with the key
+     *
+     * @api
+     */
+    public function remove($key)
+    {
+        $retval = null;
+
+        if (isset($_SESSION[$key])) {
+            $retval = $_SESSION[$key];
+            unset($_SESSION[$key]);
+        }
+
+        return $retval;
+    }
+
+    /**
+     * Writes data to this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param string $key   A unique key identifying your data
+     * @param mixed  $data  Data associated with your key
+     *
+     * @api
+     */
+    public function write($key, $data)
+    {
+        $_SESSION[$key] = $data;
+    }
+
+    /**
+     * Regenerates id that represents this storage.
+     *
+     * @param  Boolean $destroy Destroy session when regenerating?
+     *
+     * @return Boolean True if session regenerated, false if error
+     *
+     * @api
+     */
+    public function regenerate($destroy = false)
+    {
+        if (self::$sessionIdRegenerated) {
+            return;
+        }
+
+        session_regenerate_id($destroy);
+
+        self::$sessionIdRegenerated = true;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php b/includes/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php
new file mode 100644
index 000000000000..78f90b8ec956
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php
@@ -0,0 +1,263 @@
+<?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\HttpFoundation\SessionStorage;
+
+/**
+ * PdoSessionStorage.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Michael Williams <michael.williams@funsational.com>
+ */
+class PdoSessionStorage extends NativeSessionStorage
+{
+    private $db;
+    private $dbOptions;
+
+    /**
+     * Constructor.
+     *
+     * @param \PDO  $db        A PDO instance
+     * @param array $options   An associative array of session options
+     * @param array $dbOptions An associative array of DB options
+     *
+     * @throws \InvalidArgumentException When "db_table" option is not provided
+     *
+     * @see NativeSessionStorage::__construct()
+     */
+    public function __construct(\PDO $db, array $options = array(), array $dbOptions = array())
+    {
+        if (!array_key_exists('db_table', $dbOptions)) {
+            throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.');
+        }
+
+        $this->db = $db;
+        $this->dbOptions = array_merge(array(
+            'db_id_col'   => 'sess_id',
+            'db_data_col' => 'sess_data',
+            'db_time_col' => 'sess_time',
+        ), $dbOptions);
+
+        parent::__construct($options);
+    }
+
+    /**
+     * Starts the session.
+     */
+    public function start()
+    {
+        if (self::$sessionStarted) {
+            return;
+        }
+
+        // use this object as the session handler
+        session_set_save_handler(
+            array($this, 'sessionOpen'),
+            array($this, 'sessionClose'),
+            array($this, 'sessionRead'),
+            array($this, 'sessionWrite'),
+            array($this, 'sessionDestroy'),
+            array($this, 'sessionGC')
+        );
+
+        parent::start();
+    }
+
+    /**
+     * Opens a session.
+     *
+     * @param  string $path  (ignored)
+     * @param  string $name  (ignored)
+     *
+     * @return Boolean true, if the session was opened, otherwise an exception is thrown
+     */
+    public function sessionOpen($path = null, $name = null)
+    {
+        return true;
+    }
+
+    /**
+     * Closes a session.
+     *
+     * @return Boolean true, if the session was closed, otherwise false
+     */
+    public function sessionClose()
+    {
+        // do nothing
+        return true;
+    }
+
+    /**
+     * Destroys a session.
+     *
+     * @param  string $id  A session ID
+     *
+     * @return Boolean   true, if the session was destroyed, otherwise an exception is thrown
+     *
+     * @throws \RuntimeException If the session cannot be destroyed
+     */
+    public function sessionDestroy($id)
+    {
+        // get table/column
+        $dbTable  = $this->dbOptions['db_table'];
+        $dbIdCol = $this->dbOptions['db_id_col'];
+
+        // delete the record associated with this id
+        $sql = "DELETE FROM $dbTable WHERE $dbIdCol = :id";
+
+        try {
+            $stmt = $this->db->prepare($sql);
+            $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
+            $stmt->execute();
+        } catch (\PDOException $e) {
+            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+        }
+
+        return true;
+    }
+
+    /**
+     * Cleans up old sessions.
+     *
+     * @param  int $lifetime  The lifetime of a session
+     *
+     * @return Boolean true, if old sessions have been cleaned, otherwise an exception is thrown
+     *
+     * @throws \RuntimeException If any old sessions cannot be cleaned
+     */
+    public function sessionGC($lifetime)
+    {
+        // get table/column
+        $dbTable    = $this->dbOptions['db_table'];
+        $dbTimeCol = $this->dbOptions['db_time_col'];
+
+        // delete the record associated with this id
+        $sql = "DELETE FROM $dbTable WHERE $dbTimeCol < (:time - $lifetime)";
+
+        try {
+            $this->db->query($sql);
+            $stmt = $this->db->prepare($sql);
+            $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+            $stmt->execute();
+        } catch (\PDOException $e) {
+            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+        }
+
+        return true;
+    }
+
+    /**
+     * Reads a session.
+     *
+     * @param  string $id  A session ID
+     *
+     * @return string      The session data if the session was read or created, otherwise an exception is thrown
+     *
+     * @throws \RuntimeException If the session cannot be read
+     */
+    public function sessionRead($id)
+    {
+        // get table/columns
+        $dbTable    = $this->dbOptions['db_table'];
+        $dbDataCol = $this->dbOptions['db_data_col'];
+        $dbIdCol   = $this->dbOptions['db_id_col'];
+
+        try {
+            $sql = "SELECT $dbDataCol FROM $dbTable WHERE $dbIdCol = :id";
+
+            $stmt = $this->db->prepare($sql);
+            $stmt->bindParam(':id', $id, \PDO::PARAM_STR, 255);
+
+            $stmt->execute();
+            // it is recommended to use fetchAll so that PDO can close the DB cursor
+            // we anyway expect either no rows, or one row with one column. fetchColumn, seems to be buggy #4777
+            $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
+
+            if (count($sessionRows) == 1) {
+                return $sessionRows[0][0];
+            }
+
+            // session does not exist, create it
+            $this->createNewSession($id);
+
+            return '';
+        } catch (\PDOException $e) {
+            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+        }
+    }
+
+    /**
+     * Writes session data.
+     *
+     * @param  string $id    A session ID
+     * @param  string $data  A serialized chunk of session data
+     *
+     * @return Boolean true, if the session was written, otherwise an exception is thrown
+     *
+     * @throws \RuntimeException If the session data cannot be written
+     */
+    public function sessionWrite($id, $data)
+    {
+        // get table/column
+        $dbTable   = $this->dbOptions['db_table'];
+        $dbDataCol = $this->dbOptions['db_data_col'];
+        $dbIdCol   = $this->dbOptions['db_id_col'];
+        $dbTimeCol = $this->dbOptions['db_time_col'];
+
+        $sql = ('mysql' === $this->db->getAttribute(\PDO::ATTR_DRIVER_NAME))
+            ? "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time) "
+              ."ON DUPLICATE KEY UPDATE $dbDataCol = VALUES($dbDataCol), $dbTimeCol = CASE WHEN $dbTimeCol = :time THEN (VALUES($dbTimeCol) + 1) ELSE VALUES($dbTimeCol) END"
+            : "UPDATE $dbTable SET $dbDataCol = :data, $dbTimeCol = :time WHERE $dbIdCol = :id";
+
+        try {
+            $stmt = $this->db->prepare($sql);
+            $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
+            $stmt->bindParam(':data', $data, \PDO::PARAM_STR);
+            $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+            $stmt->execute();
+
+            if (!$stmt->rowCount()) {
+                // No session exists in the database to update. This happens when we have called
+                // session_regenerate_id()
+                $this->createNewSession($id, $data);
+            }
+        } catch (\PDOException $e) {
+            throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
+        }
+
+        return true;
+    }
+
+    /**
+     * Creates a new session with the given $id and $data
+     *
+     * @param string $id
+     * @param string $data
+     */
+    private function createNewSession($id, $data = '')
+    {
+        // get table/column
+        $dbTable    = $this->dbOptions['db_table'];
+        $dbDataCol = $this->dbOptions['db_data_col'];
+        $dbIdCol   = $this->dbOptions['db_id_col'];
+        $dbTimeCol = $this->dbOptions['db_time_col'];
+
+        $sql = "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time)";
+
+        $stmt = $this->db->prepare($sql);
+        $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
+        $stmt->bindParam(':data', $data, \PDO::PARAM_STR);
+        $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+        $stmt->execute();
+
+        return true;
+    }
+}
diff --git a/includes/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php b/includes/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php
new file mode 100644
index 000000000000..b61a2557b27c
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php
@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\SessionStorage;
+
+/**
+ * SessionStorageInterface.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ *
+ * @api
+ */
+interface SessionStorageInterface
+{
+    /**
+     * Starts the session.
+     *
+     * @api
+     */
+    function start();
+
+    /**
+     * Returns the session ID
+     *
+     * @return mixed  The session ID
+     *
+     * @throws \RuntimeException If the session was not started yet
+     *
+     * @api
+     */
+    function getId();
+
+    /**
+     * Reads data from this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param  string $key  A unique key identifying your data
+     *
+     * @return mixed Data associated with the key
+     *
+     * @throws \RuntimeException If an error occurs while reading data from this storage
+     *
+     * @api
+     */
+    function read($key);
+
+    /**
+     * Removes data from this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param  string $key  A unique key identifying your data
+     *
+     * @return mixed Data associated with the key
+     *
+     * @throws \RuntimeException If an error occurs while removing data from this storage
+     *
+     * @api
+     */
+    function remove($key);
+
+    /**
+     * Writes data to this storage.
+     *
+     * The preferred format for a key is directory style so naming conflicts can be avoided.
+     *
+     * @param  string $key   A unique key identifying your data
+     * @param  mixed  $data  Data associated with your key
+     *
+     * @throws \RuntimeException If an error occurs while writing to this storage
+     *
+     * @api
+     */
+    function write($key, $data);
+
+    /**
+     * Regenerates id that represents this storage.
+     *
+     * @param  Boolean $destroy Destroy session when regenerating?
+     *
+     * @return Boolean True if session regenerated, false if error
+     *
+     * @throws \RuntimeException If an error occurs while regenerating this storage
+     *
+     * @api
+     */
+    function regenerate($destroy = false);
+}
diff --git a/includes/Symfony/Component/HttpFoundation/composer.json b/includes/Symfony/Component/HttpFoundation/composer.json
new file mode 100644
index 000000000000..b3cfbbd1a728
--- /dev/null
+++ b/includes/Symfony/Component/HttpFoundation/composer.json
@@ -0,0 +1,22 @@
+{
+    "name": "symfony/http-foundation",
+    "type": "library",
+    "description": "Symfony HttpFoundation Component",
+    "keywords": [],
+    "homepage": "http://symfony.com",
+    "version": "2.0.4",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Fabien Potencier",
+            "email": "fabien@symfony.com"
+        },
+        {
+            "name": "Symfony Community",
+            "homepage": "http://symfony.com/contributors"
+        }
+    ],
+    "require": {
+        "php": ">=5.3.2"
+    }
+}
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index a7257c7c450d..ebeb4ab71283 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -2228,6 +2228,38 @@ function _drupal_bootstrap_configuration() {
   timer_start('page');
   // Initialize the configuration, including variables from settings.php.
   drupal_settings_initialize();
+
+  // Hook up the Symfony ClassLoader for loading PSR-0-compatible classes.
+  require_once(DRUPAL_ROOT . '/includes/Symfony/Component/ClassLoader/UniversalClassLoader.php');
+
+  // By default, use the UniversalClassLoader which is best for development,
+  // as it does not break when code is moved on the file system. It is slow,
+  // however, so for production the APC class loader should be used instead.
+  // @todo Switch to a cleaner way to switch autoloaders than variable_get().
+  switch (variable_get('autoloader_mode', 'default')) {
+    case 'apc':
+      if (function_exists('apc_store')) {
+        require_once(DRUPAL_ROOT . '/includes/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php');
+        $loader = new \Symfony\Component\ClassLoader\ApcUniversalClassLoader('drupal.' . $GLOBALS['drupal_hash_salt']);
+        break;
+      }
+      // If APC was not loaded, fall through to the default loader so that
+      // the site does not fail completely.
+    case 'dev':
+    case 'default':
+    default:
+      $loader = new \Symfony\Component\ClassLoader\UniversalClassLoader();
+      break;
+  }
+
+  // Register classes with namespaces.
+  $loader->registerNamespaces(array(
+    // All Symfony-borrowed code lives in /includes/Symfony.
+    'Symfony' => DRUPAL_ROOT . '/includes',
+  ));
+
+  // Activate the autoloader.
+  $loader->register();
 }
 
 /**
diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info
index c86f44042689..fab7b5ebc115 100644
--- a/modules/simpletest/simpletest.info
+++ b/modules/simpletest/simpletest.info
@@ -30,6 +30,7 @@ files[] = tests/path.test
 files[] = tests/registry.test
 files[] = tests/schema.test
 files[] = tests/session.test
+files[] = tests/symfony.test
 files[] = tests/tablesort.test
 files[] = tests/theme.test
 files[] = tests/unicode.test
diff --git a/modules/simpletest/tests/symfony.test b/modules/simpletest/tests/symfony.test
new file mode 100644
index 000000000000..8a7ecb967a69
--- /dev/null
+++ b/modules/simpletest/tests/symfony.test
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Tests for Symfony2-related functionality.
+ */
+
+/**
+ * Tests related to Symfony class loading.
+ */
+class SymfonyClassLoaderTestCase extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Class loader',
+      'description' => 'Confirm that the PSR-0 class loader is connected properly',
+      'group' => 'Symfony',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Test that we can lazy-load classes from the Symfony framework.
+   */
+  function testClassesLoad() {
+    $class_name = 'Symfony\\Component\\HttpFoundation\\Request';
+    $this->assertTrue(class_exists($class_name), t('Class !class_name exists', array('!class_name' => $class_name)));
+  }
+}
-- 
GitLab