- Patch #1178246 by Crell: added Symfony2 HttpFoundation library to core.
parent
d57f6e198a
commit
c85d62c609
|
@ -23,3 +23,4 @@ license, including:
|
|||
|
||||
jQuery - Copyright (c) 2008 - 2009 John Resig
|
||||
|
||||
Symfony2 - Copyright (c) 2004 - 2011 Fabien Potencier
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue