summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDries2011-10-24 18:14:03 (GMT)
committer Dries2011-10-24 18:14:03 (GMT)
commitc85d62c60902086e39fdea3ac5daa7f29f92edeb (patch)
treee06eead1a4ced8ff1b7bba666a73d9203eeeccd4
parentd57f6e198af0ebbfc87b97009fa06bae00d41968 (diff)
- Patch #1178246 by Crell: added Symfony2 HttpFoundation library to core.
-rw-r--r--COPYRIGHT.txt1
-rw-r--r--includes/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php96
-rw-r--r--includes/Symfony/Component/ClassLoader/ClassCollectionLoader.php222
-rw-r--r--includes/Symfony/Component/ClassLoader/DebugUniversalClassLoader.php62
-rw-r--r--includes/Symfony/Component/ClassLoader/LICENSE19
-rw-r--r--includes/Symfony/Component/ClassLoader/MapClassLoader.php76
-rw-r--r--includes/Symfony/Component/ClassLoader/UniversalClassLoader.php265
-rw-r--r--includes/Symfony/Component/ClassLoader/composer.json22
-rw-r--r--includes/Symfony/Component/HttpFoundation/ApacheRequest.php51
-rw-r--r--includes/Symfony/Component/HttpFoundation/Cookie.php207
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php30
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/Exception/FileException.php21
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php30
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php20
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/Exception/UploadException.php21
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/File.php544
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/MimeType/ContentTypeMimeTypeGuesser.php62
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php71
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php59
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php125
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php30
-rw-r--r--includes/Symfony/Component/HttpFoundation/File/UploadedFile.php225
-rw-r--r--includes/Symfony/Component/HttpFoundation/FileBag.php157
-rw-r--r--includes/Symfony/Component/HttpFoundation/HeaderBag.php306
-rw-r--r--includes/Symfony/Component/HttpFoundation/LICENSE19
-rw-r--r--includes/Symfony/Component/HttpFoundation/ParameterBag.php245
-rw-r--r--includes/Symfony/Component/HttpFoundation/RedirectResponse.php60
-rw-r--r--includes/Symfony/Component/HttpFoundation/Request.php1217
-rw-r--r--includes/Symfony/Component/HttpFoundation/RequestMatcher.php177
-rw-r--r--includes/Symfony/Component/HttpFoundation/RequestMatcherInterface.php33
-rw-r--r--includes/Symfony/Component/HttpFoundation/Response.php891
-rw-r--r--includes/Symfony/Component/HttpFoundation/ResponseHeaderBag.php238
-rw-r--r--includes/Symfony/Component/HttpFoundation/ServerBag.php43
-rw-r--r--includes/Symfony/Component/HttpFoundation/Session.php405
-rw-r--r--includes/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php58
-rw-r--r--includes/Symfony/Component/HttpFoundation/SessionStorage/FilesystemSessionStorage.php174
-rw-r--r--includes/Symfony/Component/HttpFoundation/SessionStorage/NativeSessionStorage.php180
-rw-r--r--includes/Symfony/Component/HttpFoundation/SessionStorage/PdoSessionStorage.php263
-rw-r--r--includes/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.php97
-rw-r--r--includes/Symfony/Component/HttpFoundation/composer.json22
-rw-r--r--includes/bootstrap.inc32
-rw-r--r--modules/simpletest/simpletest.info1
-rw-r--r--modules/simpletest/tests/symfony.test31
43 files changed, 6908 insertions, 0 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index e983474..18b074e 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 0000000..278f510
--- /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 0000000..da777f2
--- /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 0000000..8a958e0
--- /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 0000000..89df448
--- /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 0000000..cf17d42
--- /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 0000000..d296b94
--- /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 0000000..35b573e
--- /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 0000000..2721581
--- /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 0000000..8392812
--- /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 0000000..9c7fe68
--- /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 0000000..43c6cc8
--- /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 0000000..5b1aef8
--- /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 0000000..0444b87
--- /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 0000000..694e864
--- /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 0000000..3a900fd
--- /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 0000000..fb900b2
--- /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 0000000..1b869f2
--- /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 0000000..45d5a08
--- /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 0000000..23dd463
--- /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 0000000..c111583
--- /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 0000000..936ed70
--- /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 0000000..602cff2
--- /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 0000000..f614b09
--- /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 0000000..89df448
--- /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 0000000..3a38b5f
--- /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 0000000..3318b12
--- /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 0000000..e66abb7
--- /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 0000000..52ba078
--- /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 0000000..506ec79
--- /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 0000000..e8a7d56
--- /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 0000000..f243dcc
--- /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 0000000..02db3b1
--- /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 0000000..a835ef7
--- /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 0000000..62aac40
--- /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 0000000..87abd01
--- /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 0000000..b759f74
--- /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 0000000..78f90b8
--- /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 0000000..b61a255
--- /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 0000000..b3cfbbd
--- /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 a7257c7..ebeb4ab 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 c86f440..fab7b5e 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 0000000..8a7ecb9
--- /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)));
+ }
+}