the whole shebang

This commit is contained in:
2014-11-25 16:42:40 +01:00
parent 7f74c0613e
commit ab1334c0cf
3686 changed files with 496409 additions and 1 deletions

View File

@@ -0,0 +1,3 @@
vendor/
composer.lock
phpunit.xml

View File

@@ -0,0 +1,172 @@
<?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 an Accept-* header.
*
* An accept header is compound with a list of items,
* sorted by descending quality.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class AcceptHeader
{
/**
* @var AcceptHeaderItem[]
*/
private $items = array();
/**
* @var bool
*/
private $sorted = true;
/**
* Constructor.
*
* @param AcceptHeaderItem[] $items
*/
public function __construct(array $items)
{
foreach ($items as $item) {
$this->add($item);
}
}
/**
* Builds an AcceptHeader instance from a string.
*
* @param string $headerValue
*
* @return AcceptHeader
*/
public static function fromString($headerValue)
{
$index = 0;
return new self(array_map(function ($itemValue) use (&$index) {
$item = AcceptHeaderItem::fromString($itemValue);
$item->setIndex($index++);
return $item;
}, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
}
/**
* Returns header value's string representation.
*
* @return string
*/
public function __toString()
{
return implode(',', $this->items);
}
/**
* Tests if header has given value.
*
* @param string $value
*
* @return Boolean
*/
public function has($value)
{
return isset($this->items[$value]);
}
/**
* Returns given value's item, if exists.
*
* @param string $value
*
* @return AcceptHeaderItem|null
*/
public function get($value)
{
return isset($this->items[$value]) ? $this->items[$value] : null;
}
/**
* Adds an item.
*
* @param AcceptHeaderItem $item
*
* @return AcceptHeader
*/
public function add(AcceptHeaderItem $item)
{
$this->items[$item->getValue()] = $item;
$this->sorted = false;
return $this;
}
/**
* Returns all items.
*
* @return AcceptHeaderItem[]
*/
public function all()
{
$this->sort();
return $this->items;
}
/**
* Filters items on their value using given regex.
*
* @param string $pattern
*
* @return AcceptHeader
*/
public function filter($pattern)
{
return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) {
return preg_match($pattern, $item->getValue());
}));
}
/**
* Returns first item.
*
* @return AcceptHeaderItem|null
*/
public function first()
{
$this->sort();
return !empty($this->items) ? reset($this->items) : null;
}
/**
* Sorts items by descending quality
*/
private function sort()
{
if (!$this->sorted) {
uasort($this->items, function ($a, $b) {
$qA = $a->getQuality();
$qB = $b->getQuality();
if ($qA === $qB) {
return $a->getIndex() > $b->getIndex() ? 1 : -1;
}
return $qA > $qB ? -1 : 1;
});
$this->sorted = true;
}
}
}

View File

@@ -0,0 +1,226 @@
<?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 an Accept-* header item.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class AcceptHeaderItem
{
/**
* @var string
*/
private $value;
/**
* @var float
*/
private $quality = 1.0;
/**
* @var int
*/
private $index = 0;
/**
* @var array
*/
private $attributes = array();
/**
* Constructor.
*
* @param string $value
* @param array $attributes
*/
public function __construct($value, array $attributes = array())
{
$this->value = $value;
foreach ($attributes as $name => $value) {
$this->setAttribute($name, $value);
}
}
/**
* Builds an AcceptHeaderInstance instance from a string.
*
* @param string $itemValue
*
* @return AcceptHeaderItem
*/
public static function fromString($itemValue)
{
$bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$value = array_shift($bits);
$attributes = array();
$lastNullAttribute = null;
foreach ($bits as $bit) {
if (($start = substr($bit, 0, 1)) === ($end = substr($bit, -1)) && ($start === '"' || $start === '\'')) {
$attributes[$lastNullAttribute] = substr($bit, 1, -1);
} elseif ('=' === $end) {
$lastNullAttribute = $bit = substr($bit, 0, -1);
$attributes[$bit] = null;
} else {
$parts = explode('=', $bit);
$attributes[$parts[0]] = isset($parts[1]) && strlen($parts[1]) > 0 ? $parts[1] : '';
}
}
return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ($start === '"' || $start === '\'') ? substr($value, 1, -1) : $value, $attributes);
}
/**
* Returns header value's string representation.
*
* @return string
*/
public function __toString()
{
$string = $this->value.($this->quality < 1 ? ';q='.$this->quality : '');
if (count($this->attributes) > 0) {
$string .= ';'.implode(';', array_map(function($name, $value) {
return sprintf(preg_match('/[,;=]/', $value) ? '%s="%s"' : '%s=%s', $name, $value);
}, array_keys($this->attributes), $this->attributes));
}
return $string;
}
/**
* Set the item value.
*
* @param string $value
*
* @return AcceptHeaderItem
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* Returns the item value.
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Set the item quality.
*
* @param float $quality
*
* @return AcceptHeaderItem
*/
public function setQuality($quality)
{
$this->quality = $quality;
return $this;
}
/**
* Returns the item quality.
*
* @return float
*/
public function getQuality()
{
return $this->quality;
}
/**
* Set the item index.
*
* @param int $index
*
* @return AcceptHeaderItem
*/
public function setIndex($index)
{
$this->index = $index;
return $this;
}
/**
* Returns the item index.
*
* @return int
*/
public function getIndex()
{
return $this->index;
}
/**
* Tests if an attribute exists.
*
* @param string $name
*
* @return Boolean
*/
public function hasAttribute($name)
{
return isset($this->attributes[$name]);
}
/**
* Returns an attribute by its name.
*
* @param string $name
* @param mixed $default
*
* @return mixed
*/
public function getAttribute($name, $default = null)
{
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
}
/**
* Returns all attributes.
*
* @return array
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Set an attribute.
*
* @param string $name
* @param string $value
*
* @return AcceptHeaderItem
*/
public function setAttribute($name, $value)
{
if ('q' === $name) {
$this->quality = (float) $value;
} else {
$this->attributes[$name] = (string) $value;
}
return $this;
}
}

View File

@@ -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;
/**
* 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;
}
}

View File

@@ -0,0 +1,280 @@
<?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\File;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
/**
* BinaryFileResponse represents an HTTP response delivering a file.
*
* @author Niklas Fiekas <niklas.fiekas@tu-clausthal.de>
* @author stealth35 <stealth35-php@live.fr>
* @author Igor Wiedler <igor@wiedler.ch>
* @author Jordan Alliot <jordan.alliot@gmail.com>
* @author Sergey Linnik <linniksa@gmail.com>
*/
class BinaryFileResponse extends Response
{
protected static $trustXSendfileTypeHeader = false;
protected $file;
protected $offset;
protected $maxlen;
/**
* Constructor.
*
* @param SplFileInfo|string $file The file to stream
* @param integer $status The response status code
* @param array $headers An array of response headers
* @param boolean $public Files are public by default
* @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param boolean $autoEtag Whether the ETag header should be automatically set
* @param boolean $autoLastModified Whether the Last-Modified header should be automatically set
*/
public function __construct($file, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
{
parent::__construct(null, $status, $headers);
$this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified);
if ($public) {
$this->setPublic();
}
}
/**
* {@inheritdoc}
*/
public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
{
return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
}
/**
* Sets the file to stream.
*
* @param SplFileInfo|string $file The file to stream
* @param string $contentDisposition
* @param Boolean $autoEtag
* @param Boolean $autoLastModified
*
* @return BinaryFileResponse
*
* @throws FileException
*/
public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
{
$file = new File((string) $file);
if (!$file->isReadable()) {
throw new FileException('File must be readable.');
}
$this->file = $file;
if ($autoEtag) {
$this->setAutoEtag();
}
if ($autoLastModified) {
$this->setAutoLastModified();
}
if ($contentDisposition) {
$this->setContentDisposition($contentDisposition);
}
return $this;
}
/**
* Gets the file.
*
* @return File The file to stream
*/
public function getFile()
{
return $this->file;
}
/**
* Automatically sets the Last-Modified header according the file modification date.
*/
public function setAutoLastModified()
{
$this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime()));
return $this;
}
/**
* Automatically sets the ETag header according to the checksum of the file.
*/
public function setAutoEtag()
{
$this->setEtag(sha1_file($this->file->getPathname()));
return $this;
}
/**
* Sets the Content-Disposition header with the given filename.
*
* @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT
* @param string $filename Optionally use this filename instead of the real name of the file
* @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename
*
* @return BinaryFileResponse
*/
public function setContentDisposition($disposition, $filename = '', $filenameFallback = '')
{
if ($filename === '') {
$filename = $this->file->getFilename();
}
$dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback);
$this->headers->set('Content-Disposition', $dispositionHeader);
return $this;
}
/**
* {@inheritdoc}
*/
public function prepare(Request $request)
{
$this->headers->set('Content-Length', $this->file->getSize());
$this->headers->set('Accept-Ranges', 'bytes');
$this->headers->set('Content-Transfer-Encoding', 'binary');
if (!$this->headers->has('Content-Type')) {
$this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
}
if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
$this->setProtocolVersion('1.1');
}
$this->ensureIEOverSSLCompatibility($request);
$this->offset = 0;
$this->maxlen = -1;
if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
// Use X-Sendfile, do not send any content.
$type = $request->headers->get('X-Sendfile-Type');
$path = $this->file->getRealPath();
if (strtolower($type) == 'x-accel-redirect') {
// Do X-Accel-Mapping substitutions.
foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
$mapping = explode('=', $mapping, 2);
if (2 == count($mapping)) {
$location = trim($mapping[0]);
$pathPrefix = trim($mapping[1]);
if (substr($path, 0, strlen($pathPrefix)) == $pathPrefix) {
$path = $location.substr($path, strlen($pathPrefix));
break;
}
}
}
}
$this->headers->set($type, $path);
$this->maxlen = 0;
} elseif ($request->headers->has('Range')) {
// Process the range headers.
if (!$request->headers->has('If-Range') || $this->getEtag() == $request->headers->get('If-Range')) {
$range = $request->headers->get('Range');
$fileSize = $this->file->getSize();
list($start, $end) = explode('-', substr($range, 6), 2) + array(0);
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
if ('' === $start) {
$start = $fileSize - $end;
$end = $fileSize - 1;
} else {
$start = (int) $start;
}
$start = max($start, 0);
$end = min($end, $fileSize - 1);
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
$this->offset = $start;
$this->setStatusCode(206);
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
}
}
return $this;
}
/**
* Sends the file.
*/
public function sendContent()
{
if (!$this->isSuccessful()) {
parent::sendContent();
return;
}
if (0 === $this->maxlen) {
return;
}
$out = fopen('php://output', 'wb');
$file = fopen($this->file->getPathname(), 'rb');
stream_copy_to_stream($file, $out, $this->maxlen, $this->offset);
fclose($out);
fclose($file);
}
/**
* {@inheritdoc}
*
* @throws \LogicException when the content is not null
*/
public function setContent($content)
{
if (null !== $content) {
throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
}
}
/**
* {@inheritdoc}
*
* @return false
*/
public function getContent()
{
return false;
}
/**
* Trust X-Sendfile-Type header.
*/
public static function trustXSendfileTypeHeader()
{
self::$trustXSendfileTypeHeader = true;
}
}

View File

@@ -0,0 +1,98 @@
CHANGELOG
=========
2.3.0
-----
* added support for ranges of IPs in trusted proxies
* `UploadedFile::isValid` now returns false if the file was not uploaded via HTTP (in a non-test mode)
* Improved error-handling of `\Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler`
to ensure the supplied PDO handler throws Exceptions on error (as the class expects). Added related test cases
to verify that Exceptions are properly thrown when the PDO queries fail.
2.2.0
-----
* fixed the Request::create() precedence (URI information always take precedence now)
* added Request::getTrustedProxies()
* deprecated Request::isProxyTrusted()
* [BC BREAK] JsonResponse does not turn a top level empty array to an object anymore, use an ArrayObject to enforce objects
* added a IpUtils class to check if an IP belongs to a CIDR
* added Request::getRealMethod() to get the "real" HTTP method (getMethod() returns the "intended" HTTP method)
* disabled _method request parameter support by default (call Request::enableHttpMethodParameterOverride() to
enable it, and Request::getHttpMethodParameterOverride() to check if it is supported)
* Request::splitHttpAcceptHeader() method is deprecated and will be removed in 2.3
* Deprecated Flashbag::count() and \Countable interface, will be removed in 2.3
2.1.0
-----
* added Request::getSchemeAndHttpHost() and Request::getUserInfo()
* added a fluent interface to the Response class
* added Request::isProxyTrusted()
* added JsonResponse
* added a getTargetUrl method to RedirectResponse
* added support for streamed responses
* made Response::prepare() method the place to enforce HTTP specification
* [BC BREAK] moved management of the locale from the Session class to the Request class
* added a generic access to the PHP built-in filter mechanism: ParameterBag::filter()
* made FileBinaryMimeTypeGuesser command configurable
* added Request::getUser() and Request::getPassword()
* added support for the PATCH method in Request
* removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3
* added ResponseHeaderBag::makeDisposition() (implements RFC 6266)
* made mimetype to extension conversion configurable
* [BC BREAK] Moved all session related classes and interfaces into own namespace, as
`Symfony\Component\HttpFoundation\Session` and renamed classes accordingly.
Session handlers are located in the subnamespace `Symfony\Component\HttpFoundation\Session\Handler`.
* SessionHandlers must implement `\SessionHandlerInterface` or extend from the
`Symfony\Component\HttpFoundation\Storage\Handler\NativeSessionHandler` base class.
* Added internal storage driver proxy mechanism for forward compatibility with
PHP 5.4 `\SessionHandler` class.
* Added session handlers for custom Memcache, Memcached and Null session save handlers.
* [BC BREAK] Removed `NativeSessionStorage` and replaced with `NativeFileSessionHandler`.
* [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and
`remove()`. Added `getBag()`, `registerBag()`. The `NativeSessionStorage` class
is a mediator for the session storage internals including the session handlers
which do the real work of participating in the internal PHP session workflow.
* [BC BREAK] Introduced mock implementations of `SessionStorage` to enable unit
and functional testing without starting real PHP sessions. Removed
`ArraySessionStorage`, and replaced with `MockArraySessionStorage` for unit
tests; removed `FilesystemSessionStorage`, and replaced with`MockFileSessionStorage`
for functional tests. These do not interact with global session ini
configuration values, session functions or `$_SESSION` superglobal. This means
they can be configured directly allowing multiple instances to work without
conflicting in the same PHP process.
* [BC BREAK] Removed the `close()` method from the `Session` class, as this is
now redundant.
* Deprecated the following methods from the Session class: `setFlash()`, `setFlashes()`
`getFlash()`, `hasFlash()`, and `removeFlash()`. Use `getFlashBag()` instead
which returns a `FlashBagInterface`.
* `Session->clear()` now only clears session attributes as before it cleared
flash messages and attributes. `Session->getFlashBag()->all()` clears flashes now.
* Session data is now managed by `SessionBagInterface` to better encapsulate
session data.
* Refactored session attribute and flash messages system to their own
`SessionBagInterface` implementations.
* Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This
implementation is ESI compatible.
* Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire
behaviour of messages auto expiring after one page page load. Messages must
be retrieved by `get()` or `all()`.
* Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate
attributes storage behaviour from 2.0.x (default).
* Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for
namespace session attributes.
* Flash API can stores messages in an array so there may be multiple messages
per flash type. The old `Session` class API remains without BC break as it
will allow single messages as before.
* Added basic session meta-data to the session to record session create time,
last updated time, and the lifetime of the session cookie that was provided
to the client.
* Request::getClientIp() method doesn't take a parameter anymore but bases
itself on the trustProxy parameter.
* Added isMethod() to Request object.
* [BC BREAK] The methods `getPathInfo()`, `getBaseUrl()` and `getBasePath()` of
a `Request` now all return a raw value (vs a urldecoded value before). Any call
to one of these methods must be checked and wrapped in a `rawurldecode()` if
needed.

View File

@@ -0,0 +1,210 @@
<?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
*
* @throws \InvalidArgumentException
*
* @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 (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;
}
/**
* Returns the cookie as a string.
*
* @return string The cookie
*/
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 ($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();
}
}

View File

@@ -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 <bschussek@gmail.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));
}
}

View File

@@ -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 <bschussek@gmail.com>
*/
class FileException extends \RuntimeException
{
}

View File

@@ -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 <bschussek@gmail.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));
}
}

View File

@@ -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)));
}
}

View File

@@ -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 <bschussek@gmail.com>
*/
class UploadException extends FileException
{
}

View File

@@ -0,0 +1,160 @@
<?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;
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
/**
* A file in the file system.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
class File extends \SplFileInfo
{
/**
* Constructs a new file from the given path.
*
* @param string $path The path to the file
* @param Boolean $checkPath Whether to check the path or not
*
* @throws FileNotFoundException If the given path is not a file
*
* @api
*/
public function __construct($path, $checkPath = true)
{
if ($checkPath && !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.
*
* This method uses the mime type as guessed by getMimeType()
* to guess the file extension.
*
* @return string|null The guessed extension or null if it cannot be guessed
*
* @api
*
* @see ExtensionGuesser
* @see getMimeType()
*/
public function guessExtension()
{
$type = $this->getMimeType();
$guesser = ExtensionGuesser::getInstance();
return $guesser->guess($type);
}
/**
* Returns the mime type of the file.
*
* The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(),
* mime_content_type() and the system binary "file" (in this order), depending on
* which of those are available.
*
* @return string|null The guessed mime type (i.e. "application/pdf")
*
* @see MimeTypeGuesser
*
* @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)
{
$target = $this->getTargetFile($directory, $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 & ~umask());
return $target;
}
protected function getTargetFile($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() : $this->getName($name));
return new File($target, false);
}
/**
* Returns locale independent base name of the given path.
*
* @param string $name The new file name
*
* @return string containing
*/
protected function getName($name)
{
$originalName = str_replace('\\', '/', $name);
$pos = strrpos($originalName, '/');
$originalName = false === $pos ? $originalName : substr($originalName, $pos + 1);
return $originalName;
}
}

View File

@@ -0,0 +1,99 @@
<?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;
/**
* A singleton mime type to file extension guesser.
*
* A default guesser is provided.
* You can register custom guessers by calling the register()
* method on the singleton instance:
*
* $guesser = ExtensionGuesser::getInstance();
* $guesser->register(new MyCustomExtensionGuesser());
*
* The last registered guesser is preferred over previously registered ones.
*/
class ExtensionGuesser implements ExtensionGuesserInterface
{
/**
* The singleton instance
*
* @var ExtensionGuesser
*/
private static $instance = null;
/**
* All registered ExtensionGuesserInterface instances
*
* @var array
*/
protected $guessers = array();
/**
* Returns the singleton instance
*
* @return ExtensionGuesser
*/
public static function getInstance()
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Registers all natively provided extension guessers
*/
private function __construct()
{
$this->register(new MimeTypeExtensionGuesser());
}
/**
* Registers a new extension guesser
*
* When guessing, this guesser is preferred over previously registered ones.
*
* @param ExtensionGuesserInterface $guesser
*/
public function register(ExtensionGuesserInterface $guesser)
{
array_unshift($this->guessers, $guesser);
}
/**
* Tries to guess the extension
*
* The mime type 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 $mimeType The mime type
* @return string The guessed extension or NULL, if none could be guessed
*/
public function guess($mimeType)
{
foreach ($this->guessers as $guesser) {
$extension = $guesser->guess($mimeType);
if (null !== $extension) {
break;
}
}
return $extension;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\File\MimeType;
/**
* Guesses the file extension corresponding to a given mime type
*/
interface ExtensionGuesserInterface
{
/**
* Makes a best guess for a file extension, given a mime type
*
* @param string $mimeType The mime type
* @return string The guessed extension or NULL, if none could be guessed
*/
public function guess($mimeType);
}

View File

@@ -0,0 +1,87 @@
<?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 <bschussek@gmail.com>
*/
class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
{
private $cmd;
/**
* Constructor.
*
* The $cmd pattern must contain a "%s" string that will be replaced
* with the file name to guess.
*
* The command output must start with the mime type of the file.
*
* @param string $cmd The command to run to get the mime type of a file
*/
public function __construct($cmd = 'file -b --mime %s 2>/dev/null')
{
$this->cmd = $cmd;
}
/**
* Returns whether this guesser is supported on the current OS
*
* @return Boolean
*/
public static function isSupported()
{
return !defined('PHP_WINDOWS_VERSION_BUILD') && function_exists('passthru') && function_exists('escapeshellarg');
}
/**
* {@inheritdoc}
*/
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($this->cmd, 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];
}
}

View File

@@ -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 using the PECL extension FileInfo.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
{
private $magicFile;
/**
* Constructor.
*
* @param string $magicFile A magic file to use with the finfo instance
*
* @link http://www.php.net/manual/en/function.finfo-open.php
*/
public function __construct($magicFile = null)
{
$this->magicFile = $magicFile;
}
/**
* Returns whether this guesser is supported on the current OS/PHP setup
*
* @return Boolean
*/
public static function isSupported()
{
return function_exists('finfo_open');
}
/**
* {@inheritdoc}
*/
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, $this->magicFile)) {
return null;
}
return $finfo->file($path);
}
}

View File

@@ -0,0 +1,805 @@
<?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;
/**
* Provides a best-guess mapping of mime type to file extension.
*/
class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
{
/**
* A map of mime types and their default extensions.
*
* This list has been placed under the public domain by the Apache HTTPD project.
* This list has been updated from upstream on 2013-04-23.
*
* @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
*
* @var array
*/
protected $defaultExtensions = array(
'application/andrew-inset' => 'ez',
'application/applixware' => 'aw',
'application/atom+xml' => 'atom',
'application/atomcat+xml' => 'atomcat',
'application/atomsvc+xml' => 'atomsvc',
'application/ccxml+xml' => 'ccxml',
'application/cdmi-capability' => 'cdmia',
'application/cdmi-container' => 'cdmic',
'application/cdmi-domain' => 'cdmid',
'application/cdmi-object' => 'cdmio',
'application/cdmi-queue' => 'cdmiq',
'application/cu-seeme' => 'cu',
'application/davmount+xml' => 'davmount',
'application/docbook+xml' => 'dbk',
'application/dssc+der' => 'dssc',
'application/dssc+xml' => 'xdssc',
'application/ecmascript' => 'ecma',
'application/emma+xml' => 'emma',
'application/epub+zip' => 'epub',
'application/exi' => 'exi',
'application/font-tdpfr' => 'pfr',
'application/gml+xml' => 'gml',
'application/gpx+xml' => 'gpx',
'application/gxf' => 'gxf',
'application/hyperstudio' => 'stk',
'application/inkml+xml' => 'ink',
'application/ipfix' => 'ipfix',
'application/java-archive' => 'jar',
'application/java-serialized-object' => 'ser',
'application/java-vm' => 'class',
'application/javascript' => 'js',
'application/json' => 'json',
'application/jsonml+json' => 'jsonml',
'application/lost+xml' => 'lostxml',
'application/mac-binhex40' => 'hqx',
'application/mac-compactpro' => 'cpt',
'application/mads+xml' => 'mads',
'application/marc' => 'mrc',
'application/marcxml+xml' => 'mrcx',
'application/mathematica' => 'ma',
'application/mathml+xml' => 'mathml',
'application/mbox' => 'mbox',
'application/mediaservercontrol+xml' => 'mscml',
'application/metalink+xml' => 'metalink',
'application/metalink4+xml' => 'meta4',
'application/mets+xml' => 'mets',
'application/mods+xml' => 'mods',
'application/mp21' => 'm21',
'application/mp4' => 'mp4s',
'application/msword' => 'doc',
'application/mxf' => 'mxf',
'application/octet-stream' => 'bin',
'application/oda' => 'oda',
'application/oebps-package+xml' => 'opf',
'application/ogg' => 'ogx',
'application/omdoc+xml' => 'omdoc',
'application/onenote' => 'onetoc',
'application/oxps' => 'oxps',
'application/patch-ops-error+xml' => 'xer',
'application/pdf' => 'pdf',
'application/pgp-encrypted' => 'pgp',
'application/pgp-signature' => 'asc',
'application/pics-rules' => 'prf',
'application/pkcs10' => 'p10',
'application/pkcs7-mime' => 'p7m',
'application/pkcs7-signature' => 'p7s',
'application/pkcs8' => 'p8',
'application/pkix-attr-cert' => 'ac',
'application/pkix-cert' => 'cer',
'application/pkix-crl' => 'crl',
'application/pkix-pkipath' => 'pkipath',
'application/pkixcmp' => 'pki',
'application/pls+xml' => 'pls',
'application/postscript' => 'ai',
'application/prs.cww' => 'cww',
'application/pskc+xml' => 'pskcxml',
'application/rdf+xml' => 'rdf',
'application/reginfo+xml' => 'rif',
'application/relax-ng-compact-syntax' => 'rnc',
'application/resource-lists+xml' => 'rl',
'application/resource-lists-diff+xml' => 'rld',
'application/rls-services+xml' => 'rs',
'application/rpki-ghostbusters' => 'gbr',
'application/rpki-manifest' => 'mft',
'application/rpki-roa' => 'roa',
'application/rsd+xml' => 'rsd',
'application/rss+xml' => 'rss',
'application/rtf' => 'rtf',
'application/sbml+xml' => 'sbml',
'application/scvp-cv-request' => 'scq',
'application/scvp-cv-response' => 'scs',
'application/scvp-vp-request' => 'spq',
'application/scvp-vp-response' => 'spp',
'application/sdp' => 'sdp',
'application/set-payment-initiation' => 'setpay',
'application/set-registration-initiation' => 'setreg',
'application/shf+xml' => 'shf',
'application/smil+xml' => 'smi',
'application/sparql-query' => 'rq',
'application/sparql-results+xml' => 'srx',
'application/srgs' => 'gram',
'application/srgs+xml' => 'grxml',
'application/sru+xml' => 'sru',
'application/ssdl+xml' => 'ssdl',
'application/ssml+xml' => 'ssml',
'application/tei+xml' => 'tei',
'application/thraud+xml' => 'tfi',
'application/timestamped-data' => 'tsd',
'application/vnd.3gpp.pic-bw-large' => 'plb',
'application/vnd.3gpp.pic-bw-small' => 'psb',
'application/vnd.3gpp.pic-bw-var' => 'pvb',
'application/vnd.3gpp2.tcap' => 'tcap',
'application/vnd.3m.post-it-notes' => 'pwn',
'application/vnd.accpac.simply.aso' => 'aso',
'application/vnd.accpac.simply.imp' => 'imp',
'application/vnd.acucobol' => 'acu',
'application/vnd.acucorp' => 'atc',
'application/vnd.adobe.air-application-installer-package+zip' => 'air',
'application/vnd.adobe.formscentral.fcdt' => 'fcdt',
'application/vnd.adobe.fxp' => 'fxp',
'application/vnd.adobe.xdp+xml' => 'xdp',
'application/vnd.adobe.xfdf' => 'xfdf',
'application/vnd.ahead.space' => 'ahead',
'application/vnd.airzip.filesecure.azf' => 'azf',
'application/vnd.airzip.filesecure.azs' => 'azs',
'application/vnd.amazon.ebook' => 'azw',
'application/vnd.americandynamics.acc' => 'acc',
'application/vnd.amiga.ami' => 'ami',
'application/vnd.android.package-archive' => 'apk',
'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
'application/vnd.antix.game-component' => 'atx',
'application/vnd.apple.installer+xml' => 'mpkg',
'application/vnd.apple.mpegurl' => 'm3u8',
'application/vnd.aristanetworks.swi' => 'swi',
'application/vnd.astraea-software.iota' => 'iota',
'application/vnd.audiograph' => 'aep',
'application/vnd.blueice.multipass' => 'mpm',
'application/vnd.bmi' => 'bmi',
'application/vnd.businessobjects' => 'rep',
'application/vnd.chemdraw+xml' => 'cdxml',
'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
'application/vnd.cinderella' => 'cdy',
'application/vnd.claymore' => 'cla',
'application/vnd.cloanto.rp9' => 'rp9',
'application/vnd.clonk.c4group' => 'c4g',
'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
'application/vnd.commonspace' => 'csp',
'application/vnd.contact.cmsg' => 'cdbcmsg',
'application/vnd.cosmocaller' => 'cmc',
'application/vnd.crick.clicker' => 'clkx',
'application/vnd.crick.clicker.keyboard' => 'clkk',
'application/vnd.crick.clicker.palette' => 'clkp',
'application/vnd.crick.clicker.template' => 'clkt',
'application/vnd.crick.clicker.wordbank' => 'clkw',
'application/vnd.criticaltools.wbs+xml' => 'wbs',
'application/vnd.ctc-posml' => 'pml',
'application/vnd.cups-ppd' => 'ppd',
'application/vnd.curl.car' => 'car',
'application/vnd.curl.pcurl' => 'pcurl',
'application/vnd.dart' => 'dart',
'application/vnd.data-vision.rdz' => 'rdz',
'application/vnd.dece.data' => 'uvf',
'application/vnd.dece.ttml+xml' => 'uvt',
'application/vnd.dece.unspecified' => 'uvx',
'application/vnd.dece.zip' => 'uvz',
'application/vnd.denovo.fcselayout-link' => 'fe_launch',
'application/vnd.dna' => 'dna',
'application/vnd.dolby.mlp' => 'mlp',
'application/vnd.dpgraph' => 'dpg',
'application/vnd.dreamfactory' => 'dfac',
'application/vnd.ds-keypoint' => 'kpxx',
'application/vnd.dvb.ait' => 'ait',
'application/vnd.dvb.service' => 'svc',
'application/vnd.dynageo' => 'geo',
'application/vnd.ecowin.chart' => 'mag',
'application/vnd.enliven' => 'nml',
'application/vnd.epson.esf' => 'esf',
'application/vnd.epson.msf' => 'msf',
'application/vnd.epson.quickanime' => 'qam',
'application/vnd.epson.salt' => 'slt',
'application/vnd.epson.ssf' => 'ssf',
'application/vnd.eszigno3+xml' => 'es3',
'application/vnd.ezpix-album' => 'ez2',
'application/vnd.ezpix-package' => 'ez3',
'application/vnd.fdf' => 'fdf',
'application/vnd.fdsn.mseed' => 'mseed',
'application/vnd.fdsn.seed' => 'seed',
'application/vnd.flographit' => 'gph',
'application/vnd.fluxtime.clip' => 'ftc',
'application/vnd.framemaker' => 'fm',
'application/vnd.frogans.fnc' => 'fnc',
'application/vnd.frogans.ltf' => 'ltf',
'application/vnd.fsc.weblaunch' => 'fsc',
'application/vnd.fujitsu.oasys' => 'oas',
'application/vnd.fujitsu.oasys2' => 'oa2',
'application/vnd.fujitsu.oasys3' => 'oa3',
'application/vnd.fujitsu.oasysgp' => 'fg5',
'application/vnd.fujitsu.oasysprs' => 'bh2',
'application/vnd.fujixerox.ddd' => 'ddd',
'application/vnd.fujixerox.docuworks' => 'xdw',
'application/vnd.fujixerox.docuworks.binder' => 'xbd',
'application/vnd.fuzzysheet' => 'fzs',
'application/vnd.genomatix.tuxedo' => 'txd',
'application/vnd.geogebra.file' => 'ggb',
'application/vnd.geogebra.tool' => 'ggt',
'application/vnd.geometry-explorer' => 'gex',
'application/vnd.geonext' => 'gxt',
'application/vnd.geoplan' => 'g2w',
'application/vnd.geospace' => 'g3w',
'application/vnd.gmx' => 'gmx',
'application/vnd.google-earth.kml+xml' => 'kml',
'application/vnd.google-earth.kmz' => 'kmz',
'application/vnd.grafeq' => 'gqf',
'application/vnd.groove-account' => 'gac',
'application/vnd.groove-help' => 'ghf',
'application/vnd.groove-identity-message' => 'gim',
'application/vnd.groove-injector' => 'grv',
'application/vnd.groove-tool-message' => 'gtm',
'application/vnd.groove-tool-template' => 'tpl',
'application/vnd.groove-vcard' => 'vcg',
'application/vnd.hal+xml' => 'hal',
'application/vnd.handheld-entertainment+xml' => 'zmm',
'application/vnd.hbci' => 'hbci',
'application/vnd.hhe.lesson-player' => 'les',
'application/vnd.hp-hpgl' => 'hpgl',
'application/vnd.hp-hpid' => 'hpid',
'application/vnd.hp-hps' => 'hps',
'application/vnd.hp-jlyt' => 'jlt',
'application/vnd.hp-pcl' => 'pcl',
'application/vnd.hp-pclxl' => 'pclxl',
'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
'application/vnd.ibm.minipay' => 'mpy',
'application/vnd.ibm.modcap' => 'afp',
'application/vnd.ibm.rights-management' => 'irm',
'application/vnd.ibm.secure-container' => 'sc',
'application/vnd.iccprofile' => 'icc',
'application/vnd.igloader' => 'igl',
'application/vnd.immervision-ivp' => 'ivp',
'application/vnd.immervision-ivu' => 'ivu',
'application/vnd.insors.igm' => 'igm',
'application/vnd.intercon.formnet' => 'xpw',
'application/vnd.intergeo' => 'i2g',
'application/vnd.intu.qbo' => 'qbo',
'application/vnd.intu.qfx' => 'qfx',
'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
'application/vnd.irepository.package+xml' => 'irp',
'application/vnd.is-xpr' => 'xpr',
'application/vnd.isac.fcs' => 'fcs',
'application/vnd.jam' => 'jam',
'application/vnd.jcp.javame.midlet-rms' => 'rms',
'application/vnd.jisp' => 'jisp',
'application/vnd.joost.joda-archive' => 'joda',
'application/vnd.kahootz' => 'ktz',
'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.kenameaapp' => 'htke',
'application/vnd.kidspiration' => 'kia',
'application/vnd.kinar' => 'kne',
'application/vnd.koan' => 'skp',
'application/vnd.kodak-descriptor' => 'sse',
'application/vnd.las.las+xml' => 'lasxml',
'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
'application/vnd.lotus-1-2-3' => '123',
'application/vnd.lotus-approach' => 'apr',
'application/vnd.lotus-freelance' => 'pre',
'application/vnd.lotus-notes' => 'nsf',
'application/vnd.lotus-organizer' => 'org',
'application/vnd.lotus-screencam' => 'scm',
'application/vnd.lotus-wordpro' => 'lwp',
'application/vnd.macports.portpkg' => 'portpkg',
'application/vnd.mcd' => 'mcd',
'application/vnd.medcalcdata' => 'mc1',
'application/vnd.mediastation.cdkey' => 'cdkey',
'application/vnd.mfer' => 'mwf',
'application/vnd.mfmp' => 'mfm',
'application/vnd.micrografx.flo' => 'flo',
'application/vnd.micrografx.igx' => 'igx',
'application/vnd.mif' => 'mif',
'application/vnd.mobius.daf' => 'daf',
'application/vnd.mobius.dis' => 'dis',
'application/vnd.mobius.mbk' => 'mbk',
'application/vnd.mobius.mqy' => 'mqy',
'application/vnd.mobius.msl' => 'msl',
'application/vnd.mobius.plc' => 'plc',
'application/vnd.mobius.txf' => 'txf',
'application/vnd.mophun.application' => 'mpn',
'application/vnd.mophun.certificate' => 'mpc',
'application/vnd.mozilla.xul+xml' => 'xul',
'application/vnd.ms-artgalry' => 'cil',
'application/vnd.ms-cab-compressed' => 'cab',
'application/vnd.ms-excel' => 'xls',
'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
'application/vnd.ms-fontobject' => 'eot',
'application/vnd.ms-htmlhelp' => 'chm',
'application/vnd.ms-ims' => 'ims',
'application/vnd.ms-lrm' => 'lrm',
'application/vnd.ms-officetheme' => 'thmx',
'application/vnd.ms-pki.seccat' => 'cat',
'application/vnd.ms-pki.stl' => 'stl',
'application/vnd.ms-powerpoint' => 'ppt',
'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
'application/vnd.ms-project' => 'mpp',
'application/vnd.ms-word.document.macroenabled.12' => 'docm',
'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
'application/vnd.ms-works' => 'wps',
'application/vnd.ms-wpl' => 'wpl',
'application/vnd.ms-xpsdocument' => 'xps',
'application/vnd.mseq' => 'mseq',
'application/vnd.musician' => 'mus',
'application/vnd.muvee.style' => 'msty',
'application/vnd.mynfc' => 'taglet',
'application/vnd.neurolanguage.nlu' => 'nlu',
'application/vnd.nitf' => 'ntf',
'application/vnd.noblenet-directory' => 'nnd',
'application/vnd.noblenet-sealer' => 'nns',
'application/vnd.noblenet-web' => 'nnw',
'application/vnd.nokia.n-gage.data' => 'ngdat',
'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
'application/vnd.nokia.radio-preset' => 'rpst',
'application/vnd.nokia.radio-presets' => 'rpss',
'application/vnd.novadigm.edm' => 'edm',
'application/vnd.novadigm.edx' => 'edx',
'application/vnd.novadigm.ext' => 'ext',
'application/vnd.oasis.opendocument.chart' => 'odc',
'application/vnd.oasis.opendocument.chart-template' => 'otc',
'application/vnd.oasis.opendocument.database' => 'odb',
'application/vnd.oasis.opendocument.formula' => 'odf',
'application/vnd.oasis.opendocument.formula-template' => 'odft',
'application/vnd.oasis.opendocument.graphics' => 'odg',
'application/vnd.oasis.opendocument.graphics-template' => 'otg',
'application/vnd.oasis.opendocument.image' => 'odi',
'application/vnd.oasis.opendocument.image-template' => 'oti',
'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.text' => 'odt',
'application/vnd.oasis.opendocument.text-master' => 'odm',
'application/vnd.oasis.opendocument.text-template' => 'ott',
'application/vnd.oasis.opendocument.text-web' => 'oth',
'application/vnd.olpc-sugar' => 'xo',
'application/vnd.oma.dd2+xml' => 'dd2',
'application/vnd.openofficeorg.extension' => 'oxt',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
'application/vnd.osgeo.mapguide.package' => 'mgp',
'application/vnd.osgi.dp' => 'dp',
'application/vnd.osgi.subsystem' => 'esa',
'application/vnd.palm' => 'pdb',
'application/vnd.pawaafile' => 'paw',
'application/vnd.pg.format' => 'str',
'application/vnd.pg.osasli' => 'ei6',
'application/vnd.picsel' => 'efif',
'application/vnd.pmi.widget' => 'wg',
'application/vnd.pocketlearn' => 'plf',
'application/vnd.powerbuilder6' => 'pbd',
'application/vnd.previewsystems.box' => 'box',
'application/vnd.proteus.magazine' => 'mgz',
'application/vnd.publishare-delta-tree' => 'qps',
'application/vnd.pvi.ptid1' => 'ptid',
'application/vnd.quark.quarkxpress' => 'qxd',
'application/vnd.realvnc.bed' => 'bed',
'application/vnd.recordare.musicxml' => 'mxl',
'application/vnd.recordare.musicxml+xml' => 'musicxml',
'application/vnd.rig.cryptonote' => 'cryptonote',
'application/vnd.rim.cod' => 'cod',
'application/vnd.rn-realmedia' => 'rm',
'application/vnd.rn-realmedia-vbr' => 'rmvb',
'application/vnd.route66.link66+xml' => 'link66',
'application/vnd.sailingtracker.track' => 'st',
'application/vnd.seemail' => 'see',
'application/vnd.sema' => 'sema',
'application/vnd.semd' => 'semd',
'application/vnd.semf' => 'semf',
'application/vnd.shana.informed.formdata' => 'ifm',
'application/vnd.shana.informed.formtemplate' => 'itp',
'application/vnd.shana.informed.interchange' => 'iif',
'application/vnd.shana.informed.package' => 'ipk',
'application/vnd.simtech-mindmapper' => 'twd',
'application/vnd.smaf' => 'mmf',
'application/vnd.smart.teacher' => 'teacher',
'application/vnd.solent.sdkm+xml' => 'sdkm',
'application/vnd.spotfire.dxp' => 'dxp',
'application/vnd.spotfire.sfs' => 'sfs',
'application/vnd.stardivision.calc' => 'sdc',
'application/vnd.stardivision.draw' => 'sda',
'application/vnd.stardivision.impress' => 'sdd',
'application/vnd.stardivision.math' => 'smf',
'application/vnd.stardivision.writer' => 'sdw',
'application/vnd.stardivision.writer-global' => 'sgl',
'application/vnd.stepmania.package' => 'smzip',
'application/vnd.stepmania.stepchart' => 'sm',
'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.svd' => 'svd',
'application/vnd.symbian.install' => 'sis',
'application/vnd.syncml+xml' => 'xsm',
'application/vnd.syncml.dm+wbxml' => 'bdm',
'application/vnd.syncml.dm+xml' => 'xdm',
'application/vnd.tao.intent-module-archive' => 'tao',
'application/vnd.tcpdump.pcap' => 'pcap',
'application/vnd.tmobile-livetv' => 'tmo',
'application/vnd.trid.tpt' => 'tpt',
'application/vnd.triscape.mxs' => 'mxs',
'application/vnd.trueapp' => 'tra',
'application/vnd.ufdl' => 'ufd',
'application/vnd.uiq.theme' => 'utz',
'application/vnd.umajin' => 'umj',
'application/vnd.unity' => 'unityweb',
'application/vnd.uoml+xml' => 'uoml',
'application/vnd.vcx' => 'vcx',
'application/vnd.visio' => 'vsd',
'application/vnd.visionary' => 'vis',
'application/vnd.vsf' => 'vsf',
'application/vnd.wap.wbxml' => 'wbxml',
'application/vnd.wap.wmlc' => 'wmlc',
'application/vnd.wap.wmlscriptc' => 'wmlsc',
'application/vnd.webturbo' => 'wtb',
'application/vnd.wolfram.player' => 'nbp',
'application/vnd.wordperfect' => 'wpd',
'application/vnd.wqd' => 'wqd',
'application/vnd.wt.stf' => 'stf',
'application/vnd.xara' => 'xar',
'application/vnd.xfdl' => 'xfdl',
'application/vnd.yamaha.hv-dic' => 'hvd',
'application/vnd.yamaha.hv-script' => 'hvs',
'application/vnd.yamaha.hv-voice' => 'hvp',
'application/vnd.yamaha.openscoreformat' => 'osf',
'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
'application/vnd.yamaha.smaf-audio' => 'saf',
'application/vnd.yamaha.smaf-phrase' => 'spf',
'application/vnd.yellowriver-custom-menu' => 'cmp',
'application/vnd.zul' => 'zir',
'application/vnd.zzazz.deck+xml' => 'zaz',
'application/voicexml+xml' => 'vxml',
'application/widget' => 'wgt',
'application/winhlp' => 'hlp',
'application/wsdl+xml' => 'wsdl',
'application/wspolicy+xml' => 'wspolicy',
'application/x-7z-compressed' => '7z',
'application/x-abiword' => 'abw',
'application/x-ace-compressed' => 'ace',
'application/x-apple-diskimage' => 'dmg',
'application/x-authorware-bin' => 'aab',
'application/x-authorware-map' => 'aam',
'application/x-authorware-seg' => 'aas',
'application/x-bcpio' => 'bcpio',
'application/x-bittorrent' => 'torrent',
'application/x-blorb' => 'blb',
'application/x-bzip' => 'bz',
'application/x-bzip2' => 'bz2',
'application/x-cbr' => 'cbr',
'application/x-cdlink' => 'vcd',
'application/x-cfs-compressed' => 'cfs',
'application/x-chat' => 'chat',
'application/x-chess-pgn' => 'pgn',
'application/x-conference' => 'nsc',
'application/x-cpio' => 'cpio',
'application/x-csh' => 'csh',
'application/x-debian-package' => 'deb',
'application/x-dgc-compressed' => 'dgc',
'application/x-director' => 'dir',
'application/x-doom' => 'wad',
'application/x-dtbncx+xml' => 'ncx',
'application/x-dtbook+xml' => 'dtb',
'application/x-dtbresource+xml' => 'res',
'application/x-dvi' => 'dvi',
'application/x-envoy' => 'evy',
'application/x-eva' => 'eva',
'application/x-font-bdf' => 'bdf',
'application/x-font-ghostscript' => 'gsf',
'application/x-font-linux-psf' => 'psf',
'application/x-font-otf' => 'otf',
'application/x-font-pcf' => 'pcf',
'application/x-font-snf' => 'snf',
'application/x-font-ttf' => 'ttf',
'application/x-font-type1' => 'pfa',
'application/x-font-woff' => 'woff',
'application/x-freearc' => 'arc',
'application/x-futuresplash' => 'spl',
'application/x-gca-compressed' => 'gca',
'application/x-glulx' => 'ulx',
'application/x-gnumeric' => 'gnumeric',
'application/x-gramps-xml' => 'gramps',
'application/x-gtar' => 'gtar',
'application/x-hdf' => 'hdf',
'application/x-install-instructions' => 'install',
'application/x-iso9660-image' => 'iso',
'application/x-java-jnlp-file' => 'jnlp',
'application/x-latex' => 'latex',
'application/x-lzh-compressed' => 'lzh',
'application/x-mie' => 'mie',
'application/x-mobipocket-ebook' => 'prc',
'application/x-ms-application' => 'application',
'application/x-ms-shortcut' => 'lnk',
'application/x-ms-wmd' => 'wmd',
'application/x-ms-wmz' => 'wmz',
'application/x-ms-xbap' => 'xbap',
'application/x-msaccess' => 'mdb',
'application/x-msbinder' => 'obd',
'application/x-mscardfile' => 'crd',
'application/x-msclip' => 'clp',
'application/x-msdownload' => 'exe',
'application/x-msmediaview' => 'mvb',
'application/x-msmetafile' => 'wmf',
'application/x-msmoney' => 'mny',
'application/x-mspublisher' => 'pub',
'application/x-msschedule' => 'scd',
'application/x-msterminal' => 'trm',
'application/x-mswrite' => 'wri',
'application/x-netcdf' => 'nc',
'application/x-nzb' => 'nzb',
'application/x-pkcs12' => 'p12',
'application/x-pkcs7-certificates' => 'p7b',
'application/x-pkcs7-certreqresp' => 'p7r',
'application/x-rar-compressed' => 'rar',
'application/x-rar' => 'rar',
'application/x-research-info-systems' => 'ris',
'application/x-sh' => 'sh',
'application/x-shar' => 'shar',
'application/x-shockwave-flash' => 'swf',
'application/x-silverlight-app' => 'xap',
'application/x-sql' => 'sql',
'application/x-stuffit' => 'sit',
'application/x-stuffitx' => 'sitx',
'application/x-subrip' => 'srt',
'application/x-sv4cpio' => 'sv4cpio',
'application/x-sv4crc' => 'sv4crc',
'application/x-t3vm-image' => 't3',
'application/x-tads' => 'gam',
'application/x-tar' => 'tar',
'application/x-tcl' => 'tcl',
'application/x-tex' => 'tex',
'application/x-tex-tfm' => 'tfm',
'application/x-texinfo' => 'texinfo',
'application/x-tgif' => 'obj',
'application/x-ustar' => 'ustar',
'application/x-wais-source' => 'src',
'application/x-x509-ca-cert' => 'der',
'application/x-xfig' => 'fig',
'application/x-xliff+xml' => 'xlf',
'application/x-xpinstall' => 'xpi',
'application/x-xz' => 'xz',
'application/x-zmachine' => 'z1',
'application/xaml+xml' => 'xaml',
'application/xcap-diff+xml' => 'xdf',
'application/xenc+xml' => 'xenc',
'application/xhtml+xml' => 'xhtml',
'application/xml' => 'xml',
'application/xml-dtd' => 'dtd',
'application/xop+xml' => 'xop',
'application/xproc+xml' => 'xpl',
'application/xslt+xml' => 'xslt',
'application/xspf+xml' => 'xspf',
'application/xv+xml' => 'mxml',
'application/yang' => 'yang',
'application/yin+xml' => 'yin',
'application/zip' => 'zip',
'audio/adpcm' => 'adp',
'audio/basic' => 'au',
'audio/midi' => 'mid',
'audio/mp4' => 'mp4a',
'audio/mpeg' => 'mpga',
'audio/ogg' => 'oga',
'audio/s3m' => 's3m',
'audio/silk' => 'sil',
'audio/vnd.dece.audio' => 'uva',
'audio/vnd.digital-winds' => 'eol',
'audio/vnd.dra' => 'dra',
'audio/vnd.dts' => 'dts',
'audio/vnd.dts.hd' => 'dtshd',
'audio/vnd.lucent.voice' => 'lvp',
'audio/vnd.ms-playready.media.pya' => 'pya',
'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
'audio/vnd.rip' => 'rip',
'audio/webm' => 'weba',
'audio/x-aac' => 'aac',
'audio/x-aiff' => 'aif',
'audio/x-caf' => 'caf',
'audio/x-flac' => 'flac',
'audio/x-matroska' => 'mka',
'audio/x-mpegurl' => 'm3u',
'audio/x-ms-wax' => 'wax',
'audio/x-ms-wma' => 'wma',
'audio/x-pn-realaudio' => 'ram',
'audio/x-pn-realaudio-plugin' => 'rmp',
'audio/x-wav' => 'wav',
'audio/xm' => 'xm',
'chemical/x-cdx' => 'cdx',
'chemical/x-cif' => 'cif',
'chemical/x-cmdf' => 'cmdf',
'chemical/x-cml' => 'cml',
'chemical/x-csml' => 'csml',
'chemical/x-xyz' => 'xyz',
'image/bmp' => 'bmp',
'image/cgm' => 'cgm',
'image/g3fax' => 'g3',
'image/gif' => 'gif',
'image/ief' => 'ief',
'image/jpeg' => 'jpeg',
'image/ktx' => 'ktx',
'image/png' => 'png',
'image/prs.btif' => 'btif',
'image/sgi' => 'sgi',
'image/svg+xml' => 'svg',
'image/tiff' => 'tiff',
'image/vnd.adobe.photoshop' => 'psd',
'image/vnd.dece.graphic' => 'uvi',
'image/vnd.dvb.subtitle' => 'sub',
'image/vnd.djvu' => 'djvu',
'image/vnd.dwg' => 'dwg',
'image/vnd.dxf' => 'dxf',
'image/vnd.fastbidsheet' => 'fbs',
'image/vnd.fpx' => 'fpx',
'image/vnd.fst' => 'fst',
'image/vnd.fujixerox.edmics-mmr' => 'mmr',
'image/vnd.fujixerox.edmics-rlc' => 'rlc',
'image/vnd.ms-modi' => 'mdi',
'image/vnd.ms-photo' => 'wdp',
'image/vnd.net-fpx' => 'npx',
'image/vnd.wap.wbmp' => 'wbmp',
'image/vnd.xiff' => 'xif',
'image/webp' => 'webp',
'image/x-3ds' => '3ds',
'image/x-cmu-raster' => 'ras',
'image/x-cmx' => 'cmx',
'image/x-freehand' => 'fh',
'image/x-icon' => 'ico',
'image/x-mrsid-image' => 'sid',
'image/x-pcx' => 'pcx',
'image/x-pict' => 'pic',
'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-tga' => 'tga',
'image/x-xbitmap' => 'xbm',
'image/x-xpixmap' => 'xpm',
'image/x-xwindowdump' => 'xwd',
'message/rfc822' => 'eml',
'model/iges' => 'igs',
'model/mesh' => 'msh',
'model/vnd.collada+xml' => 'dae',
'model/vnd.dwf' => 'dwf',
'model/vnd.gdl' => 'gdl',
'model/vnd.gtw' => 'gtw',
'model/vnd.mts' => 'mts',
'model/vnd.vtu' => 'vtu',
'model/vrml' => 'wrl',
'model/x3d+binary' => 'x3db',
'model/x3d+vrml' => 'x3dv',
'model/x3d+xml' => 'x3d',
'text/cache-manifest' => 'appcache',
'text/calendar' => 'ics',
'text/css' => 'css',
'text/csv' => 'csv',
'text/html' => 'html',
'text/n3' => 'n3',
'text/plain' => 'txt',
'text/prs.lines.tag' => 'dsc',
'text/richtext' => 'rtx',
'text/sgml' => 'sgml',
'text/tab-separated-values' => 'tsv',
'text/troff' => 't',
'text/turtle' => 'ttl',
'text/uri-list' => 'uri',
'text/vcard' => 'vcard',
'text/vnd.curl' => 'curl',
'text/vnd.curl.dcurl' => 'dcurl',
'text/vnd.curl.scurl' => 'scurl',
'text/vnd.curl.mcurl' => 'mcurl',
'text/vnd.dvb.subtitle' => 'sub',
'text/vnd.fly' => 'fly',
'text/vnd.fmi.flexstor' => 'flx',
'text/vnd.graphviz' => 'gv',
'text/vnd.in3d.3dml' => '3dml',
'text/vnd.in3d.spot' => 'spot',
'text/vnd.sun.j2me.app-descriptor' => 'jad',
'text/vnd.wap.wml' => 'wml',
'text/vnd.wap.wmlscript' => 'wmls',
'text/x-asm' => 's',
'text/x-c' => 'c',
'text/x-fortran' => 'f',
'text/x-pascal' => 'p',
'text/x-java-source' => 'java',
'text/x-opml' => 'opml',
'text/x-nfo' => 'nfo',
'text/x-setext' => 'etx',
'text/x-sfv' => 'sfv',
'text/x-uuencode' => 'uu',
'text/x-vcalendar' => 'vcs',
'text/x-vcard' => 'vcf',
'video/3gpp' => '3gp',
'video/3gpp2' => '3g2',
'video/h261' => 'h261',
'video/h263' => 'h263',
'video/h264' => 'h264',
'video/jpeg' => 'jpgv',
'video/jpm' => 'jpm',
'video/mj2' => 'mj2',
'video/mp4' => 'mp4',
'video/mpeg' => 'mpeg',
'video/ogg' => 'ogv',
'video/quicktime' => 'qt',
'video/vnd.dece.hd' => 'uvh',
'video/vnd.dece.mobile' => 'uvm',
'video/vnd.dece.pd' => 'uvp',
'video/vnd.dece.sd' => 'uvs',
'video/vnd.dece.video' => 'uvv',
'video/vnd.dvb.file' => 'dvb',
'video/vnd.fvt' => 'fvt',
'video/vnd.mpegurl' => 'mxu',
'video/vnd.ms-playready.media.pyv' => 'pyv',
'video/vnd.uvvu.mp4' => 'uvu',
'video/vnd.vivo' => 'viv',
'video/webm' => 'webm',
'video/x-f4v' => 'f4v',
'video/x-fli' => 'fli',
'video/x-flv' => 'flv',
'video/x-m4v' => 'm4v',
'video/x-matroska' => 'mkv',
'video/x-mng' => 'mng',
'video/x-ms-asf' => 'asf',
'video/x-ms-vob' => 'vob',
'video/x-ms-wm' => 'wm',
'video/x-ms-wmv' => 'wmv',
'video/x-ms-wmx' => 'wmx',
'video/x-ms-wvx' => 'wvx',
'video/x-msvideo' => 'avi',
'video/x-sgi-movie' => 'movie',
'video/x-smv' => 'smv',
'x-conference/x-cooltalk' => 'ice',
);
/**
* {@inheritdoc}
*/
public function guess($mimeType)
{
return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null;
}
}

View File

@@ -0,0 +1,132 @@
<?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. Custom guessers are always called before any default ones.
*
* $guesser = MimeTypeGuesser::getInstance();
* $guesser->register(new MyCustomMimeTypeGuesser());
*
* If you want to change the order of the default guessers, just re-register your
* preferred one as a custom one. The last registered guesser is preferred over
* previously registered ones.
*
* Re-registering a built-in guesser also allows you to configure it:
*
* $guesser = MimeTypeGuesser::getInstance();
* $guesser->register(new FileinfoMimeTypeGuesser('/path/to/magic/file'));
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class MimeTypeGuesser implements MimeTypeGuesserInterface
{
/**
* The singleton instance
*
* @var MimeTypeGuesser
*/
private static $instance = null;
/**
* All registered MimeTypeGuesserInterface instances
*
* @var array
*/
protected $guessers = array();
/**
* Returns the singleton instance
*
* @return MimeTypeGuesser
*/
public static 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 (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 \LogicException
* @throws FileNotFoundException
* @throws AccessDeniedException
*/
public function guess($path)
{
if (!is_file($path)) {
throw new FileNotFoundException($path);
}
if (!is_readable($path)) {
throw new AccessDeniedException($path);
}
if (!$this->guessers) {
throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)');
}
foreach ($this->guessers as $guesser) {
if (null !== $mimeType = $guesser->guess($path)) {
return $mimeType;
}
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\File\MimeType;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
/**
* Guesses the mime type of a file
*
* @author Bernhard Schussek <bschussek@gmail.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
*/
public function guess($path);
}

View File

@@ -0,0 +1,305 @@
<?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\ExtensionGuesser;
/**
* A file uploaded through a form.
*
* @author Bernhard Schussek <bschussek@gmail.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 = $this->getName($originalName);
$this->mimeType = $mimeType ?: 'application/octet-stream';
$this->size = $size;
$this->error = $error ?: UPLOAD_ERR_OK;
$this->test = (Boolean) $test;
parent::__construct($path, UPLOAD_ERR_OK === $this->error);
}
/**
* 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 original file extension
*
* It is extracted from the original file name that was uploaded.
* Then is should not be considered as a safe value.
*
* @return string The extension
*/
public function getClientOriginalExtension()
{
return pathinfo($this->originalName, PATHINFO_EXTENSION);
}
/**
* Returns the file mime type.
*
* The client mime type is extracted from the request from which the file
* was uploaded, so it should not be considered as a safe value.
*
* For a trusted mime type, use getMimeType() instead (which guesses the mime
* type based on the file content).
*
* @return string|null The mime type
*
* @see getMimeType
*
* @api
*/
public function getClientMimeType()
{
return $this->mimeType;
}
/**
* Returns the extension based on the client mime type.
*
* If the mime type is unknown, returns null.
*
* This method uses the mime type as guessed by getClientMimeType()
* to guess the file extension. As such, the extension returned
* by this method cannot be trusted.
*
* For a trusted extension, use guessExtension() instead (which guesses
* the extension based on the guessed mime type for the file).
*
* @return string|null The guessed extension or null if it cannot be guessed
*
* @see guessExtension()
* @see getClientMimeType()
*/
public function guessClientExtension()
{
$type = $this->getClientMimeType();
$guesser = ExtensionGuesser::getInstance();
return $guesser->guess($type);
}
/**
* 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 the file has been uploaded with HTTP and no error occurred.
*
* @api
*/
public function isValid()
{
$isOk = $this->error === UPLOAD_ERR_OK;
return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
}
/**
* 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, for any reason, the file could not have been moved
*
* @api
*/
public function move($directory, $name = null)
{
if ($this->isValid()) {
if ($this->test) {
return parent::move($directory, $name);
}
$target = $this->getTargetFile($directory, $name);
if (!@move_uploaded_file($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 & ~umask());
return $target;
}
throw new FileException($this->getErrorMessage($this->getError()));
}
/**
* Returns the maximum size of an uploaded file as configured in php.ini
*
* @return int The maximum size of an uploaded file in bytes
*/
public static function getMaxFilesize()
{
$max = strtolower(ini_get('upload_max_filesize'));
if ('' === $max) {
return PHP_INT_MAX;
}
if (preg_match('#^\+?(0x?)?(.*?)([kmg]?)$#', $max, $match)) {
$shifts = array('' => 0, 'k' => 10, 'm' => 20, 'g' => 30);
$bases = array('' => 10, '0' => 8, '0x' => 16);
return intval($match[2], $bases[$match[1]]) << $shifts[$match[3]];
}
return 0;
}
/**
* Returns an informative upload error message.
*
* @param int $code The error code returned by an upload attempt
*
* @return string The error message regarding the specified error code
*/
private function getErrorMessage($errorCode)
{
static $errors = array(
UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d kb).',
UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
UPLOAD_ERR_EXTENSION => 'File upload was stopped by a php extension.',
);
$maxFilesize = $errorCode === UPLOAD_ERR_INI_SIZE ? self::getMaxFilesize() / 1024 : 0;
$message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.';
return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
}
}

View File

@@ -0,0 +1,155 @@
<?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
{
private static $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);
}
/**
* {@inheritdoc}
*
* @api
*/
public function replace(array $files = array())
{
$this->parameters = array();
$this->add($files);
}
/**
* {@inheritdoc}
*
* @api
*/
public function set($key, $value)
{
if (!is_array($value) && !$value instanceof UploadedFile) {
throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
}
parent::set($key, $this->convertFileInformation($value));
}
/**
* {@inheritdoc}
*
* @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;
}
}

View File

@@ -0,0 +1,325 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation;
/**
* HeaderBag is a container for HTTP headers.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class HeaderBag implements \IteratorAggregate, \Countable
{
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 '';
}
$max = max(array_map('strlen', array_keys($this->headers))) + 1;
$content = '';
ksort($this->headers);
foreach ($this->headers as $name => $values) {
$name = implode('-', array_map('ucfirst', explode('-', $name)));
foreach ($values as $value) {
$content .= sprintf("%-{$max}s %s\r\n", $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 or not (true by default)
*
* @api
*/
public function set($key, $values, $replace = true)
{
$key = strtr(strtolower($key), '_', '-');
$values = array_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 null|\DateTime The parsed DateTime or the default value if the header does not exist
*
* @throws \RuntimeException When the HTTP header is not parseable
*
* @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());
}
/**
* Returns an iterator for headers.
*
* @return \ArrayIterator An \ArrayIterator instance
*/
public function getIterator()
{
return new \ArrayIterator($this->headers);
}
/**
* Returns the number of headers.
*
* @return int The number of headers
*/
public function count()
{
return count($this->headers);
}
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[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true);
}
return $cacheControl;
}
}

View File

@@ -0,0 +1,119 @@
<?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;
/**
* Http utility functions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class IpUtils
{
/**
* This class should not be instantiated
*/
private function __construct() {}
/**
* Validates an IPv4 or IPv6 address.
*
* @param string $requestIp
* @param string|array $ips
*
* @return boolean Whether the IP is valid
*/
public static function checkIp($requestIp, $ips)
{
if (!is_array($ips)) {
$ips = array($ips);
}
$method = false !== strpos($requestIp, ':') ? 'checkIp6': 'checkIp4';
foreach ($ips as $ip) {
if (self::$method($requestIp, $ip)) {
return true;
}
}
return false;
}
/**
* Validates an IPv4 address.
*
* @param string $requestIp
* @param string $ip
*
* @return boolean Whether the IP is valid
*/
public static function checkIp4($requestIp, $ip)
{
if (false !== strpos($ip, '/')) {
list($address, $netmask) = explode('/', $ip, 2);
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);
}
/**
* Validates an IPv6 address.
*
* @author David Soria Parra <dsp at php dot net>
* @see https://github.com/dsp/v6tools
*
* @param string $requestIp
* @param string $ip
*
* @return boolean Whether the IP is valid
*
* @throws \RuntimeException When IPV6 support is not enabled
*/
public static function checkIp6($requestIp, $ip)
{
if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
}
if (false !== strpos($ip, '/')) {
list($address, $netmask) = explode('/', $ip, 2);
if ($netmask < 1 || $netmask > 128) {
return false;
}
} else {
$address = $ip;
$netmask = 128;
}
$bytesAddr = unpack("n*", inet_pton($address));
$bytesTest = unpack("n*", inet_pton($requestIp));
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) {
$left = $netmask - 16 * ($i-1);
$left = ($left <= 16) ? $left : 16;
$mask = ~(0xffff >> $left) & 0xffff;
if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,119 @@
<?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 in JSON format.
*
* Note that this class does not force the returned JSON content to be an
* object. It is however recommended that you do return an object as it
* protects yourself against XSSI and JSON-JavaScript Hijacking.
*
* @see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class JsonResponse extends Response
{
protected $data;
protected $callback;
/**
* Constructor.
*
* @param mixed $data The response data
* @param integer $status The response status code
* @param array $headers An array of response headers
*/
public function __construct($data = null, $status = 200, $headers = array())
{
parent::__construct('', $status, $headers);
if (null === $data) {
$data = new \ArrayObject();
}
$this->setData($data);
}
/**
* {@inheritDoc}
*/
public static function create($data = null, $status = 200, $headers = array())
{
return new static($data, $status, $headers);
}
/**
* Sets the JSONP callback.
*
* @param string $callback
*
* @return JsonResponse
*
* @throws \InvalidArgumentException
*/
public function setCallback($callback = null)
{
if (null !== $callback) {
// taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/
$pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u';
$parts = explode('.', $callback);
foreach ($parts as $part) {
if (!preg_match($pattern, $part)) {
throw new \InvalidArgumentException('The callback name is not valid.');
}
}
}
$this->callback = $callback;
return $this->update();
}
/**
* Sets the data to be sent as json.
*
* @param mixed $data
*
* @return JsonResponse
*/
public function setData($data = array())
{
// Encode <, >, ', &, and " for RFC4627-compliant JSON, which may also be embedded into HTML.
$this->data = json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
return $this->update();
}
/**
* Updates the content and headers according to the json data and callback.
*
* @return JsonResponse
*/
protected function update()
{
if (null !== $this->callback) {
// Not using application/javascript for compatibility reasons with older browsers.
$this->headers->set('Content-Type', 'text/javascript');
return $this->setContent(sprintf('%s(%s);', $this->callback, $this->data));
}
// Only set the header when there is none or when it equals 'text/javascript' (from a previous update with callback)
// in order to not overwrite a custom definition.
if (!$this->headers->has('Content-Type') || 'text/javascript' === $this->headers->get('Content-Type')) {
$this->headers->set('Content-Type', 'application/json');
}
return $this->setContent($this->data);
}
}

View File

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

View File

@@ -0,0 +1,305 @@
<?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 implements \IteratorAggregate, \Countable
{
/**
* Parameter storage.
*
* @var array
*/
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 if the parameter key does not exist
* @param boolean $deep If true, a path like foo[bar] will find deeper items
*
* @return mixed
*
* @throws \InvalidArgumentException
*
* @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 = '';
} elseif (']' === $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 if the parameter key does not exist
* @param boolean $deep If true, a path like foo[bar] will find deeper items
*
* @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 if the parameter key does not exist
* @param boolean $deep If true, a path like foo[bar] will find deeper items
*
* @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 if the parameter key does not exist
* @param boolean $deep If true, a path like foo[bar] will find deeper items
*
* @return string The filtered value
*
* @api
*/
public function getDigits($key, $default = '', $deep = false)
{
// we need to remove - and + because they're allowed in the filter
return str_replace(array('-', '+'), '', $this->filter($key, $default, $deep, FILTER_SANITIZE_NUMBER_INT));
}
/**
* Returns the parameter value converted to integer.
*
* @param string $key The parameter key
* @param mixed $default The default value if the parameter key does not exist
* @param boolean $deep If true, a path like foo[bar] will find deeper items
*
* @return integer The filtered value
*
* @api
*/
public function getInt($key, $default = 0, $deep = false)
{
return (int) $this->get($key, $default, $deep);
}
/**
* Filter key.
*
* @param string $key Key.
* @param mixed $default Default = null.
* @param boolean $deep Default = false.
* @param integer $filter FILTER_* constant.
* @param mixed $options Filter options.
*
* @see http://php.net/manual/en/function.filter-var.php
*
* @return mixed
*/
public function filter($key, $default = null, $deep = false, $filter=FILTER_DEFAULT, $options=array())
{
$value = $this->get($key, $default, $deep);
// Always turn $options into an array - this allows filter_var option shortcuts.
if (!is_array($options) && $options) {
$options = array('flags' => $options);
}
// Add a convenience check for arrays.
if (is_array($value) && !isset($options['flags'])) {
$options['flags'] = FILTER_REQUIRE_ARRAY;
}
return filter_var($value, $filter, $options);
}
/**
* Returns an iterator for parameters.
*
* @return \ArrayIterator An \ArrayIterator instance
*/
public function getIterator()
{
return new \ArrayIterator($this->parameters);
}
/**
* Returns the number of parameters.
*
* @return int The number of parameters
*/
public function count()
{
return count($this->parameters);
}
}

View File

@@ -0,0 +1,48 @@
HttpFoundation Component
========================
HttpFoundation defines an object-oriented layer for the HTTP specification.
It provides an abstraction for requests, responses, uploaded files, cookies,
sessions, ...
In this example, we get a Request object from the current PHP global
variables:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
echo $request->getPathInfo();
You can also create a Request directly -- that's interesting for unit testing:
$request = Request::create('/?foo=bar', 'GET');
echo $request->getPathInfo();
And here is how to create and send a Response:
$response = new Response('Not Found', 404, array('Content-Type' => 'text/plain'));
$response->send();
The Request and the Response classes have many other methods that implement
the HTTP specification.
Loading
-------
If you are not using Composer but are using PHP 5.3.x, you must add the following to your autoloader:
// SessionHandlerInterface
if (!interface_exists('SessionHandlerInterface')) {
$loader->registerPrefixFallback(__DIR__.'/../vendor/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs');
}
Resources
---------
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/HttpFoundation/
$ composer.phar install --dev
$ phpunit

View File

@@ -0,0 +1,106 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation;
/**
* RedirectResponse represents an HTTP response doing a redirect.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class RedirectResponse extends Response
{
protected $targetUrl;
/**
* 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)
* @param array $headers The headers (Location is always set to the given url)
*
* @throws \InvalidArgumentException
*
* @see http://tools.ietf.org/html/rfc2616#section-10.3
*
* @api
*/
public function __construct($url, $status = 302, $headers = array())
{
if (empty($url)) {
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
}
parent::__construct('', $status, $headers);
$this->setTargetUrl($url);
if (!$this->isRedirect()) {
throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
}
}
/**
* {@inheritDoc}
*/
public static function create($url = '', $status = 302, $headers = array())
{
return new static($url, $status, $headers);
}
/**
* Returns the target URL.
*
* @return string target URL
*/
public function getTargetUrl()
{
return $this->targetUrl;
}
/**
* Sets the redirect target of this response.
*
* @param string $url The URL to redirect to
*
* @return RedirectResponse The current response.
*
* @throws \InvalidArgumentException
*/
public function setTargetUrl($url)
{
if (empty($url)) {
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
}
$this->targetUrl = $url;
$this->setContent(
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')));
$this->headers->set('Location', $url);
return $this;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,160 @@
<?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
{
/**
* @var string
*/
private $path;
/**
* @var string
*/
private $host;
/**
* @var array
*/
private $methods = array();
/**
* @var string
*/
private $ips = array();
/**
* @var array
*/
private $attributes = array();
/**
* @param string|null $path
* @param string|null $host
* @param string|string[]|null $methods
* @param string|string[]|null $ips
* @param array $attributes
*/
public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array())
{
$this->matchPath($path);
$this->matchHost($host);
$this->matchMethod($methods);
$this->matchIps($ips);
foreach ($attributes as $k => $v) {
$this->matchAttribute($k, $v);
}
}
/**
* 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->matchIps($ip);
}
/**
* Adds a check for the client IP.
*
* @param string|string[] $ips A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
*/
public function matchIps($ips)
{
$this->ips = (array) $ips;
}
/**
* Adds a check for the HTTP method.
*
* @param string|string[]|null $method An HTTP method or an array of HTTP methods
*/
public function matchMethod($method)
{
$this->methods = array_map('strtoupper', (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 ($this->methods && !in_array($request->getMethod(), $this->methods)) {
return false;
}
foreach ($this->attributes as $key => $pattern) {
if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) {
return false;
}
}
if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getPathInfo()))) {
return false;
}
if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getHost())) {
return false;
}
if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
return true;
}
// Note to future implementors: add additional checks above the
// foreach above or else your check might not be run!
return count($this->ips) === 0;
}
}

View File

@@ -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
*/
public function matches(Request $request);
}

View File

@@ -0,0 +1,100 @@
<?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.
*/
/**
* SessionHandlerInterface
*
* Provides forward compatibility with PHP 5.4
*
* Extensive documentation can be found at php.net, see links:
*
* @see http://php.net/sessionhandlerinterface
* @see http://php.net/session.customhandler
* @see http://php.net/session-set-save-handler
*
* @author Drak <drak@zikula.org>
*/
interface SessionHandlerInterface
{
/**
* Open session.
*
* @see http://php.net/sessionhandlerinterface.open
*
* @param string $savePath Save path.
* @param string $sessionName Session Name.
*
* @throws \RuntimeException If something goes wrong starting the session.
*
* @return boolean
*/
public function open($savePath, $sessionName);
/**
* Close session.
*
* @see http://php.net/sessionhandlerinterface.close
*
* @return boolean
*/
public function close();
/**
* Read session.
*
* @param string $sessionId
*
* @see http://php.net/sessionhandlerinterface.read
*
* @throws \RuntimeException On fatal error but not "record not found".
*
* @return string String as stored in persistent storage or empty string in all other cases.
*/
public function read($sessionId);
/**
* Commit session to storage.
*
* @see http://php.net/sessionhandlerinterface.write
*
* @param string $sessionId Session ID.
* @param string $data Session serialized data to save.
*
* @return boolean
*/
public function write($sessionId, $data);
/**
* Destroys this session.
*
* @see http://php.net/sessionhandlerinterface.destroy
*
* @param string $sessionId Session ID.
*
* @throws \RuntimeException On fatal error.
*
* @return boolean
*/
public function destroy($sessionId);
/**
* Garbage collection for storage.
*
* @see http://php.net/sessionhandlerinterface.gc
*
* @param integer $lifetime Max lifetime in seconds to keep sessions stored.
*
* @throws \RuntimeException On fatal error.
*
* @return boolean
*/
public function gc($lifetime);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,319 @@
<?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';
const DISPOSITION_ATTACHMENT = 'attachment';
const DISPOSITION_INLINE = 'inline';
/**
* @var array
*/
protected $computedCacheControl = array();
/**
* @var array
*/
protected $cookies = array();
/**
* @var array
*/
protected $headerNames = 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";
}
ksort($this->headerNames);
return parent::__toString().$cookies;
}
/**
* Returns the headers, with original capitalizations.
*
* @return array An array of headers
*/
public function allPreserveCase()
{
return array_combine($this->headerNames, $this->headers);
}
/**
* {@inheritdoc}
*
* @api
*/
public function replace(array $headers = array())
{
$this->headerNames = 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);
$uniqueKey = strtr(strtolower($key), '_', '-');
$this->headerNames[$uniqueKey] = $key;
// ensure the cache-control header has sensible defaults
if (in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'))) {
$computed = $this->computeCacheControlValue();
$this->headers['cache-control'] = array($computed);
$this->headerNames['cache-control'] = 'Cache-Control';
$this->computedCacheControl = $this->parseCacheControl($computed);
}
}
/**
* {@inheritdoc}
*
* @api
*/
public function remove($key)
{
parent::remove($key);
$uniqueKey = strtr(strtolower($key), '_', '-');
unset($this->headerNames[$uniqueKey]);
if ('cache-control' === $uniqueKey) {
$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
*
* @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
*
* @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
*
* @api
*/
public function clearCookie($name, $path = '/', $domain = null)
{
$this->setCookie(new Cookie($name, null, 1, $path, $domain));
}
/**
* Generates a HTTP Content-Disposition field-value.
*
* @param string $disposition One of "inline" or "attachment"
* @param string $filename A unicode string
* @param string $filenameFallback A string containing only ASCII characters that
* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @return string A string suitable for use as a Content-Disposition field-value.
*
* @throws \InvalidArgumentException
* @see RFC 6266
*/
public function makeDisposition($disposition, $filename, $filenameFallback = '')
{
if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
}
if ('' == $filenameFallback) {
$filenameFallback = $filename;
}
// filenameFallback is not ASCII.
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
}
// percent characters aren't safe in fallback.
if (false !== strpos($filenameFallback, '%')) {
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
}
// path separators aren't allowed in either.
if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
}
$output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback));
if ($filename !== $filenameFallback) {
$output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
}
return $output;
}
/**
* 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;
}
}

View File

@@ -0,0 +1,83 @@
<?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>
* @author Robert Kiss <kepten@gmail.com>
*/
class ServerBag extends ParameterBag
{
/**
* Gets the HTTP headers.
*
* @return array
*/
public function getHeaders()
{
$headers = array();
$contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
foreach ($this->parameters as $key => $value) {
if (0 === strpos($key, 'HTTP_')) {
$headers[substr($key, 5)] = $value;
}
// CONTENT_* are not prefixed with HTTP_
elseif (isset($contentHeaders[$key])) {
$headers[$key] = $value;
}
}
if (isset($this->parameters['PHP_AUTH_USER'])) {
$headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER'];
$headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
} else {
/*
* php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
* For this workaround to work, add these lines to your .htaccess file:
* RewriteCond %{HTTP:Authorization} ^(.+)$
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
*
* A sample .htaccess file:
* RewriteEngine On
* RewriteCond %{HTTP:Authorization} ^(.+)$
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
* RewriteCond %{REQUEST_FILENAME} !-f
* RewriteRule ^(.*)$ app.php [QSA,L]
*/
$authorizationHeader = null;
if (isset($this->parameters['HTTP_AUTHORIZATION'])) {
$authorizationHeader = $this->parameters['HTTP_AUTHORIZATION'];
} elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) {
$authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION'];
}
// Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic
if ((null !== $authorizationHeader) && (0 === stripos($authorizationHeader, 'basic'))) {
$exploded = explode(':', base64_decode(substr($authorizationHeader, 6)));
if (count($exploded) == 2) {
list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
}
}
}
// PHP_AUTH_USER/PHP_AUTH_PW
if (isset($headers['PHP_AUTH_USER'])) {
$headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
}
return $headers;
}
}

View File

@@ -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\Session\Attribute;
/**
* This class relates to session attribute storage
*/
class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable
{
private $name = 'attributes';
/**
* @var string
*/
private $storageKey;
/**
* @var array
*/
protected $attributes = array();
/**
* Constructor.
*
* @param string $storageKey The key used to store attributes in the session.
*/
public function __construct($storageKey = '_sf2_attributes')
{
$this->storageKey = $storageKey;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
/**
* {@inheritdoc}
*/
public function initialize(array &$attributes)
{
$this->attributes = &$attributes;
}
/**
* {@inheritdoc}
*/
public function getStorageKey()
{
return $this->storageKey;
}
/**
* {@inheritdoc}
*/
public function has($name)
{
return array_key_exists($name, $this->attributes);
}
/**
* {@inheritdoc}
*/
public function get($name, $default = null)
{
return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}
/**
* {@inheritdoc}
*/
public function set($name, $value)
{
$this->attributes[$name] = $value;
}
/**
* {@inheritdoc}
*/
public function all()
{
return $this->attributes;
}
/**
* {@inheritdoc}
*/
public function replace(array $attributes)
{
$this->attributes = array();
foreach ($attributes as $key => $value) {
$this->set($key, $value);
}
}
/**
* {@inheritdoc}
*/
public function remove($name)
{
$retval = null;
if (array_key_exists($name, $this->attributes)) {
$retval = $this->attributes[$name];
unset($this->attributes[$name]);
}
return $retval;
}
/**
* {@inheritdoc}
*/
public function clear()
{
$return = $this->attributes;
$this->attributes = array();
return $return;
}
/**
* Returns an iterator for attributes.
*
* @return \ArrayIterator An \ArrayIterator instance
*/
public function getIterator()
{
return new \ArrayIterator($this->attributes);
}
/**
* Returns the number of attributes.
*
* @return int The number of attributes
*/
public function count()
{
return count($this->attributes);
}
}

View File

@@ -0,0 +1,72 @@
<?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\Session\Attribute;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
/**
* Attributes store.
*
* @author Drak <drak@zikula.org>
*/
interface AttributeBagInterface extends SessionBagInterface
{
/**
* Checks if an attribute is defined.
*
* @param string $name The attribute name
*
* @return Boolean true if the attribute is defined, false otherwise
*/
public function has($name);
/**
* Returns an attribute.
*
* @param string $name The attribute name
* @param mixed $default The default value if not found.
*
* @return mixed
*/
public function get($name, $default = null);
/**
* Sets an attribute.
*
* @param string $name
* @param mixed $value
*/
public function set($name, $value);
/**
* Returns attributes.
*
* @return array Attributes
*/
public function all();
/**
* Sets attributes.
*
* @param array $attributes Attributes
*/
public function replace(array $attributes);
/**
* Removes an attribute.
*
* @param string $name
*
* @return mixed The removed value
*/
public function remove($name);
}

View File

@@ -0,0 +1,158 @@
<?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\Session\Attribute;
/**
* This class provides structured storage of session attributes using
* a name spacing character in the key.
*
* @author Drak <drak@zikula.org>
*/
class NamespacedAttributeBag extends AttributeBag
{
/**
* Namespace character.
*
* @var string
*/
private $namespaceCharacter;
/**
* Constructor.
*
* @param string $storageKey Session storage key.
* @param string $namespaceCharacter Namespace character to use in keys.
*/
public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/')
{
$this->namespaceCharacter = $namespaceCharacter;
parent::__construct($storageKey);
}
/**
* {@inheritdoc}
*/
public function has($name)
{
$attributes = $this->resolveAttributePath($name);
$name = $this->resolveKey($name);
if (null === $attributes) {
return false;
}
return array_key_exists($name, $attributes);
}
/**
* {@inheritdoc}
*/
public function get($name, $default = null)
{
$attributes = $this->resolveAttributePath($name);
$name = $this->resolveKey($name);
if (null === $attributes) {
return $default;
}
return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
}
/**
* {@inheritdoc}
*/
public function set($name, $value)
{
$attributes = & $this->resolveAttributePath($name, true);
$name = $this->resolveKey($name);
$attributes[$name] = $value;
}
/**
* {@inheritdoc}
*/
public function remove($name)
{
$retval = null;
$attributes = & $this->resolveAttributePath($name);
$name = $this->resolveKey($name);
if (null !== $attributes && array_key_exists($name, $attributes)) {
$retval = $attributes[$name];
unset($attributes[$name]);
}
return $retval;
}
/**
* Resolves a path in attributes property and returns it as a reference.
*
* This method allows structured namespacing of session attributes.
*
* @param string $name Key name
* @param boolean $writeContext Write context, default false
*
* @return array
*/
protected function &resolveAttributePath($name, $writeContext = false)
{
$array = & $this->attributes;
$name = (strpos($name, $this->namespaceCharacter) === 0) ? substr($name, 1) : $name;
// Check if there is anything to do, else return
if (!$name) {
return $array;
}
$parts = explode($this->namespaceCharacter, $name);
if (count($parts) < 2) {
if (!$writeContext) {
return $array;
}
$array[$parts[0]] = array();
return $array;
}
unset($parts[count($parts)-1]);
foreach ($parts as $part) {
if (null !== $array && !array_key_exists($part, $array)) {
$array[$part] = $writeContext ? array() : null;
}
$array = & $array[$part];
}
return $array;
}
/**
* Resolves the key from the name.
*
* This is the last part in a dot separated string.
*
* @param string $name
*
* @return string
*/
protected function resolveKey($name)
{
if (strpos($name, $this->namespaceCharacter) !== false) {
$name = substr($name, strrpos($name, $this->namespaceCharacter)+1, strlen($name));
}
return $name;
}
}

View File

@@ -0,0 +1,176 @@
<?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\Session\Flash;
/**
* AutoExpireFlashBag flash message container.
*
* @author Drak <drak@zikula.org>
*/
class AutoExpireFlashBag implements FlashBagInterface
{
private $name = 'flashes';
/**
* Flash messages.
*
* @var array
*/
private $flashes = array();
/**
* The storage key for flashes in the session
*
* @var string
*/
private $storageKey;
/**
* Constructor.
*
* @param string $storageKey The key used to store flashes in the session.
*/
public function __construct($storageKey = '_sf2_flashes')
{
$this->storageKey = $storageKey;
$this->flashes = array('display' => array(), 'new' => array());
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
/**
* {@inheritdoc}
*/
public function initialize(array &$flashes)
{
$this->flashes = &$flashes;
// The logic: messages from the last request will be stored in new, so we move them to previous
// This request we will show what is in 'display'. What is placed into 'new' this time round will
// be moved to display next time round.
$this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array();
$this->flashes['new'] = array();
}
/**
* {@inheritdoc}
*/
public function add($type, $message)
{
$this->flashes['new'][$type][] = $message;
}
/**
* {@inheritdoc}
*/
public function peek($type, array $default = array())
{
return $this->has($type) ? $this->flashes['display'][$type] : $default;
}
/**
* {@inheritdoc}
*/
public function peekAll()
{
return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array();
}
/**
* {@inheritdoc}
*/
public function get($type, array $default = array())
{
$return = $default;
if (!$this->has($type)) {
return $return;
}
if (isset($this->flashes['display'][$type])) {
$return = $this->flashes['display'][$type];
unset($this->flashes['display'][$type]);
}
return $return;
}
/**
* {@inheritdoc}
*/
public function all()
{
$return = $this->flashes['display'];
$this->flashes = array('new' => array(), 'display' => array());
return $return;
}
/**
* {@inheritdoc}
*/
public function setAll(array $messages)
{
$this->flashes['new'] = $messages;
}
/**
* {@inheritdoc}
*/
public function set($type, $messages)
{
$this->flashes['new'][$type] = (array) $messages;
}
/**
* {@inheritdoc}
*/
public function has($type)
{
return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
}
/**
* {@inheritdoc}
*/
public function keys()
{
return array_keys($this->flashes['display']);
}
/**
* {@inheritdoc}
*/
public function getStorageKey()
{
return $this->storageKey;
}
/**
* {@inheritdoc}
*/
public function clear()
{
return $this->all();
}
}

View File

@@ -0,0 +1,176 @@
<?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\Session\Flash;
/**
* FlashBag flash message container.
*
* @author Drak <drak@zikula.org>
*/
class FlashBag implements FlashBagInterface, \IteratorAggregate
{
private $name = 'flashes';
/**
* Flash messages.
*
* @var array
*/
private $flashes = array();
/**
* The storage key for flashes in the session
*
* @var string
*/
private $storageKey;
/**
* Constructor.
*
* @param string $storageKey The key used to store flashes in the session.
*/
public function __construct($storageKey = '_sf2_flashes')
{
$this->storageKey = $storageKey;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
/**
* {@inheritdoc}
*/
public function initialize(array &$flashes)
{
$this->flashes = &$flashes;
}
/**
* {@inheritdoc}
*/
public function add($type, $message)
{
$this->flashes[$type][] = $message;
}
/**
* {@inheritdoc}
*/
public function peek($type, array $default =array())
{
return $this->has($type) ? $this->flashes[$type] : $default;
}
/**
* {@inheritdoc}
*/
public function peekAll()
{
return $this->flashes;
}
/**
* {@inheritdoc}
*/
public function get($type, array $default = array())
{
if (!$this->has($type)) {
return $default;
}
$return = $this->flashes[$type];
unset($this->flashes[$type]);
return $return;
}
/**
* {@inheritdoc}
*/
public function all()
{
$return = $this->peekAll();
$this->flashes = array();
return $return;
}
/**
* {@inheritdoc}
*/
public function set($type, $messages)
{
$this->flashes[$type] = (array) $messages;
}
/**
* {@inheritdoc}
*/
public function setAll(array $messages)
{
$this->flashes = $messages;
}
/**
* {@inheritdoc}
*/
public function has($type)
{
return array_key_exists($type, $this->flashes) && $this->flashes[$type];
}
/**
* {@inheritdoc}
*/
public function keys()
{
return array_keys($this->flashes);
}
/**
* {@inheritdoc}
*/
public function getStorageKey()
{
return $this->storageKey;
}
/**
* {@inheritdoc}
*/
public function clear()
{
return $this->all();
}
/**
* Returns an iterator for flashes.
*
* @return \ArrayIterator An \ArrayIterator instance
*/
public function getIterator()
{
return new \ArrayIterator($this->all());
}
}

View File

@@ -0,0 +1,93 @@
<?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\Session\Flash;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
/**
* FlashBagInterface.
*
* @author Drak <drak@zikula.org>
*/
interface FlashBagInterface extends SessionBagInterface
{
/**
* Adds a flash message for type.
*
* @param string $type
* @param string $message
*/
public function add($type, $message);
/**
* Registers a message for a given type.
*
* @param string $type
* @param string|array $message
*/
public function set($type, $message);
/**
* Gets flash messages for a given type.
*
* @param string $type Message category type.
* @param array $default Default value if $type does not exist.
*
* @return array
*/
public function peek($type, array $default = array());
/**
* Gets all flash messages.
*
* @return array
*/
public function peekAll();
/**
* Gets and clears flash from the stack.
*
* @param string $type
* @param array $default Default value if $type does not exist.
*
* @return array
*/
public function get($type, array $default = array());
/**
* Gets and clears flashes from the stack.
*
* @return array
*/
public function all();
/**
* Sets all flash messages.
*/
public function setAll(array $messages);
/**
* Has flash messages for a given type?
*
* @param string $type
*
* @return boolean
*/
public function has($type);
/**
* Returns a list of all defined types.
*
* @return array
*/
public function keys();
}

View File

@@ -0,0 +1,252 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session;
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
/**
* Session.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Drak <drak@zikula.org>
*
* @api
*/
class Session implements SessionInterface, \IteratorAggregate, \Countable
{
/**
* Storage driver.
*
* @var SessionStorageInterface
*/
protected $storage;
/**
* @var string
*/
private $flashName;
/**
* @var string
*/
private $attributeName;
/**
* Constructor.
*
* @param SessionStorageInterface $storage A SessionStorageInterface instance.
* @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
* @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
*/
public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
{
$this->storage = $storage ?: new NativeSessionStorage();
$attributes = $attributes ?: new AttributeBag();
$this->attributeName = $attributes->getName();
$this->registerBag($attributes);
$flashes = $flashes ?: new FlashBag();
$this->flashName = $flashes->getName();
$this->registerBag($flashes);
}
/**
* {@inheritdoc}
*/
public function start()
{
return $this->storage->start();
}
/**
* {@inheritdoc}
*/
public function has($name)
{
return $this->storage->getBag($this->attributeName)->has($name);
}
/**
* {@inheritdoc}
*/
public function get($name, $default = null)
{
return $this->storage->getBag($this->attributeName)->get($name, $default);
}
/**
* {@inheritdoc}
*/
public function set($name, $value)
{
$this->storage->getBag($this->attributeName)->set($name, $value);
}
/**
* {@inheritdoc}
*/
public function all()
{
return $this->storage->getBag($this->attributeName)->all();
}
/**
* {@inheritdoc}
*/
public function replace(array $attributes)
{
$this->storage->getBag($this->attributeName)->replace($attributes);
}
/**
* {@inheritdoc}
*/
public function remove($name)
{
return $this->storage->getBag($this->attributeName)->remove($name);
}
/**
* {@inheritdoc}
*/
public function clear()
{
$this->storage->getBag($this->attributeName)->clear();
}
/**
* {@inheritdoc}
*/
public function isStarted()
{
return $this->storage->isStarted();
}
/**
* Returns an iterator for attributes.
*
* @return \ArrayIterator An \ArrayIterator instance
*/
public function getIterator()
{
return new \ArrayIterator($this->storage->getBag($this->attributeName)->all());
}
/**
* Returns the number of attributes.
*
* @return int The number of attributes
*/
public function count()
{
return count($this->storage->getBag($this->attributeName)->all());
}
/**
* {@inheritdoc}
*/
public function invalidate($lifetime = null)
{
$this->storage->clear();
return $this->migrate(true, $lifetime);
}
/**
* {@inheritdoc}
*/
public function migrate($destroy = false, $lifetime = null)
{
return $this->storage->regenerate($destroy, $lifetime);
}
/**
* {@inheritdoc}
*/
public function save()
{
$this->storage->save();
}
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->storage->getId();
}
/**
* {@inheritdoc}
*/
public function setId($id)
{
$this->storage->setId($id);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->storage->getName();
}
/**
* {@inheritdoc}
*/
public function setName($name)
{
$this->storage->setName($name);
}
/**
* {@inheritdoc}
*/
public function getMetadataBag()
{
return $this->storage->getMetadataBag();
}
/**
* {@inheritdoc}
*/
public function registerBag(SessionBagInterface $bag)
{
$this->storage->registerBag($bag);
}
/**
* {@inheritdoc}
*/
public function getBag($name)
{
return $this->storage->getBag($name);
}
/**
* Gets the flashbag interface.
*
* @return FlashBagInterface
*/
public function getFlashBag()
{
return $this->getBag($this->flashName);
}
}

View File

@@ -0,0 +1,48 @@
<?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\Session;
/**
* Session Bag store.
*
* @author Drak <drak@zikula.org>
*/
interface SessionBagInterface
{
/**
* Gets this bag's name
*
* @return string
*/
public function getName();
/**
* Initializes the Bag
*
* @param array $array
*/
public function initialize(array &$array);
/**
* Gets the storage key for this bag.
*
* @return string
*/
public function getStorageKey();
/**
* Clears out data from bag.
*
* @return mixed Whatever data was contained.
*/
public function clear();
}

View File

@@ -0,0 +1,208 @@
<?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\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
/**
* Interface for the session.
*
* @author Drak <drak@zikula.org>
*/
interface SessionInterface
{
/**
* Starts the session storage.
*
* @return Boolean True if session started.
*
* @throws \RuntimeException If session fails to start.
*
* @api
*/
public function start();
/**
* Returns the session ID.
*
* @return string The session ID.
*
* @api
*/
public function getId();
/**
* Sets the session ID
*
* @param string $id
*
* @api
*/
public function setId($id);
/**
* Returns the session name.
*
* @return mixed The session name.
*
* @api
*/
public function getName();
/**
* Sets the session name.
*
* @param string $name
*
* @api
*/
public function setName($name);
/**
* Invalidates the current session.
*
* Clears all session attributes and flashes and regenerates the
* session and deletes the old session from persistence.
*
* @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value
* will leave the system settings unchanged, 0 sets the cookie
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*
* @return Boolean True if session invalidated, false if error.
*
* @api
*/
public function invalidate($lifetime = null);
/**
* Migrates the current session to a new session id while maintaining all
* session attributes.
*
* @param Boolean $destroy Whether to delete the old session or leave it to garbage collection.
* @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value
* will leave the system settings unchanged, 0 sets the cookie
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*
* @return Boolean True if session migrated, false if error.
*
* @api
*/
public function migrate($destroy = false, $lifetime = null);
/**
* Force the session to be saved and closed.
*
* This method is generally not required for real sessions as
* the session will be automatically saved at the end of
* code execution.
*/
public function save();
/**
* 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);
/**
* Returns an attribute.
*
* @param string $name The attribute name
* @param mixed $default The default value if not found.
*
* @return mixed
*
* @api
*/
public function get($name, $default = null);
/**
* Sets an attribute.
*
* @param string $name
* @param mixed $value
*
* @api
*/
public function set($name, $value);
/**
* Returns attributes.
*
* @return array Attributes
*
* @api
*/
public function all();
/**
* Sets attributes.
*
* @param array $attributes Attributes
*/
public function replace(array $attributes);
/**
* Removes an attribute.
*
* @param string $name
*
* @return mixed The removed value
*
* @api
*/
public function remove($name);
/**
* Clears all attributes.
*
* @api
*/
public function clear();
/**
* Checks if the session was started.
*
* @return Boolean
*/
public function isStarted();
/**
* Registers a SessionBagInterface with the session.
*
* @param SessionBagInterface $bag
*/
public function registerBag(SessionBagInterface $bag);
/**
* Gets a bag instance by name.
*
* @param string $name
*
* @return SessionBagInterface
*/
public function getBag($name);
/**
* Gets session meta.
*
* @return MetadataBag
*/
public function getMetadataBag();
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
/**
* MemcacheSessionHandler.
*
* @author Drak <drak@zikula.org>
*/
class MemcacheSessionHandler implements \SessionHandlerInterface
{
/**
* @var \Memcache Memcache driver.
*/
private $memcache;
/**
* @var integer Time to live in seconds
*/
private $ttl;
/**
* @var string Key prefix for shared environments.
*/
private $prefix;
/**
* Constructor.
*
* List of available options:
* * prefix: The prefix to use for the memcache keys in order to avoid collision
* * expiretime: The time to live in seconds
*
* @param \Memcache $memcache A \Memcache instance
* @param array $options An associative array of Memcache options
*
* @throws \InvalidArgumentException When unsupported options are passed
*/
public function __construct(\Memcache $memcache, array $options = array())
{
if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) {
throw new \InvalidArgumentException(sprintf(
'The following options are not supported "%s"', implode(', ', $diff)
));
}
$this->memcache = $memcache;
$this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
$this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
}
/**
* {@inheritDoc}
*/
public function open($savePath, $sessionName)
{
return true;
}
/**
* {@inheritDoc}
*/
public function close()
{
return $this->memcache->close();
}
/**
* {@inheritDoc}
*/
public function read($sessionId)
{
return $this->memcache->get($this->prefix.$sessionId) ?: '';
}
/**
* {@inheritDoc}
*/
public function write($sessionId, $data)
{
return $this->memcache->set($this->prefix.$sessionId, $data, 0, time() + $this->ttl);
}
/**
* {@inheritDoc}
*/
public function destroy($sessionId)
{
return $this->memcache->delete($this->prefix.$sessionId);
}
/**
* {@inheritDoc}
*/
public function gc($lifetime)
{
// not required here because memcache will auto expire the records anyhow.
return true;
}
}

View File

@@ -0,0 +1,115 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
/**
* MemcachedSessionHandler.
*
* Memcached based session storage handler based on the Memcached class
* provided by the PHP memcached extension.
*
* @see http://php.net/memcached
*
* @author Drak <drak@zikula.org>
*/
class MemcachedSessionHandler implements \SessionHandlerInterface
{
/**
* @var \Memcached Memcached driver.
*/
private $memcached;
/**
* @var integer Time to live in seconds
*/
private $ttl;
/**
* @var string Key prefix for shared environments.
*/
private $prefix;
/**
* Constructor.
*
* List of available options:
* * prefix: The prefix to use for the memcached keys in order to avoid collision
* * expiretime: The time to live in seconds
*
* @param \Memcached $memcached A \Memcached instance
* @param array $options An associative array of Memcached options
*
* @throws \InvalidArgumentException When unsupported options are passed
*/
public function __construct(\Memcached $memcached, array $options = array())
{
$this->memcached = $memcached;
if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) {
throw new \InvalidArgumentException(sprintf(
'The following options are not supported "%s"', implode(', ', $diff)
));
}
$this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
$this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
}
/**
* {@inheritDoc}
*/
public function open($savePath, $sessionName)
{
return true;
}
/**
* {@inheritDoc}
*/
public function close()
{
return true;
}
/**
* {@inheritDoc}
*/
public function read($sessionId)
{
return $this->memcached->get($this->prefix.$sessionId) ?: '';
}
/**
* {@inheritDoc}
*/
public function write($sessionId, $data)
{
return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
}
/**
* {@inheritDoc}
*/
public function destroy($sessionId)
{
return $this->memcached->delete($this->prefix.$sessionId);
}
/**
* {@inheritDoc}
*/
public function gc($lifetime)
{
// not required here because memcached will auto expire the records anyhow.
return true;
}
}

View File

@@ -0,0 +1,163 @@
<?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\Session\Storage\Handler;
/**
* MongoDB session handler
*
* @author Markus Bachmann <markus.bachmann@bachi.biz>
*/
class MongoDbSessionHandler implements \SessionHandlerInterface
{
/**
* @var \Mongo
*/
private $mongo;
/**
* @var \MongoCollection
*/
private $collection;
/**
* @var array
*/
private $options;
/**
* Constructor.
*
* List of available options:
* * database: The name of the database [required]
* * collection: The name of the collection [required]
* * id_field: The field name for storing the session id [default: _id]
* * data_field: The field name for storing the session data [default: data]
* * time_field: The field name for storing the timestamp [default: time]
*
* @param \Mongo|\MongoClient $mongo A MongoClient or Mongo instance
* @param array $options An associative array of field options
*
* @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
* @throws \InvalidArgumentException When "database" or "collection" not provided
*/
public function __construct($mongo, array $options)
{
if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
throw new \InvalidArgumentException('MongoClient or Mongo instance required');
}
if (!isset($options['database']) || !isset($options['collection'])) {
throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler');
}
$this->mongo = $mongo;
$this->options = array_merge(array(
'id_field' => '_id',
'data_field' => 'data',
'time_field' => 'time',
), $options);
}
/**
* {@inheritDoc}
*/
public function open($savePath, $sessionName)
{
return true;
}
/**
* {@inheritDoc}
*/
public function close()
{
return true;
}
/**
* {@inheritDoc}
*/
public function destroy($sessionId)
{
$this->getCollection()->remove(array(
$this->options['id_field'] => $sessionId
));
return true;
}
/**
* {@inheritDoc}
*/
public function gc($lifetime)
{
/* Note: MongoDB 2.2+ supports TTL collections, which may be used in
* place of this method by indexing the "time_field" field with an
* "expireAfterSeconds" option. Regardless of whether TTL collections
* are used, consider indexing this field to make the remove query more
* efficient.
*
* See: http://docs.mongodb.org/manual/tutorial/expire-data/
*/
$time = new \MongoDate(time() - $lifetime);
$this->getCollection()->remove(array(
$this->options['time_field'] => array('$lt' => $time),
));
return true;
}
/**
* {@inheritDoc]
*/
public function write($sessionId, $data)
{
$this->getCollection()->update(
array($this->options['id_field'] => $sessionId),
array('$set' => array(
$this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY),
$this->options['time_field'] => new \MongoDate(),
)),
array('upsert' => true, 'multiple' => false)
);
return true;
}
/**
* {@inheritDoc}
*/
public function read($sessionId)
{
$dbData = $this->getCollection()->findOne(array(
$this->options['id_field'] => $sessionId,
));
return null === $dbData ? '' : $dbData[$this->options['data_field']]->bin;
}
/**
* Return a "MongoCollection" instance
*
* @return \MongoCollection
*/
private function getCollection()
{
if (null === $this->collection) {
$this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
}
return $this->collection;
}
}

View File

@@ -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\Session\Storage\Handler;
/**
* NativeFileSessionHandler.
*
* Native session handler using PHP's built in file storage.
*
* @author Drak <drak@zikula.org>
*/
class NativeFileSessionHandler extends NativeSessionHandler
{
/**
* Constructor.
*
* @param string $savePath Path of directory to save session files.
* Default null will leave setting as defined by PHP.
* '/path', 'N;/path', or 'N;octal-mode;/path
*
* @see http://php.net/session.configuration.php#ini.session.save-path for further details.
*
* @throws \InvalidArgumentException On invalid $savePath
*/
public function __construct($savePath = null)
{
if (null === $savePath) {
$savePath = ini_get('session.save_path');
}
$baseDir = $savePath;
if ($count = substr_count($savePath, ';')) {
if ($count > 2) {
throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath));
}
// characters after last ';' are the path
$baseDir = ltrim(strrchr($savePath, ';'), ';');
}
if ($baseDir && !is_dir($baseDir)) {
mkdir($baseDir, 0777, true);
}
ini_set('session.save_path', $savePath);
ini_set('session.save_handler', 'files');
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
/**
* Adds SessionHandler functionality if available.
*
* @see http://php.net/sessionhandler
*/
if (version_compare(phpversion(), '5.4.0', '>=')) {
class NativeSessionHandler extends \SessionHandler {}
} else {
class NativeSessionHandler {}
}

View File

@@ -0,0 +1,72 @@
<?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\Session\Storage\Handler;
/**
* NullSessionHandler.
*
* Can be used in unit testing or in a situations where persisted sessions are not desired.
*
* @author Drak <drak@zikula.org>
*
* @api
*/
class NullSessionHandler implements \SessionHandlerInterface
{
/**
* {@inheritdoc}
*/
public function open($savePath, $sessionName)
{
return true;
}
/**
* {@inheritdoc}
*/
public function close()
{
return true;
}
/**
* {@inheritdoc}
*/
public function read($sessionId)
{
return '';
}
/**
* {@inheritdoc}
*/
public function write($sessionId, $data)
{
return true;
}
/**
* {@inheritdoc}
*/
public function destroy($sessionId)
{
return true;
}
/**
* {@inheritdoc}
*/
public function gc($lifetime)
{
return true;
}
}

View File

@@ -0,0 +1,243 @@
<?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\Session\Storage\Handler;
/**
* PdoSessionHandler.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Michael Williams <michael.williams@funsational.com>
*/
class PdoSessionHandler implements \SessionHandlerInterface
{
/**
* @var \PDO PDO instance.
*/
private $pdo;
/**
* @var array Database options.
*/
private $dbOptions;
/**
* Constructor.
*
* List of available options:
* * db_table: The name of the table [required]
* * db_id_col: The column where to store the session id [default: sess_id]
* * db_data_col: The column where to store the session data [default: sess_data]
* * db_time_col: The column where to store the timestamp [default: sess_time]
*
* @param \PDO $pdo A \PDO instance
* @param array $dbOptions An associative array of DB options
*
* @throws \InvalidArgumentException When "db_table" option is not provided
*/
public function __construct(\PDO $pdo, array $dbOptions = array())
{
if (!array_key_exists('db_table', $dbOptions)) {
throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.');
}
if (\PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) {
throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
}
$this->pdo = $pdo;
$this->dbOptions = array_merge(array(
'db_id_col' => 'sess_id',
'db_data_col' => 'sess_data',
'db_time_col' => 'sess_time',
), $dbOptions);
}
/**
* {@inheritDoc}
*/
public function open($path, $name)
{
return true;
}
/**
* {@inheritDoc}
*/
public function close()
{
return true;
}
/**
* {@inheritDoc}
*/
public function destroy($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->pdo->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;
}
/**
* {@inheritDoc}
*/
public function gc($lifetime)
{
// get table/column
$dbTable = $this->dbOptions['db_table'];
$dbTimeCol = $this->dbOptions['db_time_col'];
// delete the session records that have expired
$sql = "DELETE FROM $dbTable WHERE $dbTimeCol < :time";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':time', time() - $lifetime, \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;
}
/**
* {@inheritDoc}
*/
public function read($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->pdo->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$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 base64_decode($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 read the session data: %s', $e->getMessage()), 0, $e);
}
}
/**
* {@inheritDoc}
*/
public function write($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'];
//session data can contain non binary safe characters so we need to encode it
$encoded = base64_encode($data);
try {
$driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
if ('mysql' === $driver) {
// MySQL would report $stmt->rowCount() = 0 on UPDATE when the data is left unchanged
// it could result in calling createNewSession() whereas the session already exists in
// the DB which would fail as the id is unique
$stmt = $this->pdo->prepare(
"INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time) " .
"ON DUPLICATE KEY UPDATE $dbDataCol = VALUES($dbDataCol), $dbTimeCol = VALUES($dbTimeCol)"
);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
$stmt->execute();
} elseif ('oci' === $driver) {
$stmt = $this->pdo->prepare("MERGE INTO $dbTable USING DUAL ON($dbIdCol = :id) ".
"WHEN NOT MATCHED THEN INSERT ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, sysdate) " .
"WHEN MATCHED THEN UPDATE SET $dbDataCol = :data WHERE $dbIdCol = :id");
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$stmt->execute();
} else {
$stmt = $this->pdo->prepare("UPDATE $dbTable SET $dbDataCol = :data, $dbTimeCol = :time WHERE $dbIdCol = :id");
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$stmt->bindParam(':data', $encoded, \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 write the 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
*
* @return boolean True.
*/
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)";
//session data can contain non binary safe characters so we need to encode it
$encoded = base64_encode($data);
$stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
$stmt->execute();
return true;
}
}

View File

@@ -0,0 +1,160 @@
<?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\Session\Storage;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
/**
* Metadata container.
*
* Adds metadata to the session.
*
* @author Drak <drak@zikula.org>
*/
class MetadataBag implements SessionBagInterface
{
const CREATED = 'c';
const UPDATED = 'u';
const LIFETIME = 'l';
/**
* @var string
*/
private $name = '__metadata';
/**
* @var string
*/
private $storageKey;
/**
* @var array
*/
protected $meta = array();
/**
* Unix timestamp.
*
* @var integer
*/
private $lastUsed;
/**
* Constructor.
*
* @param string $storageKey The key used to store bag in the session.
*/
public function __construct($storageKey = '_sf2_meta')
{
$this->storageKey = $storageKey;
$this->meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0);
}
/**
* {@inheritdoc}
*/
public function initialize(array &$array)
{
$this->meta = &$array;
if (isset($array[self::CREATED])) {
$this->lastUsed = $this->meta[self::UPDATED];
$this->meta[self::UPDATED] = time();
} else {
$this->stampCreated();
}
}
/**
* Gets the lifetime that the session cookie was set with.
*
* @return integer
*/
public function getLifetime()
{
return $this->meta[self::LIFETIME];
}
/**
* Stamps a new session's metadata.
*
* @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value
* will leave the system settings unchanged, 0 sets the cookie
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*/
public function stampNew($lifetime = null)
{
$this->stampCreated($lifetime);
}
/**
* {@inheritdoc}
*/
public function getStorageKey()
{
return $this->storageKey;
}
/**
* Gets the created timestamp metadata.
*
* @return integer Unix timestamp
*/
public function getCreated()
{
return $this->meta[self::CREATED];
}
/**
* Gets the last used metadata.
*
* @return integer Unix timestamp
*/
public function getLastUsed()
{
return $this->lastUsed;
}
/**
* {@inheritdoc}
*/
public function clear()
{
// nothing to do
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
/**
* Sets name.
*
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
private function stampCreated($lifetime = null)
{
$timeStamp = time();
$this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp;
$this->meta[self::LIFETIME] = (null === $lifetime) ? ini_get('session.cookie_lifetime') : $lifetime;
}
}

View File

@@ -0,0 +1,268 @@
<?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\Session\Storage;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
/**
* MockArraySessionStorage mocks the session for unit tests.
*
* No PHP session is actually started since a session can be initialized
* and shutdown only once per PHP execution cycle.
*
* When doing functional testing, you should use MockFileSessionStorage instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
* @author Drak <drak@zikula.org>
*/
class MockArraySessionStorage implements SessionStorageInterface
{
/**
* @var string
*/
protected $id = '';
/**
* @var string
*/
protected $name;
/**
* @var boolean
*/
protected $started = false;
/**
* @var boolean
*/
protected $closed = false;
/**
* @var array
*/
protected $data = array();
/**
* @var MetadataBag
*/
protected $metadataBag;
/**
* @var array
*/
protected $bags;
/**
* Constructor.
*
* @param string $name Session name
* @param MetadataBag $metaBag MetadataBag instance.
*/
public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null)
{
$this->name = $name;
$this->setMetadataBag($metaBag);
}
/**
* Sets the session data.
*
* @param array $array
*/
public function setSessionData(array $array)
{
$this->data = $array;
}
/**
* {@inheritdoc}
*/
public function start()
{
if ($this->started && !$this->closed) {
return true;
}
if (empty($this->id)) {
$this->id = $this->generateId();
}
$this->loadSession();
return true;
}
/**
* {@inheritdoc}
*/
public function regenerate($destroy = false, $lifetime = null)
{
if (!$this->started) {
$this->start();
}
$this->metadataBag->stampNew($lifetime);
$this->id = $this->generateId();
return true;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->id;
}
/**
* {@inheritdoc}
*/
public function setId($id)
{
if ($this->started) {
throw new \LogicException('Cannot set session ID after the session has started.');
}
$this->id = $id;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
/**
* {@inheritdoc}
*/
public function setName($name)
{
$this->name = $name;
}
/**
* {@inheritdoc}
*/
public function save()
{
if (!$this->started || $this->closed) {
throw new \RuntimeException("Trying to save a session that was not started yet or was already closed");
}
// nothing to do since we don't persist the session data
$this->closed = false;
}
/**
* {@inheritdoc}
*/
public function clear()
{
// clear out the bags
foreach ($this->bags as $bag) {
$bag->clear();
}
// clear out the session
$this->data = array();
// reconnect the bags to the session
$this->loadSession();
}
/**
* {@inheritdoc}
*/
public function registerBag(SessionBagInterface $bag)
{
$this->bags[$bag->getName()] = $bag;
}
/**
* {@inheritdoc}
*/
public function getBag($name)
{
if (!isset($this->bags[$name])) {
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
}
if (!$this->started) {
$this->start();
}
return $this->bags[$name];
}
/**
* {@inheritdoc}
*/
public function isStarted()
{
return $this->started;
}
/**
* Sets the MetadataBag.
*
* @param MetadataBag $bag
*/
public function setMetadataBag(MetadataBag $bag = null)
{
if (null === $bag) {
$bag = new MetadataBag();
}
$this->metadataBag = $bag;
}
/**
* Gets the MetadataBag.
*
* @return MetadataBag
*/
public function getMetadataBag()
{
return $this->metadataBag;
}
/**
* Generates a session ID.
*
* This doesn't need to be particularly cryptographically secure since this is just
* a mock.
*
* @return string
*/
protected function generateId()
{
return sha1(uniqid(mt_rand()));
}
protected function loadSession()
{
$bags = array_merge($this->bags, array($this->metadataBag));
foreach ($bags as $bag) {
$key = $bag->getStorageKey();
$this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array();
$bag->initialize($this->data[$key]);
}
$this->started = true;
$this->closed = false;
}
}

View File

@@ -0,0 +1,138 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage;
/**
* MockFileSessionStorage is used to mock sessions for
* functional testing when done in a single PHP process.
*
* No PHP session is actually started since a session can be initialized
* and shutdown only once per PHP execution cycle and this class does
* not pollute any session related globals, including session_*() functions
* or session.* PHP ini directives.
*
* @author Drak <drak@zikula.org>
*/
class MockFileSessionStorage extends MockArraySessionStorage
{
/**
* @var string
*/
private $savePath;
/**
* Constructor.
*
* @param string $savePath Path of directory to save session files.
* @param string $name Session name.
* @param MetadataBag $metaBag MetadataBag instance.
*/
public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null)
{
if (null === $savePath) {
$savePath = sys_get_temp_dir();
}
if (!is_dir($savePath)) {
mkdir($savePath, 0777, true);
}
$this->savePath = $savePath;
parent::__construct($name, $metaBag);
}
/**
* {@inheritdoc}
*/
public function start()
{
if ($this->started) {
return true;
}
if (!$this->id) {
$this->id = $this->generateId();
}
$this->read();
$this->started = true;
return true;
}
/**
* {@inheritdoc}
*/
public function regenerate($destroy = false, $lifetime = null)
{
if (!$this->started) {
$this->start();
}
if ($destroy) {
$this->destroy();
}
return parent::regenerate($destroy, $lifetime);
}
/**
* {@inheritdoc}
*/
public function save()
{
if (!$this->started) {
throw new \RuntimeException("Trying to save a session that was not started yet or was already closed");
}
file_put_contents($this->getFilePath(), serialize($this->data));
// this is needed for Silex, where the session object is re-used across requests
// in functional tests. In Symfony, the container is rebooted, so we don't have
// this issue
$this->started = false;
}
/**
* Deletes a session from persistent storage.
* Deliberately leaves session data in memory intact.
*/
private function destroy()
{
if (is_file($this->getFilePath())) {
unlink($this->getFilePath());
}
}
/**
* Calculate path to file.
*
* @return string File path
*/
private function getFilePath()
{
return $this->savePath.'/'.$this->id.'.mocksess';
}
/**
* Reads session from storage and loads session.
*/
private function read()
{
$filePath = $this->getFilePath();
$this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array();
$this->loadSession();
}
}

View File

@@ -0,0 +1,431 @@
<?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\Session\Storage;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
/**
* This provides a base class for session attribute storage.
*
* @author Drak <drak@zikula.org>
*/
class NativeSessionStorage implements SessionStorageInterface
{
/**
* Array of SessionBagInterface
*
* @var SessionBagInterface[]
*/
protected $bags;
/**
* @var Boolean
*/
protected $started = false;
/**
* @var Boolean
*/
protected $closed = false;
/**
* @var AbstractProxy
*/
protected $saveHandler;
/**
* @var MetadataBag
*/
protected $metadataBag;
/**
* Constructor.
*
* Depending on how you want the storage driver to behave you probably
* want to override this constructor entirely.
*
* List of options for $options array with their defaults.
* @see http://php.net/session.configuration for options
* but we omit 'session.' from the beginning of the keys for convenience.
*
* ("auto_start", is not supported as it tells PHP to start a session before
* PHP starts to execute user-land code. Setting during runtime has no effect).
*
* cache_limiter, "nocache" (use "0" to prevent headers from being sent entirely).
* cookie_domain, ""
* cookie_httponly, ""
* cookie_lifetime, "0"
* cookie_path, "/"
* cookie_secure, ""
* entropy_file, ""
* entropy_length, "0"
* gc_divisor, "100"
* gc_maxlifetime, "1440"
* gc_probability, "1"
* hash_bits_per_character, "4"
* hash_function, "0"
* name, "PHPSESSID"
* referer_check, ""
* serialize_handler, "php"
* use_cookies, "1"
* use_only_cookies, "1"
* use_trans_sid, "0"
* upload_progress.enabled, "1"
* upload_progress.cleanup, "1"
* upload_progress.prefix, "upload_progress_"
* upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
* upload_progress.freq, "1%"
* upload_progress.min-freq, "1"
* url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
*
* @param array $options Session configuration options.
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
* @param MetadataBag $metaBag MetadataBag.
*/
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
{
session_cache_limiter(''); // disable by default because it's managed by HeaderBag (if used)
ini_set('session.use_cookies', 1);
if (version_compare(phpversion(), '5.4.0', '>=')) {
session_register_shutdown();
} else {
register_shutdown_function('session_write_close');
}
$this->setMetadataBag($metaBag);
$this->setOptions($options);
$this->setSaveHandler($handler);
}
/**
* Gets the save handler instance.
*
* @return AbstractProxy
*/
public function getSaveHandler()
{
return $this->saveHandler;
}
/**
* {@inheritdoc}
*/
public function start()
{
if ($this->started && !$this->closed) {
return true;
}
if (version_compare(phpversion(), '5.4.0', '>=') && \PHP_SESSION_ACTIVE === session_status()) {
throw new \RuntimeException('Failed to start the session: already started by PHP.');
}
if (version_compare(phpversion(), '5.4.0', '<') && isset($_SESSION) && session_id()) {
// not 100% fool-proof, but is the most reliable way to determine if a session is active in PHP 5.3
throw new \RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).');
}
if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
}
// ok to try and start the session
if (!session_start()) {
throw new \RuntimeException('Failed to start the session');
}
$this->loadSession();
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
// This condition matches only PHP 5.3 with internal save handlers
$this->saveHandler->setActive(true);
}
return true;
}
/**
* {@inheritdoc}
*/
public function getId()
{
if (!$this->started) {
return ''; // returning empty is consistent with session_id() behaviour
}
return $this->saveHandler->getId();
}
/**
* {@inheritdoc}
*/
public function setId($id)
{
$this->saveHandler->setId($id);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->saveHandler->getName();
}
/**
* {@inheritdoc}
*/
public function setName($name)
{
$this->saveHandler->setName($name);
}
/**
* {@inheritdoc}
*/
public function regenerate($destroy = false, $lifetime = null)
{
if (null !== $lifetime) {
ini_set('session.cookie_lifetime', $lifetime);
}
if ($destroy) {
$this->metadataBag->stampNew();
}
$ret = session_regenerate_id($destroy);
// workaround for https://bugs.php.net/bug.php?id=61470 as suggested by David Grudl
if ('files' === $this->getSaveHandler()->getSaveHandlerName()) {
session_write_close();
if (isset($_SESSION)) {
$backup = $_SESSION;
session_start();
$_SESSION = $backup;
} else {
session_start();
}
}
return $ret;
}
/**
* {@inheritdoc}
*/
public function save()
{
session_write_close();
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
// This condition matches only PHP 5.3 with internal save handlers
$this->saveHandler->setActive(false);
}
$this->closed = true;
}
/**
* {@inheritdoc}
*/
public function clear()
{
// clear out the bags
foreach ($this->bags as $bag) {
$bag->clear();
}
// clear out the session
$_SESSION = array();
// reconnect the bags to the session
$this->loadSession();
}
/**
* {@inheritdoc}
*/
public function registerBag(SessionBagInterface $bag)
{
$this->bags[$bag->getName()] = $bag;
}
/**
* {@inheritdoc}
*/
public function getBag($name)
{
if (!isset($this->bags[$name])) {
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
}
if ($this->saveHandler->isActive() && !$this->started) {
$this->loadSession();
} elseif (!$this->started) {
$this->start();
}
return $this->bags[$name];
}
/**
* Sets the MetadataBag.
*
* @param MetadataBag $metaBag
*/
public function setMetadataBag(MetadataBag $metaBag = null)
{
if (null === $metaBag) {
$metaBag = new MetadataBag();
}
$this->metadataBag = $metaBag;
}
/**
* Gets the MetadataBag.
*
* @return MetadataBag
*/
public function getMetadataBag()
{
return $this->metadataBag;
}
/**
* {@inheritdoc}
*/
public function isStarted()
{
return $this->started;
}
/**
* Sets session.* ini variables.
*
* For convenience we omit 'session.' from the beginning of the keys.
* Explicitly ignores other ini keys.
*
* @param array $options Session ini directives array(key => value).
*
* @see http://php.net/session.configuration
*/
public function setOptions(array $options)
{
$validOptions = array_flip(array(
'cache_limiter', 'cookie_domain', 'cookie_httponly',
'cookie_lifetime', 'cookie_path', 'cookie_secure',
'entropy_file', 'entropy_length', 'gc_divisor',
'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
'hash_function', 'name', 'referer_check',
'serialize_handler', 'use_cookies',
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags',
));
foreach ($options as $key => $value) {
if (isset($validOptions[$key])) {
ini_set('session.'.$key, $value);
}
}
}
/**
* Registers session save handler as a PHP session handler.
*
* To use internal PHP session save handlers, override this method using ini_set with
* session.save_handler and session.save_path e.g.
*
* ini_set('session.save_handler', 'files');
* ini_set('session.save_path', /tmp');
*
* or pass in a NativeSessionHandler instance which configures session.save_handler in the
* constructor, for a template see NativeFileSessionHandler or use handlers in
* composer package drak/native-session
*
* @see http://php.net/session-set-save-handler
* @see http://php.net/sessionhandlerinterface
* @see http://php.net/sessionhandler
* @see http://github.com/drak/NativeSession
*
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $saveHandler
*
* @throws \InvalidArgumentException
*/
public function setSaveHandler($saveHandler = null)
{
if (!$saveHandler instanceof AbstractProxy &&
!$saveHandler instanceof NativeSessionHandler &&
!$saveHandler instanceof \SessionHandlerInterface &&
null !== $saveHandler) {
throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \SessionHandlerInterface; or be null.');
}
// Wrap $saveHandler in proxy and prevent double wrapping of proxy
if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
$saveHandler = new SessionHandlerProxy($saveHandler);
} elseif (!$saveHandler instanceof AbstractProxy) {
$saveHandler = version_compare(phpversion(), '5.4.0', '>=') ?
new SessionHandlerProxy(new \SessionHandler()) : new NativeProxy();
}
$this->saveHandler = $saveHandler;
if ($this->saveHandler instanceof \SessionHandlerInterface) {
if (version_compare(phpversion(), '5.4.0', '>=')) {
session_set_save_handler($this->saveHandler, false);
} else {
session_set_save_handler(
array($this->saveHandler, 'open'),
array($this->saveHandler, 'close'),
array($this->saveHandler, 'read'),
array($this->saveHandler, 'write'),
array($this->saveHandler, 'destroy'),
array($this->saveHandler, 'gc')
);
}
}
}
/**
* Load the session with attributes.
*
* After starting the session, PHP retrieves the session from whatever handlers
* are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()).
* PHP takes the return value from the read() handler, unserializes it
* and populates $_SESSION with the result automatically.
*
* @param array|null $session
*/
protected function loadSession(array &$session = null)
{
if (null === $session) {
$session = &$_SESSION;
}
$bags = array_merge($this->bags, array($this->metadataBag));
foreach ($bags as $bag) {
$key = $bag->getStorageKey();
$session[$key] = isset($session[$key]) ? $session[$key] : array();
$bag->initialize($session[$key]);
}
$this->started = true;
$this->closed = false;
}
}

View File

@@ -0,0 +1,69 @@
<?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\Session\Storage;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
/**
* Allows session to be started by PHP and managed by Symfony2
*
* @author Drak <drak@zikula.org>
*/
class PhpBridgeSessionStorage extends NativeSessionStorage
{
/**
* Constructor.
*
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
* @param MetadataBag $metaBag MetadataBag
*/
public function __construct($handler = null, MetadataBag $metaBag = null)
{
$this->setMetadataBag($metaBag);
$this->setSaveHandler($handler);
}
/**
* {@inheritdoc}
*/
public function start()
{
if ($this->started && !$this->closed) {
return true;
}
$this->loadSession();
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
// This condition matches only PHP 5.3 + internal save handlers
$this->saveHandler->setActive(true);
}
return true;
}
/**
* {@inheritdoc}
*/
public function clear()
{
// clear out the bags and nothing else that may be set
// since the purpose of this driver is to share a handler
foreach ($this->bags as $bag) {
$bag->clear();
}
// reconnect the bags to the session
$this->loadSession();
}
}

View File

@@ -0,0 +1,154 @@
<?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\Session\Storage\Proxy;
/**
* AbstractProxy.
*
* @author Drak <drak@zikula.org>
*/
abstract class AbstractProxy
{
/**
* Flag if handler wraps an internal PHP session handler (using \SessionHandler).
*
* @var boolean
*/
protected $wrapper = false;
/**
* @var boolean
*/
protected $active = false;
/**
* @var string
*/
protected $saveHandlerName;
/**
* Gets the session.save_handler name.
*
* @return string
*/
public function getSaveHandlerName()
{
return $this->saveHandlerName;
}
/**
* Is this proxy handler and instance of \SessionHandlerInterface.
*
* @return boolean
*/
public function isSessionHandlerInterface()
{
return ($this instanceof \SessionHandlerInterface);
}
/**
* Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
*
* @return Boolean
*/
public function isWrapper()
{
return $this->wrapper;
}
/**
* Has a session started?
*
* @return Boolean
*/
public function isActive()
{
if (version_compare(phpversion(), '5.4.0', '>=')) {
return $this->active = \PHP_SESSION_ACTIVE === session_status();
}
return $this->active;
}
/**
* Sets the active flag.
*
* Has no effect under PHP 5.4+ as status is detected
* automatically in isActive()
*
* @internal
*
* @param Boolean $flag
*
* @throws \LogicException
*/
public function setActive($flag)
{
if (version_compare(phpversion(), '5.4.0', '>=')) {
throw new \LogicException('This method is disabled in PHP 5.4.0+');
}
$this->active = (bool) $flag;
}
/**
* Gets the session ID.
*
* @return string
*/
public function getId()
{
return session_id();
}
/**
* Sets the session ID.
*
* @param string $id
*
* @throws \LogicException
*/
public function setId($id)
{
if ($this->isActive()) {
throw new \LogicException('Cannot change the ID of an active session');
}
session_id($id);
}
/**
* Gets the session name.
*
* @return string
*/
public function getName()
{
return session_name();
}
/**
* Sets the session name.
*
* @param string $name
*
* @throws \LogicException
*/
public function setName($name)
{
if ($this->isActive()) {
throw new \LogicException('Cannot change the name of an active session');
}
session_name($name);
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
/**
* NativeProxy.
*
* This proxy is built-in session handlers in PHP 5.3.x
*
* @author Drak <drak@zikula.org>
*/
class NativeProxy extends AbstractProxy
{
/**
* Constructor.
*/
public function __construct()
{
// this makes an educated guess as to what the handler is since it should already be set.
$this->saveHandlerName = ini_get('session.save_handler');
}
/**
* Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
*
* @return Boolean False.
*/
public function isWrapper()
{
return false;
}
}

View File

@@ -0,0 +1,95 @@
<?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\Session\Storage\Proxy;
/**
* SessionHandler proxy.
*
* @author Drak <drak@zikula.org>
*/
class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface
{
/**
* @var \SessionHandlerInterface
*/
protected $handler;
/**
* Constructor.
*
* @param \SessionHandlerInterface $handler
*/
public function __construct(\SessionHandlerInterface $handler)
{
$this->handler = $handler;
$this->wrapper = ($handler instanceof \SessionHandler);
$this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
}
// \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function open($savePath, $sessionName)
{
$return = (bool) $this->handler->open($savePath, $sessionName);
if (true === $return) {
$this->active = true;
}
return $return;
}
/**
* {@inheritdoc}
*/
public function close()
{
$this->active = false;
return (bool) $this->handler->close();
}
/**
* {@inheritdoc}
*/
public function read($id)
{
return (string) $this->handler->read($id);
}
/**
* {@inheritdoc}
*/
public function write($id, $data)
{
return (bool) $this->handler->write($id, $data);
}
/**
* {@inheritdoc}
*/
public function destroy($id)
{
return (bool) $this->handler->destroy($id);
}
/**
* {@inheritdoc}
*/
public function gc($maxlifetime)
{
return (bool) $this->handler->gc($maxlifetime);
}
}

View File

@@ -0,0 +1,146 @@
<?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\Session\Storage;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
/**
* StorageInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Drak <drak@zikula.org>
*
* @api
*/
interface SessionStorageInterface
{
/**
* Starts the session.
*
* @throws \RuntimeException If something goes wrong starting the session.
*
* @return boolean True if started.
*
* @api
*/
public function start();
/**
* Checks if the session is started.
*
* @return boolean True if started, false otherwise.
*/
public function isStarted();
/**
* Returns the session ID
*
* @return string The session ID or empty.
*
* @api
*/
public function getId();
/**
* Sets the session ID
*
* @param string $id
*
* @api
*/
public function setId($id);
/**
* Returns the session name
*
* @return mixed The session name.
*
* @api
*/
public function getName();
/**
* Sets the session name
*
* @param string $name
*
* @api
*/
public function setName($name);
/**
* Regenerates id that represents this storage.
*
* This method must invoke session_regenerate_id($destroy) unless
* this interface is used for a storage object designed for unit
* or functional testing where a real PHP session would interfere
* with testing.
*
* Note regenerate+destroy should not clear the session data in memory
* only delete the session data from persistent storage.
*
* @param Boolean $destroy Destroy session when regenerating?
* @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value
* will leave the system settings unchanged, 0 sets the cookie
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*
* @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, $lifetime = null);
/**
* Force the session to be saved and closed.
*
* This method must invoke session_write_close() unless this interface is
* used for a storage object design for unit or functional testing where
* a real PHP session would interfere with testing, in which case it
* it should actually persist the session data if required.
*
* @throws \RuntimeException If the session is saved without being started, or if the session
* is already closed.
*/
public function save();
/**
* Clear all session data in memory.
*/
public function clear();
/**
* Gets a SessionBagInterface by name.
*
* @param string $name
*
* @return SessionBagInterface
*
* @throws \InvalidArgumentException If the bag does not exist
*/
public function getBag($name);
/**
* Registers a SessionBagInterface for use.
*
* @param SessionBagInterface $bag
*/
public function registerBag(SessionBagInterface $bag);
/**
* @return MetadataBag
*/
public function getMetadataBag();
}

View File

@@ -0,0 +1,127 @@
<?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;
/**
* StreamedResponse represents a streamed HTTP response.
*
* A StreamedResponse uses a callback for its content.
*
* The callback should use the standard PHP functions like echo
* to stream the response back to the client. The flush() method
* can also be used if needed.
*
* @see flush()
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class StreamedResponse extends Response
{
protected $callback;
protected $streamed;
/**
* Constructor.
*
* @param mixed $callback A valid PHP callback
* @param integer $status The response status code
* @param array $headers An array of response headers
*
* @api
*/
public function __construct($callback = null, $status = 200, $headers = array())
{
parent::__construct(null, $status, $headers);
if (null !== $callback) {
$this->setCallback($callback);
}
$this->streamed = false;
}
/**
* {@inheritDoc}
*/
public static function create($callback = null, $status = 200, $headers = array())
{
return new static($callback, $status, $headers);
}
/**
* Sets the PHP callback associated with this Response.
*
* @param mixed $callback A valid PHP callback
*
* @throws \LogicException
*/
public function setCallback($callback)
{
if (!is_callable($callback)) {
throw new \LogicException('The Response callback must be a valid PHP callable.');
}
$this->callback = $callback;
}
/**
* {@inheritdoc}
*/
public function prepare(Request $request)
{
$this->headers->set('Cache-Control', 'no-cache');
return parent::prepare($request);
}
/**
* {@inheritdoc}
*
* This method only sends the content once.
*/
public function sendContent()
{
if ($this->streamed) {
return;
}
$this->streamed = true;
if (null === $this->callback) {
throw new \LogicException('The Response callback must not be null.');
}
call_user_func($this->callback);
}
/**
* {@inheritdoc}
*
* @throws \LogicException when the content is not null
*/
public function setContent($content)
{
if (null !== $content) {
throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
}
}
/**
* {@inheritdoc}
*
* @return false
*/
public function getContent()
{
return false;
}
}

View File

@@ -0,0 +1,112 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
class AcceptHeaderItemTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideFromStringData
*/
public function testFromString($string, $value, array $attributes)
{
$item = AcceptHeaderItem::fromString($string);
$this->assertEquals($value, $item->getValue());
$this->assertEquals($attributes, $item->getAttributes());
}
public function provideFromStringData()
{
return array(
array(
'text/html',
'text/html', array()
),
array(
'"this;should,not=matter"',
'this;should,not=matter', array()
),
array(
"text/plain; charset=utf-8;param=\"this;should,not=matter\";\tfootnotes=true",
'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true')
),
array(
'"this;should,not=matter";charset=utf-8',
'this;should,not=matter', array('charset' => 'utf-8')
),
);
}
/**
* @dataProvider provideToStringData
*/
public function testToString($value, array $attributes, $string)
{
$item = new AcceptHeaderItem($value, $attributes);
$this->assertEquals($string, (string) $item);
}
public function provideToStringData()
{
return array(
array(
'text/html', array(),
'text/html'
),
array(
'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'),
'text/plain;charset=utf-8;param="this;should,not=matter";footnotes=true'
),
);
}
public function testValue()
{
$item = new AcceptHeaderItem('value', array());
$this->assertEquals('value', $item->getValue());
$item->setValue('new value');
$this->assertEquals('new value', $item->getValue());
$item->setValue(1);
$this->assertEquals('1', $item->getValue());
}
public function testQuality()
{
$item = new AcceptHeaderItem('value', array());
$this->assertEquals(1.0, $item->getQuality());
$item->setQuality(0.5);
$this->assertEquals(0.5, $item->getQuality());
$item->setAttribute('q', 0.75);
$this->assertEquals(0.75, $item->getQuality());
$this->assertFalse($item->hasAttribute('q'));
}
public function testAttribute()
{
$item = new AcceptHeaderItem('value', array());
$this->assertEquals(array(), $item->getAttributes());
$this->assertFalse($item->hasAttribute('test'));
$this->assertNull($item->getAttribute('test'));
$this->assertEquals('default', $item->getAttribute('test', 'default'));
$item->setAttribute('test', 'value');
$this->assertEquals(array('test' => 'value'), $item->getAttributes());
$this->assertTrue($item->hasAttribute('test'));
$this->assertEquals('value', $item->getAttribute('test'));
$this->assertEquals('value', $item->getAttribute('test', 'default'));
}
}

View File

@@ -0,0 +1,102 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\AcceptHeader;
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
class AcceptHeaderTest extends \PHPUnit_Framework_TestCase
{
public function testFirst()
{
$header = AcceptHeader::fromString('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c');
$this->assertSame('text/html', $header->first()->getValue());
}
/**
* @dataProvider provideFromStringData
*/
public function testFromString($string, array $items)
{
$header = AcceptHeader::fromString($string);
$parsed = array_values($header->all());
// reset index since the fixtures don't have them set
foreach ($parsed as $item) {
$item->setIndex(0);
}
$this->assertEquals($items, $parsed);
}
public function provideFromStringData()
{
return array(
array('', array()),
array('gzip', array(new AcceptHeaderItem('gzip'))),
array('gzip,deflate,sdch', array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))),
array("gzip, deflate\t,sdch", array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))),
array('"this;should,not=matter"', array(new AcceptHeaderItem('this;should,not=matter'))),
);
}
/**
* @dataProvider provideToStringData
*/
public function testToString(array $items, $string)
{
$header = new AcceptHeader($items);
$this->assertEquals($string, (string) $header);
}
public function provideToStringData()
{
return array(
array(array(), ''),
array(array(new AcceptHeaderItem('gzip')), 'gzip'),
array(array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')), 'gzip,deflate,sdch'),
array(array(new AcceptHeaderItem('this;should,not=matter')), 'this;should,not=matter'),
);
}
/**
* @dataProvider provideFilterData
*/
public function testFilter($string, $filter, array $values)
{
$header = AcceptHeader::fromString($string)->filter($filter);
$this->assertEquals($values, array_keys($header->all()));
}
public function provideFilterData()
{
return array(
array('fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', array('fr-FR', 'fr')),
);
}
/**
* @dataProvider provideSortingData
*/
public function testSorting($string, array $values)
{
$header = AcceptHeader::fromString($string);
$this->assertEquals($values, array_keys($header->all()));
}
public function provideSortingData()
{
return array(
'quality has priority' => array('*;q=0.3,ISO-8859-1,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')),
'order matters when q is equal' => array('*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')),
'order matters when q is equal2' => array('*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', array('utf-8', 'ISO-8859-1', '*')),
);
}
}

View File

@@ -0,0 +1,92 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\ApacheRequest;
class ApacheRequestTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideServerVars
*/
public function testUriMethods($server, $expectedRequestUri, $expectedBaseUrl, $expectedPathInfo)
{
$request = new ApacheRequest();
$request->server->replace($server);
$this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
$this->assertEquals($expectedBaseUrl, $request->getBaseUrl(), '->getBaseUrl() is correct');
$this->assertEquals($expectedPathInfo, $request->getPathInfo(), '->getPathInfo() is correct');
}
public function provideServerVars()
{
return array(
array(
array(
'REQUEST_URI' => '/foo/app_dev.php/bar',
'SCRIPT_NAME' => '/foo/app_dev.php',
'PATH_INFO' => '/bar',
),
'/foo/app_dev.php/bar',
'/foo/app_dev.php',
'/bar'
),
array(
array(
'REQUEST_URI' => '/foo/bar',
'SCRIPT_NAME' => '/foo/app_dev.php',
),
'/foo/bar',
'/foo',
'/bar',
),
array(
array(
'REQUEST_URI' => '/app_dev.php/foo/bar',
'SCRIPT_NAME' => '/app_dev.php',
'PATH_INFO' => '/foo/bar',
),
'/app_dev.php/foo/bar',
'/app_dev.php',
'/foo/bar',
),
array(
array(
'REQUEST_URI' => '/foo/bar',
'SCRIPT_NAME' => '/app_dev.php',
),
'/foo/bar',
'',
'/foo/bar',
),
array(
array(
'REQUEST_URI' => '/app_dev.php',
'SCRIPT_NAME' => '/app_dev.php',
),
'/app_dev.php',
'/app_dev.php',
'/',
),
array(
array(
'REQUEST_URI' => '/',
'SCRIPT_NAME' => '/app_dev.php',
),
'/',
'',
'/',
),
);
}
}

View File

@@ -0,0 +1,153 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
class BinaryFileResponseTest extends ResponseTestCase
{
public function testConstruction()
{
$response = new BinaryFileResponse('README.md', 404, array('X-Header' => 'Foo'), true, null, true, true);
$this->assertEquals(404, $response->getStatusCode());
$this->assertEquals('Foo', $response->headers->get('X-Header'));
$this->assertTrue($response->headers->has('ETag'));
$this->assertTrue($response->headers->has('Last-Modified'));
$this->assertFalse($response->headers->has('Content-Disposition'));
$response = BinaryFileResponse::create('README.md', 404, array(), true, ResponseHeaderBag::DISPOSITION_INLINE);
$this->assertEquals(404, $response->getStatusCode());
$this->assertFalse($response->headers->has('ETag'));
$this->assertEquals('inline; filename="README.md"', $response->headers->get('Content-Disposition'));
}
/**
* @expectedException \LogicException
*/
public function testSetContent()
{
$response = new BinaryFileResponse('README.md');
$response->setContent('foo');
}
public function testGetContent()
{
$response = new BinaryFileResponse('README.md');
$this->assertFalse($response->getContent());
}
/**
* @dataProvider provideRanges
*/
public function testRequests($requestRange, $offset, $length, $responseRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif')->setAutoEtag();
// do a request to get the ETag
$request = Request::create('/');
$response->prepare($request);
$etag = $response->headers->get('ETag');
// prepare a request for a range of the testing file
$request = Request::create('/');
$request->headers->set('If-Range', $etag);
$request->headers->set('Range', $requestRange);
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
fseek($file, $offset);
$data = fread($file, $length);
fclose($file);
$this->expectOutputString($data);
$response = clone $response;
$response->prepare($request);
$response->sendContent();
$this->assertEquals(206, $response->getStatusCode());
$this->assertEquals('binary', $response->headers->get('Content-Transfer-Encoding'));
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
}
public function provideRanges()
{
return array(
array('bytes=1-4', 1, 4, 'bytes 1-4/35'),
array('bytes=-5', 30, 5, 'bytes 30-34/35'),
array('bytes=-35', 0, 35, 'bytes 0-34/35'),
array('bytes=-40', 0, 35, 'bytes 0-34/35'),
array('bytes=30-', 30, 5, 'bytes 30-34/35'),
array('bytes=30-30', 30, 1, 'bytes 30-30/35'),
array('bytes=30-34', 30, 5, 'bytes 30-34/35'),
array('bytes=30-40', 30, 5, 'bytes 30-34/35')
);
}
public function testXSendfile()
{
$request = Request::create('/');
$request->headers->set('X-Sendfile-Type', 'X-Sendfile');
BinaryFileResponse::trustXSendfileTypeHeader();
$response = BinaryFileResponse::create('README.md');
$response->prepare($request);
$this->expectOutputString('');
$response->sendContent();
$this->assertContains('README.md', $response->headers->get('X-Sendfile'));
}
/**
* @dataProvider getSampleXAccelMappings
*/
public function testXAccelMapping($realpath, $mapping, $virtual)
{
$request = Request::create('/');
$request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect');
$request->headers->set('X-Accel-Mapping', $mapping);
$file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
->disableOriginalConstructor()
->getMock();
$file->expects($this->any())
->method('getRealPath')
->will($this->returnValue($realpath));
$file->expects($this->any())
->method('isReadable')
->will($this->returnValue(true));
BinaryFileResponse::trustXSendFileTypeHeader();
$response = new BinaryFileResponse('README.md');
$reflection = new \ReflectionObject($response);
$property = $reflection->getProperty('file');
$property->setAccessible(true);
$property->setValue($response, $file);
$response->prepare($request);
$this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect'));
}
public function getSampleXAccelMappings()
{
return array(
array('/var/www/var/www/files/foo.txt', '/files/=/var/www/', '/files/var/www/files/foo.txt'),
array('/home/foo/bar.txt', '/files/=/var/www/,/baz/=/home/foo/', '/baz/bar.txt'),
);
}
protected function provideResponse()
{
return new BinaryFileResponse('README.md');
}
}

View File

@@ -0,0 +1,145 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\Cookie;
/**
* CookieTest
*
* @author John Kary <john@johnkary.net>
* @author Hugo Hamon <hugo.hamon@sensio.com>
*/
class CookieTest extends \PHPUnit_Framework_TestCase
{
public function invalidNames()
{
return array(
array(''),
array(",MyName"),
array(";MyName"),
array(" MyName"),
array("\tMyName"),
array("\rMyName"),
array("\nMyName"),
array("\013MyName"),
array("\014MyName"),
);
}
/**
* @dataProvider invalidNames
* @expectedException InvalidArgumentException
* @covers Symfony\Component\HttpFoundation\Cookie::__construct
*/
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
{
new Cookie($name);
}
/**
* @expectedException InvalidArgumentException
*/
public function testInvalidExpiration()
{
$cookie = new Cookie('MyCookie', 'foo','bar');
}
/**
* @covers Symfony\Component\HttpFoundation\Cookie::getValue
*/
public function testGetValue()
{
$value = 'MyValue';
$cookie = new Cookie('MyCookie', $value);
$this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value');
}
public function testGetPath()
{
$cookie = new Cookie('foo', 'bar');
$this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path');
}
public function testGetExpiresTime()
{
$cookie = new Cookie('foo', 'bar', 3600);
$this->assertEquals(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
public function testConstructorWithDateTime()
{
$expire = new \DateTime();
$cookie = new Cookie('foo', 'bar', $expire);
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
public function testGetExpiresTimeWithStringValue()
{
$value = "+1 day";
$cookie = new Cookie('foo', 'bar', $value);
$expire = strtotime($value);
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
public function testGetDomain()
{
$cookie = new Cookie('foo', 'bar', 3600, '/', '.myfoodomain.com');
$this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
}
public function testIsSecure()
{
$cookie = new Cookie('foo', 'bar', 3600, '/', '.myfoodomain.com', true);
$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
}
public function testIsHttpOnly()
{
$cookie = new Cookie('foo', 'bar', 3600, '/', '.myfoodomain.com', false, true);
$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
}
public function testCookieIsNotCleared()
{
$cookie = new Cookie('foo', 'bar', time()+3600*24);
$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
}
public function testCookieIsCleared()
{
$cookie = new Cookie('foo', 'bar', time()-20);
$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
}
public function testToString()
{
$cookie = new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
$this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly', $cookie->__toString(), '->__toString() returns string representation of the cookie');
$cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com');
$this->assertEquals('foo=deleted; expires='.gmdate("D, d-M-Y H:i:s T", time()-31536001).'; path=/admin/; domain=.myfoodomain.com; httponly', $cookie->__toString(), '->__toString() returns string representation of a cleared cookie if value is NULL');
$cookie = new Cookie('foo', 'bar', 0, '/', '');
$this->assertEquals('foo=bar; path=/; httponly', $cookie->__toString());
}
}

View File

@@ -0,0 +1,169 @@
<?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\Tests\File;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
class FileTest extends \PHPUnit_Framework_TestCase
{
protected $file;
public function testGetMimeTypeUsesMimeTypeGuessers()
{
$file = new File(__DIR__.'/Fixtures/test.gif');
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
MimeTypeGuesser::getInstance()->register($guesser);
$this->assertEquals('image/gif', $file->getMimeType());
}
public function testGuessExtensionWithoutGuesser()
{
$file = new File(__DIR__.'/Fixtures/directory/.empty');
$this->assertNull($file->guessExtension());
}
public function testGuessExtensionIsBasedOnMimeType()
{
$file = new File(__DIR__.'/Fixtures/test');
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
MimeTypeGuesser::getInstance()->register($guesser);
$this->assertEquals('gif', $file->guessExtension());
}
public function testConstructWhenFileNotExists()
{
$this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
new File(__DIR__.'/Fixtures/not_here');
}
public function testMove()
{
$path = __DIR__.'/Fixtures/test.copy.gif';
$targetDir = __DIR__.'/Fixtures/directory';
$targetPath = $targetDir.'/test.copy.gif';
@unlink($path);
@unlink($targetPath);
copy(__DIR__.'/Fixtures/test.gif', $path);
$file = new File($path);
$movedFile = $file->move($targetDir);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
$this->assertTrue(file_exists($targetPath));
$this->assertFalse(file_exists($path));
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($targetPath);
}
public function testMoveWithNewName()
{
$path = __DIR__.'/Fixtures/test.copy.gif';
$targetDir = __DIR__.'/Fixtures/directory';
$targetPath = $targetDir.'/test.newname.gif';
@unlink($path);
@unlink($targetPath);
copy(__DIR__.'/Fixtures/test.gif', $path);
$file = new File($path);
$movedFile = $file->move($targetDir, 'test.newname.gif');
$this->assertTrue(file_exists($targetPath));
$this->assertFalse(file_exists($path));
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($targetPath);
}
public function getFilenameFixtures()
{
return array(
array('original.gif', 'original.gif'),
array('..\\..\\original.gif', 'original.gif'),
array('../../original.gif', 'original.gif'),
array(айлfile.gif', айлfile.gif'),
array('..\\..\\файлfile.gif', айлfile.gif'),
array('../../файлfile.gif', айлfile.gif'),
);
}
/**
* @dataProvider getFilenameFixtures
*/
public function testMoveWithNonLatinName($filename, $sanitizedFilename)
{
$path = __DIR__.'/Fixtures/'.$sanitizedFilename;
$targetDir = __DIR__.'/Fixtures/directory/';
$targetPath = $targetDir.$sanitizedFilename;
@unlink($path);
@unlink($targetPath);
copy(__DIR__.'/Fixtures/test.gif', $path);
$file = new File($path);
$movedFile = $file->move($targetDir,$filename);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
$this->assertTrue(file_exists($targetPath));
$this->assertFalse(file_exists($path));
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($targetPath);
}
public function testMoveToAnUnexistentDirectory()
{
$sourcePath = __DIR__.'/Fixtures/test.copy.gif';
$targetDir = __DIR__.'/Fixtures/directory/sub';
$targetPath = $targetDir.'/test.copy.gif';
@unlink($sourcePath);
@unlink($targetPath);
@rmdir($targetDir);
copy(__DIR__.'/Fixtures/test.gif', $sourcePath);
$file = new File($sourcePath);
$movedFile = $file->move($targetDir);
$this->assertFileExists($targetPath);
$this->assertFileNotExists($sourcePath);
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($sourcePath);
@unlink($targetPath);
@rmdir($targetDir);
}
public function testGetExtension()
{
$file = new File(__DIR__.'/Fixtures/test.gif');
$this->assertEquals('gif', $file->getExtension());
}
protected function createMockGuesser($path, $mimeType)
{
$guesser = $this->getMock('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface');
$guesser
->expects($this->once())
->method('guess')
->with($this->equalTo($path))
->will($this->returnValue($mimeType))
;
return $guesser;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 B

View File

@@ -0,0 +1,105 @@
<?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\Tests\File;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
class MimeTypeTest extends \PHPUnit_Framework_TestCase
{
protected $path;
public function testGuessImageWithoutExtension()
{
if (extension_loaded('fileinfo')) {
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
} else {
$this->assertNull(MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
}
}
public function testGuessImageWithDirectory()
{
$this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory');
}
public function testGuessImageWithFileBinaryMimeTypeGuesser()
{
$guesser = MimeTypeGuesser::getInstance();
$guesser->register(new FileBinaryMimeTypeGuesser());
if (extension_loaded('fileinfo')) {
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
} else {
$this->assertNull(MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
}
}
public function testGuessImageWithKnownExtension()
{
if (extension_loaded('fileinfo')) {
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif'));
} else {
$this->assertNull(MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif'));
}
}
public function testGuessFileWithUnknownExtension()
{
if (extension_loaded('fileinfo')) {
$this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
} else {
$this->assertNull(MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
}
}
public function testGuessWithIncorrectPath()
{
$this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here');
}
public function testGuessWithNonReadablePath()
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->markTestSkipped('Can not verify chmod operations on Windows');
}
if (in_array(get_current_user(), array('root'))) {
$this->markTestSkipped('This test will fail if run under superuser');
}
$path = __DIR__.'/../Fixtures/to_delete';
touch($path);
@chmod($path, 0333);
if (get_current_user() != 'root' && substr(sprintf('%o', fileperms($path)), -4) == '0333') {
$this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException');
MimeTypeGuesser::getInstance()->guess($path);
} else {
$this->markTestSkipped('Can not verify chmod operations, change of file permissions failed');
}
}
public static function tearDownAfterClass()
{
$path = __DIR__.'/../Fixtures/to_delete';
if (file_exists($path)) {
@chmod($path, 0666);
@unlink($path);
}
}
}

View File

@@ -0,0 +1,272 @@
<?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\Tests\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class UploadedFileTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!ini_get('file_uploads')) {
$this->markTestSkipped('file_uploads is disabled in php.ini');
}
}
public function testConstructWhenFileNotExists()
{
$this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
new UploadedFile(
__DIR__.'/Fixtures/not_here',
'original.gif',
null
);
}
public function testFileUploadsWithNoMimeType()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
null,
filesize(__DIR__.'/Fixtures/test.gif'),
UPLOAD_ERR_OK
);
$this->assertEquals('application/octet-stream', $file->getClientMimeType());
if (extension_loaded('fileinfo')) {
$this->assertEquals('image/gif', $file->getMimeType());
}
}
public function testFileUploadsWithUnknownMimeType()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/.unknownextension',
'original.gif',
null,
filesize(__DIR__.'/Fixtures/.unknownextension'),
UPLOAD_ERR_OK
);
$this->assertEquals('application/octet-stream', $file->getClientMimeType());
}
public function testGuessClientExtension()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/gif',
filesize(__DIR__.'/Fixtures/test.gif'),
null
);
$this->assertEquals('gif', $file->guessClientExtension());
}
public function testGuessClientExtensionWithIncorrectMimeType()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/jpeg',
filesize(__DIR__.'/Fixtures/test.gif'),
null
);
$this->assertEquals('jpeg', $file->guessClientExtension());
}
public function testErrorIsOkByDefault()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/gif',
filesize(__DIR__.'/Fixtures/test.gif'),
null
);
$this->assertEquals(UPLOAD_ERR_OK, $file->getError());
}
public function testGetClientOriginalName()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/gif',
filesize(__DIR__.'/Fixtures/test.gif'),
null
);
$this->assertEquals('original.gif', $file->getClientOriginalName());
}
public function testGetClientOriginalExtension()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/gif',
filesize(__DIR__.'/Fixtures/test.gif'),
null
);
$this->assertEquals('gif', $file->getClientOriginalExtension());
}
/**
* @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException
*/
public function testMoveLocalFileIsNotAllowed()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/gif',
filesize(__DIR__.'/Fixtures/test.gif'),
UPLOAD_ERR_OK
);
$movedFile = $file->move(__DIR__.'/Fixtures/directory');
}
public function testMoveLocalFileIsAllowedInTestMode()
{
$path = __DIR__.'/Fixtures/test.copy.gif';
$targetDir = __DIR__.'/Fixtures/directory';
$targetPath = $targetDir.'/test.copy.gif';
@unlink($path);
@unlink($targetPath);
copy(__DIR__.'/Fixtures/test.gif', $path);
$file = new UploadedFile(
$path,
'original.gif',
'image/gif',
filesize($path),
UPLOAD_ERR_OK,
true
);
$movedFile = $file->move(__DIR__.'/Fixtures/directory');
$this->assertTrue(file_exists($targetPath));
$this->assertFalse(file_exists($path));
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($targetPath);
}
public function testGetClientOriginalNameSanitizeFilename()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'../../original.gif',
'image/gif',
filesize(__DIR__.'/Fixtures/test.gif'),
null
);
$this->assertEquals('original.gif', $file->getClientOriginalName());
}
public function testGetSize()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/gif',
filesize(__DIR__.'/Fixtures/test.gif'),
null
);
$this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize());
$file = new UploadedFile(
__DIR__.'/Fixtures/test',
'original.gif',
'image/gif'
);
$this->assertEquals(filesize(__DIR__.'/Fixtures/test'), $file->getSize());
}
public function testGetExtension()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
null
);
$this->assertEquals('gif', $file->getExtension());
}
public function testIsValid()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
null,
filesize(__DIR__.'/Fixtures/test.gif'),
UPLOAD_ERR_OK,
true
);
$this->assertTrue($file->isValid());
}
/**
* @dataProvider uploadedFileErrorProvider
*/
public function testIsInvalidOnUploadError($error)
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
null,
filesize(__DIR__.'/Fixtures/test.gif'),
$error
);
$this->assertFalse($file->isValid());
}
public function uploadedFileErrorProvider()
{
return array(
array(UPLOAD_ERR_INI_SIZE),
array(UPLOAD_ERR_FORM_SIZE),
array(UPLOAD_ERR_PARTIAL),
array(UPLOAD_ERR_NO_TMP_DIR),
array(UPLOAD_ERR_EXTENSION),
);
}
public function testIsInvalidIfNotHttpUpload()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
null,
filesize(__DIR__.'/Fixtures/test.gif'),
UPLOAD_ERR_OK
);
$this->assertFalse($file->isValid());
}
}

View File

@@ -0,0 +1,148 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\FileBag;
/**
* FileBagTest.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
*/
class FileBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \InvalidArgumentException
*/
public function testFileMustBeAnArrayOrUploadedFile()
{
new FileBag(array('file' => 'foo'));
}
public function testShouldConvertsUploadedFiles()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array('file' => array(
'name' => basename($tmpFile),
'type' => 'text/plain',
'tmp_name' => $tmpFile,
'error' => 0,
'size' => 100
)));
$this->assertEquals($file, $bag->get('file'));
}
public function testShouldSetEmptyUploadedFilesToNull()
{
$bag = new FileBag(array('file' => array(
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => UPLOAD_ERR_NO_FILE,
'size' => 0
)));
$this->assertNull($bag->get('file'));
}
public function testShouldConvertUploadedFilesWithPhpBug()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array(
'child' => array(
'name' => array(
'file' => basename($tmpFile),
),
'type' => array(
'file' => 'text/plain',
),
'tmp_name' => array(
'file' => $tmpFile,
),
'error' => array(
'file' => 0,
),
'size' => array(
'file' => 100,
),
)
));
$files = $bag->all();
$this->assertEquals($file, $files['child']['file']);
}
public function testShouldConvertNestedUploadedFilesWithPhpBug()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array(
'child' => array(
'name' => array(
'sub' => array('file' => basename($tmpFile))
),
'type' => array(
'sub' => array('file' => 'text/plain')
),
'tmp_name' => array(
'sub' => array('file' => $tmpFile)
),
'error' => array(
'sub' => array('file' => 0)
),
'size' => array(
'sub' => array('file' => 100)
),
)
));
$files = $bag->all();
$this->assertEquals($file, $files['child']['sub']['file']);
}
public function testShouldNotConvertNestedUploadedFiles()
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array('image' => array('file' => $file)));
$files = $bag->all();
$this->assertEquals($file, $files['image']['file']);
}
protected function createTempFile()
{
return tempnam(sys_get_temp_dir().'/form_test', 'FormTest');
}
protected function setUp()
{
mkdir(sys_get_temp_dir().'/form_test', 0777, true);
}
protected function tearDown()
{
foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) {
unlink($file);
}
rmdir(sys_get_temp_dir().'/form_test');
}
}

View File

@@ -0,0 +1,216 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests;
use Symfony\Component\HttpFoundation\HeaderBag;
class HeaderBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Symfony\Component\HttpFoundation\HeaderBag::__construct
*/
public function testConstructor()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$this->assertTrue($bag->has('foo'));
}
public function testToStringNull()
{
$bag = new HeaderBag();
$this->assertEquals('', $bag->__toString());
}
public function testToStringNotNull()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$this->assertEquals("Foo: bar\r\n", $bag->__toString());
}
public function testKeys()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$keys = $bag->keys();
$this->assertEquals("foo", $keys[0]);
}
public function testGetDate()
{
$bag = new HeaderBag(array('foo' => 'Tue, 4 Sep 2012 20:00:00 +0200'));
$headerDate = $bag->getDate('foo');
$this->assertInstanceOf('DateTime', $headerDate);
}
/**
* @expectedException \RuntimeException
*/
public function testGetDateException()
{
$bag = new HeaderBag(array('foo' => 'Tue'));
$headerDate = $bag->getDate('foo');
}
public function testGetCacheControlHeader()
{
$bag = new HeaderBag();
$bag->addCacheControlDirective('public', '#a');
$this->assertTrue($bag->hasCacheControlDirective('public'));
$this->assertEquals('#a', $bag->getCacheControlDirective('public'));
}
/**
* @covers Symfony\Component\HttpFoundation\HeaderBag::all
*/
public function testAll()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$this->assertEquals(array('foo' => array('bar')), $bag->all(), '->all() gets all the input');
$bag = new HeaderBag(array('FOO' => 'BAR'));
$this->assertEquals(array('foo' => array('BAR')), $bag->all(), '->all() gets all the input key are lower case');
}
/**
* @covers Symfony\Component\HttpFoundation\HeaderBag::replace
*/
public function testReplace()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$bag->replace(array('NOPE' => 'BAR'));
$this->assertEquals(array('nope' => array('BAR')), $bag->all(), '->replace() replaces the input with the argument');
$this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input');
}
/**
* @covers Symfony\Component\HttpFoundation\HeaderBag::get
*/
public function testGet()
{
$bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz'));
$this->assertEquals( 'bar', $bag->get('foo'), '->get return current value');
$this->assertEquals( 'bar', $bag->get('FoO'), '->get key in case insensitive');
$this->assertEquals( array('bar'), $bag->get('foo', 'nope', false), '->get return the value as array');
// defaults
$this->assertNull($bag->get('none'), '->get unknown values returns null');
$this->assertEquals( 'default', $bag->get('none', 'default'), '->get unknown values returns default');
$this->assertEquals( array('default'), $bag->get('none', 'default', false), '->get unknown values returns default as array');
$bag->set('foo', 'bor', false);
$this->assertEquals( 'bar', $bag->get('foo'), '->get return first value');
$this->assertEquals( array('bar', 'bor'), $bag->get('foo', 'nope', false), '->get return all values as array');
}
public function testSetAssociativeArray()
{
$bag = new HeaderBag();
$bag->set('foo', array('bad-assoc-index' => 'value'));
$this->assertSame('value', $bag->get('foo'));
$this->assertEquals(array('value'), $bag->get('foo', 'nope', false), 'assoc indices of multi-valued headers are ignored');
}
/**
* @covers Symfony\Component\HttpFoundation\HeaderBag::contains
*/
public function testContains()
{
$bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz'));
$this->assertTrue( $bag->contains('foo', 'bar'), '->contains first value');
$this->assertTrue( $bag->contains('fuzz', 'bizz'), '->contains second value');
$this->assertFalse( $bag->contains('nope', 'nope'), '->contains unknown value');
$this->assertFalse( $bag->contains('foo', 'nope'), '->contains unknown value');
// Multiple values
$bag->set('foo', 'bor', false);
$this->assertTrue( $bag->contains('foo', 'bar'), '->contains first value');
$this->assertTrue( $bag->contains('foo', 'bor'), '->contains second value');
$this->assertFalse( $bag->contains('foo', 'nope'), '->contains unknown value');
}
public function testCacheControlDirectiveAccessors()
{
$bag = new HeaderBag();
$bag->addCacheControlDirective('public');
$this->assertTrue($bag->hasCacheControlDirective('public'));
$this->assertTrue($bag->getCacheControlDirective('public'));
$this->assertEquals('public', $bag->get('cache-control'));
$bag->addCacheControlDirective('max-age', 10);
$this->assertTrue($bag->hasCacheControlDirective('max-age'));
$this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
$this->assertEquals('max-age=10, public', $bag->get('cache-control'));
$bag->removeCacheControlDirective('max-age');
$this->assertFalse($bag->hasCacheControlDirective('max-age'));
}
public function testCacheControlDirectiveParsing()
{
$bag = new HeaderBag(array('cache-control' => 'public, max-age=10'));
$this->assertTrue($bag->hasCacheControlDirective('public'));
$this->assertTrue($bag->getCacheControlDirective('public'));
$this->assertTrue($bag->hasCacheControlDirective('max-age'));
$this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
$bag->addCacheControlDirective('s-maxage', 100);
$this->assertEquals('max-age=10, public, s-maxage=100', $bag->get('cache-control'));
}
public function testCacheControlDirectiveParsingQuotedZero()
{
$bag = new HeaderBag(array('cache-control' => 'max-age="0"'));
$this->assertTrue($bag->hasCacheControlDirective('max-age'));
$this->assertEquals(0, $bag->getCacheControlDirective('max-age'));
}
public function testCacheControlDirectiveOverrideWithReplace()
{
$bag = new HeaderBag(array('cache-control' => 'private, max-age=100'));
$bag->replace(array('cache-control' => 'public, max-age=10'));
$this->assertTrue($bag->hasCacheControlDirective('public'));
$this->assertTrue($bag->getCacheControlDirective('public'));
$this->assertTrue($bag->hasCacheControlDirective('max-age'));
$this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
}
/**
* @covers Symfony\Component\HttpFoundation\HeaderBag::getIterator
*/
public function testGetIterator()
{
$headers = array('foo' => 'bar', 'hello' => 'world', 'third' => 'charm');
$headerBag = new HeaderBag($headers);
$i = 0;
foreach ($headerBag as $key => $val) {
$i++;
$this->assertEquals(array($headers[$key]), $val);
}
$this->assertEquals(count($headers), $i);
}
/**
* @covers Symfony\Component\HttpFoundation\HeaderBag::count
*/
public function testCount()
{
$headers = array('foo' => 'bar', 'HELLO' => 'WORLD');
$headerBag = new HeaderBag($headers);
$this->assertEquals(count($headers), count($headerBag));
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests;
use Symfony\Component\HttpFoundation\IpUtils;
class IpUtilsTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider testIpv4Provider
*/
public function testIpv4($matches, $remoteAddr, $cidr)
{
$this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
}
public function testIpv4Provider()
{
return array(
array(true, '192.168.1.1', '192.168.1.1'),
array(true, '192.168.1.1', '192.168.1.1/1'),
array(true, '192.168.1.1', '192.168.1.0/24'),
array(false, '192.168.1.1', '1.2.3.4/1'),
array(false, '192.168.1.1', '192.168.1/33'),
array(true, '192.168.1.1', array('1.2.3.4/1', '192.168.1.0/24')),
array(true, '192.168.1.1', array('192.168.1.0/24', '1.2.3.4/1')),
array(false, '192.168.1.1', array('1.2.3.4/1', '4.3.2.1/1')),
);
}
/**
* @dataProvider testIpv6Provider
*/
public function testIpv6($matches, $remoteAddr, $cidr)
{
if (!defined('AF_INET6')) {
$this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".');
}
$this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
}
public function testIpv6Provider()
{
return array(
array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
array(true, '0:0:0:0:0:0:0:1', '::1'),
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '2a01:198:603:0::/65')),
array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('2a01:198:603:0::/65', '::1')),
array(false, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '1a01:198:603:0::/65')),
);
}
/**
* @expectedException \RuntimeException
*/
public function testAnIpv6WithOptionDisabledIpv6()
{
if (!extension_loaded('sockets')) {
$this->markTestSkipped('Only works when the socket extension is enabled');
}
if (defined('AF_INET6')) {
$this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
}
IpUtils::checkIp('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65');
}
}

View File

@@ -0,0 +1,176 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\JsonResponse;
class JsonResponseTest extends \PHPUnit_Framework_TestCase
{
public function testConstructorEmptyCreatesJsonObject()
{
$response = new JsonResponse();
$this->assertSame('{}', $response->getContent());
}
public function testConstructorWithArrayCreatesJsonArray()
{
$response = new JsonResponse(array(0, 1, 2, 3));
$this->assertSame('[0,1,2,3]', $response->getContent());
}
public function testConstructorWithAssocArrayCreatesJsonObject()
{
$response = new JsonResponse(array('foo' => 'bar'));
$this->assertSame('{"foo":"bar"}', $response->getContent());
}
public function testConstructorWithSimpleTypes()
{
$response = new JsonResponse('foo');
$this->assertSame('"foo"', $response->getContent());
$response = new JsonResponse(0);
$this->assertSame('0', $response->getContent());
$response = new JsonResponse(0.1);
$this->assertSame('0.1', $response->getContent());
$response = new JsonResponse(true);
$this->assertSame('true', $response->getContent());
}
public function testConstructorWithCustomStatus()
{
$response = new JsonResponse(array(), 202);
$this->assertSame(202, $response->getStatusCode());
}
public function testConstructorAddsContentTypeHeader()
{
$response = new JsonResponse();
$this->assertSame('application/json', $response->headers->get('Content-Type'));
}
public function testConstructorWithCustomHeaders()
{
$response = new JsonResponse(array(), 200, array('ETag' => 'foo'));
$this->assertSame('application/json', $response->headers->get('Content-Type'));
$this->assertSame('foo', $response->headers->get('ETag'));
}
public function testConstructorWithCustomContentType()
{
$headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json');
$response = new JsonResponse(array(), 200, $headers);
$this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
}
public function testCreate()
{
$response = JsonResponse::create(array('foo' => 'bar'), 204);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertEquals('{"foo":"bar"}', $response->getContent());
$this->assertEquals(204, $response->getStatusCode());
}
public function testStaticCreateEmptyJsonObject()
{
$response = JsonResponse::create();
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('{}', $response->getContent());
}
public function testStaticCreateJsonArray()
{
$response = JsonResponse::create(array(0, 1, 2, 3));
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('[0,1,2,3]', $response->getContent());
}
public function testStaticCreateJsonObject()
{
$response = JsonResponse::create(array('foo' => 'bar'));
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('{"foo":"bar"}', $response->getContent());
}
public function testStaticCreateWithSimpleTypes()
{
$response = JsonResponse::create('foo');
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('"foo"', $response->getContent());
$response = JsonResponse::create(0);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('0', $response->getContent());
$response = JsonResponse::create(0.1);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('0.1', $response->getContent());
$response = JsonResponse::create(true);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('true', $response->getContent());
}
public function testStaticCreateWithCustomStatus()
{
$response = JsonResponse::create(array(), 202);
$this->assertSame(202, $response->getStatusCode());
}
public function testStaticCreateAddsContentTypeHeader()
{
$response = JsonResponse::create();
$this->assertSame('application/json', $response->headers->get('Content-Type'));
}
public function testStaticCreateWithCustomHeaders()
{
$response = JsonResponse::create(array(), 200, array('ETag' => 'foo'));
$this->assertSame('application/json', $response->headers->get('Content-Type'));
$this->assertSame('foo', $response->headers->get('ETag'));
}
public function testStaticCreateWithCustomContentType()
{
$headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json');
$response = JsonResponse::create(array(), 200, $headers);
$this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
}
public function testSetCallback()
{
$response = JsonResponse::create(array('foo' => 'bar'))->setCallback('callback');
$this->assertEquals('callback({"foo":"bar"});', $response->getContent());
$this->assertEquals('text/javascript', $response->headers->get('Content-Type'));
}
public function testSetCallbackInvalidIdentifier()
{
$response = new JsonResponse('foo');
$this->setExpectedException('InvalidArgumentException');
$response->setCallback('+invalid');
}
public function testJsonEncodeFlags()
{
$response = new JsonResponse('<>\'&"');
$this->assertEquals('"\u003C\u003E\u0027\u0026\u0022"', $response->getContent());
}
}

View File

@@ -0,0 +1,254 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\ParameterBag;
class ParameterBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::__construct
*/
public function testConstructor()
{
$this->testAll();
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::all
*/
public function testAll()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$this->assertEquals(array('foo' => 'bar'), $bag->all(), '->all() gets all the input');
}
public function testKeys()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$this->assertEquals(array('foo'), $bag->keys());
}
public function testAdd()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$bag->add(array('bar' => 'bas'));
$this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all());
}
public function testRemove()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$bag->add(array('bar' => 'bas'));
$this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all());
$bag->remove('bar');
$this->assertEquals(array('foo' => 'bar'), $bag->all());
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::replace
*/
public function testReplace()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$bag->replace(array('FOO' => 'BAR'));
$this->assertEquals(array('FOO' => 'BAR'), $bag->all(), '->replace() replaces the input with the argument');
$this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input');
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::get
*/
public function testGet()
{
$bag = new ParameterBag(array('foo' => 'bar', 'null' => null));
$this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter');
$this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined');
$this->assertNull($bag->get('null', 'default'), '->get() returns null if null is set');
}
public function testGetDoesNotUseDeepByDefault()
{
$bag = new ParameterBag(array('foo' => array('bar' => 'moo')));
$this->assertNull($bag->get('foo[bar]'));
}
/**
* @dataProvider getInvalidPaths
* @expectedException \InvalidArgumentException
*/
public function testGetDeepWithInvalidPaths($path)
{
$bag = new ParameterBag(array('foo' => array('bar' => 'moo')));
$bag->get($path, null, true);
}
public function getInvalidPaths()
{
return array(
array('foo[['),
array('foo[d'),
array('foo[bar]]'),
array('foo[bar]d'),
);
}
public function testGetDeep()
{
$bag = new ParameterBag(array('foo' => array('bar' => array('moo' => 'boo'))));
$this->assertEquals(array('moo' => 'boo'), $bag->get('foo[bar]', null, true));
$this->assertEquals('boo', $bag->get('foo[bar][moo]', null, true));
$this->assertEquals('default', $bag->get('foo[bar][foo]', 'default', true));
$this->assertEquals('default', $bag->get('bar[moo][foo]', 'default', true));
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::set
*/
public function testSet()
{
$bag = new ParameterBag(array());
$bag->set('foo', 'bar');
$this->assertEquals('bar', $bag->get('foo'), '->set() sets the value of parameter');
$bag->set('foo', 'baz');
$this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter');
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::has
*/
public function testHas()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined');
$this->assertFalse($bag->has('unknown'), '->has() return false if a parameter is not defined');
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::getAlpha
*/
public function testGetAlpha()
{
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
$this->assertEquals('fooBAR', $bag->getAlpha('word'), '->getAlpha() gets only alphabetic characters');
$this->assertEquals('', $bag->getAlpha('unknown'), '->getAlpha() returns empty string if a parameter is not defined');
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::getAlnum
*/
public function testGetAlnum()
{
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
$this->assertEquals('fooBAR012', $bag->getAlnum('word'), '->getAlnum() gets only alphanumeric characters');
$this->assertEquals('', $bag->getAlnum('unknown'), '->getAlnum() returns empty string if a parameter is not defined');
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::getDigits
*/
public function testGetDigits()
{
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
$this->assertEquals('012', $bag->getDigits('word'), '->getDigits() gets only digits as string');
$this->assertEquals('', $bag->getDigits('unknown'), '->getDigits() returns empty string if a parameter is not defined');
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::getInt
*/
public function testGetInt()
{
$bag = new ParameterBag(array('digits' => '0123'));
$this->assertEquals(123, $bag->getInt('digits'), '->getInt() gets a value of parameter as integer');
$this->assertEquals(0, $bag->getInt('unknown'), '->getInt() returns zero if a parameter is not defined');
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::filter
*/
public function testFilter()
{
$bag = new ParameterBag(array(
'digits' => '0123ab',
'email' => 'example@example.com',
'url' => 'http://example.com/foo',
'dec' => '256',
'hex' => '0x100',
'array' => array('bang'),
));
$this->assertEmpty($bag->filter('nokey'), '->filter() should return empty by default if no key is found');
$this->assertEquals('0123', $bag->filter('digits', '', false, FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters');
$this->assertEquals('example@example.com', $bag->filter('email', '', false, FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email');
$this->assertEquals('http://example.com/foo', $bag->filter('url', '', false, FILTER_VALIDATE_URL, array('flags' => FILTER_FLAG_PATH_REQUIRED)), '->filter() gets a value of parameter as url with a path');
// This test is repeated for code-coverage
$this->assertEquals('http://example.com/foo', $bag->filter('url', '', false, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as url with a path');
$this->assertFalse($bag->filter('dec', '', false, FILTER_VALIDATE_INT, array(
'flags' => FILTER_FLAG_ALLOW_HEX,
'options' => array('min_range' => 1, 'max_range' => 0xff))
), '->filter() gets a value of parameter as integer between boundaries');
$this->assertFalse($bag->filter('hex', '', false, FILTER_VALIDATE_INT, array(
'flags' => FILTER_FLAG_ALLOW_HEX,
'options' => array('min_range' => 1, 'max_range' => 0xff))
), '->filter() gets a value of parameter as integer between boundaries');
$this->assertEquals(array('bang'), $bag->filter('array', '', false), '->filter() gets a value of parameter as an array');
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::getIterator
*/
public function testGetIterator()
{
$parameters = array('foo' => 'bar', 'hello' => 'world');
$bag = new ParameterBag($parameters);
$i = 0;
foreach ($bag as $key => $val) {
$i++;
$this->assertEquals($parameters[$key], $val);
}
$this->assertEquals(count($parameters), $i);
}
/**
* @covers Symfony\Component\HttpFoundation\ParameterBag::count
*/
public function testCount()
{
$parameters = array('foo' => 'bar', 'hello' => 'world');
$bag = new ParameterBag($parameters);
$this->assertEquals(count($parameters), count($bag));
}
}

View File

@@ -0,0 +1,83 @@
<?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\Tests;
use \Symfony\Component\HttpFoundation\RedirectResponse;
class RedirectResponseTest extends \PHPUnit_Framework_TestCase
{
public function testGenerateMetaRedirect()
{
$response = new RedirectResponse('foo.bar');
$this->assertEquals(1, preg_match(
'#<meta http-equiv="refresh" content="\d+;url=foo\.bar" />#',
preg_replace(array('/\s+/', '/\'/'), array(' ', '"'), $response->getContent())
));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRedirectResponseConstructorNullUrl()
{
$response = new RedirectResponse(null);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRedirectResponseConstructorWrongStatusCode()
{
$response = new RedirectResponse('foo.bar', 404);
}
public function testGenerateLocationHeader()
{
$response = new RedirectResponse('foo.bar');
$this->assertTrue($response->headers->has('Location'));
$this->assertEquals('foo.bar', $response->headers->get('Location'));
}
public function testGetTargetUrl()
{
$response = new RedirectResponse('foo.bar');
$this->assertEquals('foo.bar', $response->getTargetUrl());
}
public function testSetTargetUrl()
{
$response = new RedirectResponse('foo.bar');
$response->setTargetUrl('baz.beep');
$this->assertEquals('baz.beep', $response->getTargetUrl());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testSetTargetUrlNull()
{
$response = new RedirectResponse('foo.bar');
$response->setTargetUrl(null);
}
public function testCreate()
{
$response = RedirectResponse::create('foo', 301);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
$this->assertEquals(301, $response->getStatusCode());
}
}

View File

@@ -0,0 +1,130 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests;
use Symfony\Component\HttpFoundation\RequestMatcher;
use Symfony\Component\HttpFoundation\Request;
class RequestMatcherTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider testMethodFixtures
*/
public function testMethod($requestMethod, $matcherMethod, $isMatch)
{
$matcher = new RequestMatcher();
$matcher->matchMethod($matcherMethod);
$request = Request::create('', $requestMethod);
$this->assertSame($isMatch, $matcher->matches($request));
$matcher = new RequestMatcher(null, null, $matcherMethod);
$request = Request::create('', $requestMethod);
$this->assertSame($isMatch, $matcher->matches($request));
}
public function testMethodFixtures()
{
return array(
array('get', 'get', true),
array('get', array('get', 'post'), true),
array('get', 'post', false),
array('get', 'GET', true),
array('get', array('GET', 'POST'), true),
array('get', 'POST', false),
);
}
/**
* @dataProvider testHostFixture
*/
public function testHost($pattern, $isMatch)
{
$matcher = new RequestMatcher();
$request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com'));
$matcher->matchHost($pattern);
$this->assertSame($isMatch, $matcher->matches($request));
$matcher= new RequestMatcher(null, $pattern);
$this->assertSame($isMatch, $matcher->matches($request));
}
public function testHostFixture()
{
return array(
array('.*\.example\.com', true),
array('\.example\.com$', true),
array('^.*\.example\.com$', true),
array('.*\.sensio\.com', false),
array('.*\.example\.COM', true),
array('\.example\.COM$', true),
array('^.*\.example\.COM$', true),
array('.*\.sensio\.COM', false), );
}
public function testPath()
{
$matcher = new RequestMatcher();
$request = Request::create('/admin/foo');
$matcher->matchPath('/admin/.*');
$this->assertTrue($matcher->matches($request));
$matcher->matchPath('/admin');
$this->assertTrue($matcher->matches($request));
$matcher->matchPath('^/admin/.*$');
$this->assertTrue($matcher->matches($request));
$matcher->matchMethod('/blog/.*');
$this->assertFalse($matcher->matches($request));
}
public function testPathWithLocaleIsNotSupported()
{
$matcher = new RequestMatcher();
$request = Request::create('/en/login');
$request->setLocale('en');
$matcher->matchPath('^/{_locale}/login$');
$this->assertFalse($matcher->matches($request));
}
public function testPathWithEncodedCharacters()
{
$matcher = new RequestMatcher();
$request = Request::create('/admin/fo%20o');
$matcher->matchPath('^/admin/fo o*$');
$this->assertTrue($matcher->matches($request));
}
public function testAttributes()
{
$matcher = new RequestMatcher();
$request = Request::create('/admin/foo');
$request->attributes->set('foo', 'foo_bar');
$matcher->matchAttribute('foo', 'foo_.*');
$this->assertTrue($matcher->matches($request));
$matcher->matchAttribute('foo', 'foo');
$this->assertTrue($matcher->matches($request));
$matcher->matchAttribute('foo', '^foo_bar$');
$this->assertTrue($matcher->matches($request));
$matcher->matchAttribute('foo', 'babar');
$this->assertFalse($matcher->matches($request));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,286 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\Cookie;
class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Symfony\Component\HttpFoundation\ResponseHeaderBag::allPreserveCase
* @dataProvider provideAllPreserveCase
*/
public function testAllPreserveCase($headers, $expected)
{
$bag = new ResponseHeaderBag($headers);
$this->assertEquals($expected, $bag->allPreserveCase(), '->allPreserveCase() gets all input keys in original case');
}
public function provideAllPreserveCase()
{
return array(
array(
array('fOo' => 'BAR'),
array('fOo' => array('BAR'), 'Cache-Control' => array('no-cache'))
),
array(
array('ETag' => 'xyzzy'),
array('ETag' => array('xyzzy'), 'Cache-Control' => array('private, must-revalidate'))
),
array(
array('Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ=='),
array('Content-MD5' => array('Q2hlY2sgSW50ZWdyaXR5IQ=='), 'Cache-Control' => array('no-cache'))
),
array(
array('P3P' => 'CP="CAO PSA OUR"'),
array('P3P' => array('CP="CAO PSA OUR"'), 'Cache-Control' => array('no-cache'))
),
array(
array('WWW-Authenticate' => 'Basic realm="WallyWorld"'),
array('WWW-Authenticate' => array('Basic realm="WallyWorld"'), 'Cache-Control' => array('no-cache'))
),
array(
array('X-UA-Compatible' => 'IE=edge,chrome=1'),
array('X-UA-Compatible' => array('IE=edge,chrome=1'), 'Cache-Control' => array('no-cache'))
),
array(
array('X-XSS-Protection' => '1; mode=block'),
array('X-XSS-Protection' => array('1; mode=block'), 'Cache-Control' => array('no-cache'))
),
);
}
public function testCacheControlHeader()
{
$bag = new ResponseHeaderBag(array());
$this->assertEquals('no-cache', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
$bag = new ResponseHeaderBag(array('Cache-Control' => 'public'));
$this->assertEquals('public', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('public'));
$bag = new ResponseHeaderBag(array('ETag' => 'abcde'));
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('private'));
$this->assertTrue($bag->hasCacheControlDirective('must-revalidate'));
$this->assertFalse($bag->hasCacheControlDirective('max-age'));
$bag = new ResponseHeaderBag(array('Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT'));
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array(
'Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT',
'Cache-Control' => 'max-age=3600'
));
$this->assertEquals('max-age=3600, private', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('Last-Modified' => 'abcde'));
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('Etag' => 'abcde', 'Last-Modified' => 'abcde'));
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('cache-control' => 'max-age=100'));
$this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('cache-control' => 's-maxage=100'));
$this->assertEquals('s-maxage=100', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('cache-control' => 'private, max-age=100'));
$this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('cache-control' => 'public, max-age=100'));
$this->assertEquals('max-age=100, public', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag();
$bag->set('Last-Modified', 'abcde');
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
}
public function testToStringIncludesCookieHeaders()
{
$bag = new ResponseHeaderBag(array());
$bag->setCookie(new Cookie('foo', 'bar'));
$this->assertContains("Set-Cookie: foo=bar; path=/; httponly", explode("\r\n", $bag->__toString()));
$bag->clearCookie('foo');
$this->assertContains("Set-Cookie: foo=deleted; expires=".gmdate("D, d-M-Y H:i:s T", time() - 31536001)."; path=/; httponly", explode("\r\n", $bag->__toString()));
}
public function testReplace()
{
$bag = new ResponseHeaderBag(array());
$this->assertEquals('no-cache', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
$bag->replace(array('Cache-Control' => 'public'));
$this->assertEquals('public', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('public'));
}
public function testReplaceWithRemove()
{
$bag = new ResponseHeaderBag(array());
$this->assertEquals('no-cache', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
$bag->remove('Cache-Control');
$bag->replace(array());
$this->assertEquals('no-cache', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
}
public function testCookiesWithSameNames()
{
$bag = new ResponseHeaderBag();
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar'));
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo'));
$bag->setCookie(new Cookie('foo', 'bar'));
$this->assertCount(4, $bag->getCookies());
$headers = explode("\r\n", $bag->__toString());
$this->assertContains("Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly", $headers);
$this->assertContains("Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly", $headers);
$this->assertContains("Set-Cookie: foo=bar; path=/path/bar; domain=bar.foo; httponly", $headers);
$this->assertContains("Set-Cookie: foo=bar; path=/; httponly", $headers);
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertTrue(isset($cookies['foo.bar']['/path/foo']['foo']));
$this->assertTrue(isset($cookies['foo.bar']['/path/bar']['foo']));
$this->assertTrue(isset($cookies['bar.foo']['/path/bar']['foo']));
$this->assertTrue(isset($cookies['']['/']['foo']));
}
public function testRemoveCookie()
{
$bag = new ResponseHeaderBag();
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
$bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar'));
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertTrue(isset($cookies['foo.bar']['/path/foo']));
$bag->removeCookie('foo', '/path/foo', 'foo.bar');
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertFalse(isset($cookies['foo.bar']['/path/foo']));
$bag->removeCookie('bar', '/path/bar', 'foo.bar');
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertFalse(isset($cookies['foo.bar']));
}
public function testRemoveCookieWithNullRemove()
{
$bag = new ResponseHeaderBag();
$bag->setCookie(new Cookie('foo', 'bar', 0));
$bag->setCookie(new Cookie('bar', 'foo', 0));
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertTrue(isset($cookies['']['/']));
$bag->removeCookie('foo', null);
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertFalse(isset($cookies['']['/']['foo']));
$bag->removeCookie('bar', null);
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertFalse(isset($cookies['']['/']['bar']));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testGetCookiesWithInvalidArgument()
{
$bag = new ResponseHeaderBag();
$cookies = $bag->getCookies('invalid_argument');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionInvalidDisposition()
{
$headers = new ResponseHeaderBag();
$headers->makeDisposition('invalid', 'foo.html');
}
/**
* @dataProvider provideMakeDisposition
*/
public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
{
$headers = new ResponseHeaderBag();
$this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback));
}
public function testToStringDoesntMessUpHeaders()
{
$headers = new ResponseHeaderBag();
$headers->set('Location', 'http://www.symfony.com');
$headers->set('Content-type', 'text/html');
(string) $headers;
$allHeaders = $headers->allPreserveCase();
$this->assertEquals(array('http://www.symfony.com'), $allHeaders['Location']);
$this->assertEquals(array('text/html'), $allHeaders['Content-type']);
}
public function provideMakeDisposition()
{
return array(
array('attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'),
array('attachment', 'foo.html', '', 'attachment; filename="foo.html"'),
array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'),
array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'),
array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'),
array('attachment', 'föö.html', 'foo.html', 'attachment; filename="foo.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html'),
);
}
/**
* @dataProvider provideMakeDispositionFail
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionFail($disposition, $filename)
{
$headers = new ResponseHeaderBag();
$headers->makeDisposition($disposition, $filename);
}
public function provideMakeDispositionFail()
{
return array(
array('attachment', 'foo%20bar.html'),
array('attachment', 'foo/bar.html'),
array('attachment', '/foo.html'),
array('attachment', 'foo\bar.html'),
array('attachment', '\foo.html'),
array('attachment', 'föö.html'),
);
}
}

View File

@@ -0,0 +1,717 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ResponseTest extends ResponseTestCase
{
public function testCreate()
{
$response = Response::create('foo', 301, array('Foo' => 'bar'));
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
$this->assertEquals(301, $response->getStatusCode());
$this->assertEquals('bar', $response->headers->get('foo'));
}
public function testToString()
{
$response = new Response();
$response = explode("\r\n", $response);
$this->assertEquals("HTTP/1.0 200 OK", $response[0]);
$this->assertEquals("Cache-Control: no-cache", $response[1]);
}
public function testClone()
{
$response = new Response();
$responseClone = clone $response;
$this->assertEquals($response, $responseClone);
}
public function testSendHeaders()
{
$response = new Response();
$headers = $response->sendHeaders();
$this->assertObjectHasAttribute('headers', $headers);
$this->assertObjectHasAttribute('content', $headers);
$this->assertObjectHasAttribute('version', $headers);
$this->assertObjectHasAttribute('statusCode', $headers);
$this->assertObjectHasAttribute('statusText', $headers);
$this->assertObjectHasAttribute('charset', $headers);
}
public function testSend()
{
$response = new Response();
$responseSend = $response->send();
$this->assertObjectHasAttribute('headers', $responseSend);
$this->assertObjectHasAttribute('content', $responseSend);
$this->assertObjectHasAttribute('version', $responseSend);
$this->assertObjectHasAttribute('statusCode', $responseSend);
$this->assertObjectHasAttribute('statusText', $responseSend);
$this->assertObjectHasAttribute('charset', $responseSend);
}
public function testGetCharset()
{
$response = new Response();
$charsetOrigin = 'UTF-8';
$response->setCharset($charsetOrigin);
$charset = $response->getCharset();
$this->assertEquals($charsetOrigin, $charset);
}
public function testIsCacheable()
{
$response = new Response();
$this->assertFalse($response->isCacheable());
}
public function testIsCacheableWithSetTtl()
{
$response = new Response();
$response->setTtl(10);
$this->assertTrue($response->isCacheable());
}
public function testMustRevalidate()
{
$response = new Response();
$this->assertFalse($response->mustRevalidate());
}
public function testSetNotModified()
{
$response = new Response();
$modified = $response->setNotModified();
$this->assertObjectHasAttribute('headers', $modified);
$this->assertObjectHasAttribute('content', $modified);
$this->assertObjectHasAttribute('version', $modified);
$this->assertObjectHasAttribute('statusCode', $modified);
$this->assertObjectHasAttribute('statusText', $modified);
$this->assertObjectHasAttribute('charset', $modified);
$this->assertEquals(304, $modified->getStatusCode());
}
public function testIsSuccessful()
{
$response = new Response();
$this->assertTrue($response->isSuccessful());
}
public function testIsNotModified()
{
$response = new Response();
$modified = $response->isNotModified(new Request());
$this->assertFalse($modified);
}
public function testIsValidateable()
{
$response = new Response('', 200, array('Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
$this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present');
$response = new Response('', 200, array('ETag' => '"12345"'));
$this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present');
$response = new Response();
$this->assertFalse($response->isValidateable(), '->isValidateable() returns false when no validator is present');
}
public function testGetDate()
{
$response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
$this->assertEquals(0, $this->createDateTimeOneHourAgo()->diff($response->getDate())->format('%s'), '->getDate() returns the Date header if present');
$response = new Response();
$date = $response->getDate();
$this->assertLessThan(1, $date->diff(new \DateTime(), true)->format('%s'), '->getDate() returns the current Date if no Date header present');
$response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
$now = $this->createDateTimeNow();
$response->headers->set('Date', $now->format(DATE_RFC2822));
$this->assertEquals(0, $now->diff($response->getDate())->format('%s'), '->getDate() returns the date when the header has been modified');
$response = new Response('', 200);
$response->headers->remove('Date');
$this->assertInstanceOf('\DateTime', $response->getDate());
}
public function testGetMaxAge()
{
$response = new Response();
$response->headers->set('Cache-Control', 's-maxage=600, max-age=0');
$this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() uses s-maxage cache control directive when present');
$response = new Response();
$response->headers->set('Cache-Control', 'max-age=600');
$this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() falls back to max-age when no s-maxage directive present');
$response = new Response();
$response->headers->set('Cache-Control', 'must-revalidate');
$response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
$this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present');
$response = new Response();
$response->headers->set('Cache-Control', 'must-revalidate');
$response->headers->set('Expires', -1);
$this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822));
$response = new Response();
$this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available');
}
public function testSetSharedMaxAge()
{
$response = new Response();
$response->setSharedMaxAge(20);
$cacheControl = $response->headers->get('Cache-Control');
$this->assertEquals('public, s-maxage=20', $cacheControl);
}
public function testIsPrivate()
{
$response = new Response();
$response->headers->set('Cache-Control', 'max-age=100');
$response->setPrivate();
$this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true');
$this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true');
$response = new Response();
$response->headers->set('Cache-Control', 'public, max-age=100');
$response->setPrivate();
$this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true');
$this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true');
$this->assertFalse($response->headers->hasCacheControlDirective('public'), '->isPrivate() removes the public Cache-Control directive');
}
public function testExpire()
{
$response = new Response();
$response->headers->set('Cache-Control', 'max-age=100');
$response->expire();
$this->assertEquals(100, $response->headers->get('Age'), '->expire() sets the Age to max-age when present');
$response = new Response();
$response->headers->set('Cache-Control', 'max-age=100, s-maxage=500');
$response->expire();
$this->assertEquals(500, $response->headers->get('Age'), '->expire() sets the Age to s-maxage when both max-age and s-maxage are present');
$response = new Response();
$response->headers->set('Cache-Control', 'max-age=5, s-maxage=500');
$response->headers->set('Age', '1000');
$response->expire();
$this->assertEquals(1000, $response->headers->get('Age'), '->expire() does nothing when the response is already stale/expired');
$response = new Response();
$response->expire();
$this->assertFalse($response->headers->has('Age'), '->expire() does nothing when the response does not include freshness information');
$response = new Response();
$response->headers->set('Expires', -1);
$response->expire();
$this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired');
}
public function testGetTtl()
{
$response = new Response();
$this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present');
$response = new Response();
$response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
$this->assertLessThan(1, 3600 - $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present');
$response = new Response();
$response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822));
$this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past');
$response = new Response();
$response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822));
$response->headers->set('Age', 0);
$this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero');
$response = new Response();
$response->headers->set('Cache-Control', 'max-age=60');
$this->assertLessThan(1, 60 - $response->getTtl(), '->getTtl() uses Cache-Control max-age when present');
}
public function testSetClientTtl()
{
$response = new Response();
$response->setClientTtl(10);
$this->assertEquals($response->getMaxAge(), $response->getAge() + 10);
}
public function testGetSetProtocolVersion()
{
$response = new Response();
$this->assertEquals('1.0', $response->getProtocolVersion());
$response->setProtocolVersion('1.1');
$this->assertEquals('1.1', $response->getProtocolVersion());
}
public function testGetVary()
{
$response = new Response();
$this->assertEquals(array(), $response->getVary(), '->getVary() returns an empty array if no Vary header is present');
$response = new Response();
$response->headers->set('Vary', 'Accept-Language');
$this->assertEquals(array('Accept-Language'), $response->getVary(), '->getVary() parses a single header name value');
$response = new Response();
$response->headers->set('Vary', 'Accept-Language User-Agent X-Foo');
$this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by spaces');
$response = new Response();
$response->headers->set('Vary', 'Accept-Language,User-Agent, X-Foo');
$this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas');
}
public function testSetVary()
{
$response = new Response();
$response->setVary('Accept-Language');
$this->assertEquals(array('Accept-Language'), $response->getVary());
$response->setVary('Accept-Language, User-Agent');
$this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default');
$response->setVary('X-Foo', false);
$this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() doesn\'t change the Vary header if replace is set to false');
}
public function testDefaultContentType()
{
$headerMock = $this->getMock('Symfony\Component\HttpFoundation\ResponseHeaderBag', array('set'));
$headerMock->expects($this->at(0))
->method('set')
->with('Content-Type', 'text/html');
$headerMock->expects($this->at(1))
->method('set')
->with('Content-Type', 'text/html; charset=UTF-8');
$response = new Response('foo');
$response->headers = $headerMock;
$response->prepare(new Request());
}
public function testContentTypeCharset()
{
$response = new Response();
$response->headers->set('Content-Type', 'text/css');
// force fixContentType() to be called
$response->prepare(new Request());
$this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type'));
}
public function testPrepareDoesNothingIfContentTypeIsSet()
{
$response = new Response('foo');
$response->headers->set('Content-Type', 'text/plain');
$response->prepare(new Request());
$this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type'));
}
public function testPrepareDoesNothingIfRequestFormatIsNotDefined()
{
$response = new Response('foo');
$response->prepare(new Request());
$this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type'));
}
public function testPrepareSetContentType()
{
$response = new Response('foo');
$request = Request::create('/');
$request->setRequestFormat('json');
$response->prepare($request);
$this->assertEquals('application/json', $response->headers->get('content-type'));
}
public function testPrepareRemovesContentForHeadRequests()
{
$response = new Response('foo');
$request = Request::create('/', 'HEAD');
$response->prepare($request);
$this->assertEquals('', $response->getContent());
}
public function testPrepareSetsPragmaOnHttp10Only()
{
$request = Request::create('/', 'GET');
$request->server->set('SERVER_PROTOCOL', 'HTTP/1.0');
$response = new Response('foo');
$response->prepare($request);
$this->assertEquals('no-cache', $response->headers->get('pragma'));
$this->assertEquals('-1', $response->headers->get('expires'));
$request->server->set('SERVER_PROTOCOL', 'HTTP/1.1');
$response = new Response('foo');
$response->prepare($request);
$this->assertFalse($response->headers->has('pragma'));
$this->assertFalse($response->headers->has('expires'));
}
public function testSetCache()
{
$response = new Response();
//array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public')
try {
$response->setCache(array("wrong option" => "value"));
$this->fail('->setCache() throws an InvalidArgumentException if an option is not supported');
} catch (\Exception $e) {
$this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported');
$this->assertContains('"wrong option"', $e->getMessage());
}
$options = array('etag' => '"whatever"');
$response->setCache($options);
$this->assertEquals($response->getEtag(), '"whatever"');
$now = new \DateTime();
$options = array('last_modified' => $now);
$response->setCache($options);
$this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp());
$options = array('max_age' => 100);
$response->setCache($options);
$this->assertEquals($response->getMaxAge(), 100);
$options = array('s_maxage' => 200);
$response->setCache($options);
$this->assertEquals($response->getMaxAge(), 200);
$this->assertTrue($response->headers->hasCacheControlDirective('public'));
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
$response->setCache(array('public' => true));
$this->assertTrue($response->headers->hasCacheControlDirective('public'));
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
$response->setCache(array('public' => false));
$this->assertFalse($response->headers->hasCacheControlDirective('public'));
$this->assertTrue($response->headers->hasCacheControlDirective('private'));
$response->setCache(array('private' => true));
$this->assertFalse($response->headers->hasCacheControlDirective('public'));
$this->assertTrue($response->headers->hasCacheControlDirective('private'));
$response->setCache(array('private' => false));
$this->assertTrue($response->headers->hasCacheControlDirective('public'));
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
}
public function testSendContent()
{
$response = new Response('test response rendering', 200);
ob_start();
$response->sendContent();
$string = ob_get_clean();
$this->assertContains('test response rendering', $string);
}
public function testSetPublic()
{
$response = new Response();
$response->setPublic();
$this->assertTrue($response->headers->hasCacheControlDirective('public'));
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
}
public function testSetExpires()
{
$response = new Response();
$response->setExpires(null);
$this->assertNull($response->getExpires(), '->setExpires() remove the header when passed null');
$now = new \DateTime();
$response->setExpires($now);
$this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp());
}
public function testSetLastModified()
{
$response = new Response();
$response->setLastModified(new \DateTime());
$this->assertNotNull($response->getLastModified());
$response->setLastModified(null);
$this->assertNull($response->getLastModified());
}
public function testIsInvalid()
{
$response = new Response();
try {
$response->setStatusCode(99);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertTrue($response->isInvalid());
}
try {
$response->setStatusCode(650);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertTrue($response->isInvalid());
}
$response = new Response('', 200);
$this->assertFalse($response->isInvalid());
}
/**
* @dataProvider getStatusCodeFixtures
*/
public function testSetStatusCode($code, $text, $expectedText)
{
$response = new Response();
$response->setStatusCode($code, $text);
$statusText = new \ReflectionProperty($response, 'statusText');
$statusText->setAccessible(true);
$this->assertEquals($expectedText, $statusText->getValue($response));
}
public function getStatusCodeFixtures()
{
return array(
array('200', null, 'OK'),
array('200', false, ''),
array('200', 'foo', 'foo'),
array('199', null, ''),
array('199', false, ''),
array('199', 'foo', 'foo')
);
}
public function testIsInformational()
{
$response = new Response('', 100);
$this->assertTrue($response->isInformational());
$response = new Response('', 200);
$this->assertFalse($response->isInformational());
}
public function testIsRedirectRedirection()
{
foreach (array(301, 302, 303, 307) as $code) {
$response = new Response('', $code);
$this->assertTrue($response->isRedirection());
$this->assertTrue($response->isRedirect());
}
$response = new Response('', 304);
$this->assertTrue($response->isRedirection());
$this->assertFalse($response->isRedirect());
$response = new Response('', 200);
$this->assertFalse($response->isRedirection());
$this->assertFalse($response->isRedirect());
$response = new Response('', 404);
$this->assertFalse($response->isRedirection());
$this->assertFalse($response->isRedirect());
$response = new Response('', 301, array('Location' => '/good-uri'));
$this->assertFalse($response->isRedirect('/bad-uri'));
$this->assertTrue($response->isRedirect('/good-uri'));
}
public function testIsNotFound()
{
$response = new Response('', 404);
$this->assertTrue($response->isNotFound());
$response = new Response('', 200);
$this->assertFalse($response->isNotFound());
}
public function testIsEmpty()
{
foreach (array(201, 204, 304) as $code) {
$response = new Response('', $code);
$this->assertTrue($response->isEmpty());
}
$response = new Response('', 200);
$this->assertFalse($response->isEmpty());
}
public function testIsForbidden()
{
$response = new Response('', 403);
$this->assertTrue($response->isForbidden());
$response = new Response('', 200);
$this->assertFalse($response->isForbidden());
}
public function testIsOk()
{
$response = new Response('', 200);
$this->assertTrue($response->isOk());
$response = new Response('', 404);
$this->assertFalse($response->isOk());
}
public function testIsServerOrClientError()
{
$response = new Response('', 404);
$this->assertTrue($response->isClientError());
$this->assertFalse($response->isServerError());
$response = new Response('', 500);
$this->assertFalse($response->isClientError());
$this->assertTrue($response->isServerError());
}
public function testHasVary()
{
$response = new Response();
$this->assertFalse($response->hasVary());
$response->setVary('User-Agent');
$this->assertTrue($response->hasVary());
}
public function testSetEtag()
{
$response = new Response('', 200, array('ETag' => '"12345"'));
$response->setEtag();
$this->assertNull($response->headers->get('Etag'), '->setEtag() removes Etags when call with null');
}
/**
* @dataProvider validContentProvider
*/
public function testSetContent($content)
{
$response = new Response();
$response->setContent($content);
$this->assertEquals((string) $content, $response->getContent());
}
/**
* @expectedException UnexpectedValueException
* @dataProvider invalidContentProvider
*/
public function testSetContentInvalid($content)
{
$response = new Response();
$response->setContent($content);
}
public function testSettersAreChainable()
{
$response = new Response();
$setters = array(
'setProtocolVersion' => '1.0',
'setCharset' => 'UTF-8',
'setPublic' => null,
'setPrivate' => null,
'setDate' => new \DateTime,
'expire' => null,
'setMaxAge' => 1,
'setSharedMaxAge' => 1,
'setTtl' => 1,
'setClientTtl' => 1,
);
foreach ($setters as $setter => $arg) {
$this->assertEquals($response, $response->{$setter}($arg));
}
}
public function validContentProvider()
{
return array(
'obj' => array(new StringableObject),
'string' => array('Foo'),
'int' => array(2),
);
}
public function invalidContentProvider()
{
return array(
'obj' => array(new \stdClass),
'array' => array(array()),
'bool' => array(true, '1'),
);
}
protected function createDateTimeOneHourAgo()
{
$date = new \DateTime();
return $date->sub(new \DateInterval('PT1H'));
}
protected function createDateTimeOneHourLater()
{
$date = new \DateTime();
return $date->add(new \DateInterval('PT1H'));
}
protected function createDateTimeNow()
{
return new \DateTime();
}
protected function provideResponse()
{
return new Response();
}
}
class StringableObject
{
public function __toString()
{
return 'Foo';
}
}

View File

@@ -0,0 +1,88 @@
<?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\Tests;
use Symfony\Component\HttpFoundation\Request;
abstract class ResponseTestCase extends \PHPUnit_Framework_TestCase
{
public function testNoCacheControlHeaderOnAttachmentUsingHTTPSAndMSIE()
{
// Check for HTTPS and IE 8
$request = new Request();
$request->server->set('HTTPS', true);
$request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)');
$response = $this->provideResponse();
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
$response->prepare($request);
$this->assertFalse($response->headers->has('Cache-Control'));
// Check for IE 10 and HTTPS
$request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)');
$response = $this->provideResponse();
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
$response->prepare($request);
$this->assertTrue($response->headers->has('Cache-Control'));
// Check for IE 9 and HTTPS
$request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)');
$response = $this->provideResponse();
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
$response->prepare($request);
$this->assertTrue($response->headers->has('Cache-Control'));
// Check for IE 9 and HTTP
$request->server->set('HTTPS', false);
$response = $this->provideResponse();
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
$response->prepare($request);
$this->assertTrue($response->headers->has('Cache-Control'));
// Check for IE 8 and HTTP
$request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)');
$response = $this->provideResponse();
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
$response->prepare($request);
$this->assertTrue($response->headers->has('Cache-Control'));
// Check for non-IE and HTTPS
$request->server->set('HTTPS', true);
$request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17');
$response = $this->provideResponse();
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
$response->prepare($request);
$this->assertTrue($response->headers->has('Cache-Control'));
// Check for non-IE and HTTP
$request->server->set('HTTPS', false);
$response = $this->provideResponse();
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
$response->prepare($request);
$this->assertTrue($response->headers->has('Cache-Control'));
}
abstract protected function provideResponse();
}

View File

@@ -0,0 +1,101 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests;
use Symfony\Component\HttpFoundation\ServerBag;
/**
* ServerBagTest
*
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
*/
class ServerBagTest extends \PHPUnit_Framework_TestCase
{
public function testShouldExtractHeadersFromServerArray()
{
$server = array(
'SOME_SERVER_VARIABLE' => 'value',
'SOME_SERVER_VARIABLE2' => 'value',
'ROOT' => 'value',
'HTTP_CONTENT_TYPE' => 'text/html',
'HTTP_CONTENT_LENGTH' => '0',
'HTTP_ETAG' => 'asdf',
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => 'bar',
);
$bag = new ServerBag($server);
$this->assertEquals(array(
'CONTENT_TYPE' => 'text/html',
'CONTENT_LENGTH' => '0',
'ETAG' => 'asdf',
'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => 'bar',
), $bag->getHeaders());
}
public function testHttpPasswordIsOptional()
{
$bag = new ServerBag(array('PHP_AUTH_USER' => 'foo'));
$this->assertEquals(array(
'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => ''
), $bag->getHeaders());
}
public function testHttpBasicAuthWithPhpCgi()
{
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')));
$this->assertEquals(array(
'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => 'bar'
), $bag->getHeaders());
}
public function testHttpBasicAuthWithPhpCgiRedirect()
{
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')));
$this->assertEquals(array(
'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => 'bar'
), $bag->getHeaders());
}
public function testHttpBasicAuthWithPhpCgiEmptyPassword()
{
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:')));
$this->assertEquals(array(
'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => ''
), $bag->getHeaders());
}
public function testOAuthBearerAuth()
{
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => $headerContent));
$this->assertEquals(array(
'AUTHORIZATION' => $headerContent,
), $bag->getHeaders());
}
}

View File

@@ -0,0 +1,193 @@
<?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\Tests\Session\Attribute;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
/**
* Tests AttributeBag
*
* @author Drak <drak@zikula.org>
*/
class AttributeBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @var array
*/
private $array;
/**
* @var AttributeBag
*/
private $bag;
protected function setUp()
{
$this->array = array(
'hello' => 'world',
'always' => 'be happy',
'user.login' => 'drak',
'csrf.token' => array(
'a' => '1234',
'b' => '4321',
),
'category' => array(
'fishing' => array(
'first' => 'cod',
'second' => 'sole')
),
);
$this->bag = new AttributeBag('_sf2');
$this->bag->initialize($this->array);
}
protected function tearDown()
{
$this->bag = null;
$this->array = array();
}
public function testInitialize()
{
$bag = new AttributeBag();
$bag->initialize($this->array);
$this->assertEquals($this->array, $bag->all());
$array = array('should' => 'change');
$bag->initialize($array);
$this->assertEquals($array, $bag->all());
}
public function testGetStorageKey()
{
$this->assertEquals('_sf2', $this->bag->getStorageKey());
$attributeBag = new AttributeBag('test');
$this->assertEquals('test', $attributeBag->getStorageKey());
}
public function testGetSetName()
{
$this->assertEquals('attributes', $this->bag->getName());
$this->bag->setName('foo');
$this->assertEquals('foo', $this->bag->getName());
}
/**
* @dataProvider attributesProvider
*/
public function testHas($key, $value, $exists)
{
$this->assertEquals($exists, $this->bag->has($key));
}
/**
* @dataProvider attributesProvider
*/
public function testGet($key, $value, $expected)
{
$this->assertEquals($value, $this->bag->get($key));
}
public function testGetDefaults()
{
$this->assertNull($this->bag->get('user2.login'));
$this->assertEquals('default', $this->bag->get('user2.login', 'default'));
}
/**
* @dataProvider attributesProvider
*/
public function testSet($key, $value, $expected)
{
$this->bag->set($key, $value);
$this->assertEquals($value, $this->bag->get($key));
}
public function testAll()
{
$this->assertEquals($this->array, $this->bag->all());
$this->bag->set('hello', 'fabien');
$array = $this->array;
$array['hello'] = 'fabien';
$this->assertEquals($array, $this->bag->all());
}
public function testReplace()
{
$array = array();
$array['name'] = 'jack';
$array['foo.bar'] = 'beep';
$this->bag->replace($array);
$this->assertEquals($array, $this->bag->all());
$this->assertNull($this->bag->get('hello'));
$this->assertNull($this->bag->get('always'));
$this->assertNull($this->bag->get('user.login'));
}
public function testRemove()
{
$this->assertEquals('world', $this->bag->get('hello'));
$this->bag->remove('hello');
$this->assertNull($this->bag->get('hello'));
$this->assertEquals('be happy', $this->bag->get('always'));
$this->bag->remove('always');
$this->assertNull($this->bag->get('always'));
$this->assertEquals('drak', $this->bag->get('user.login'));
$this->bag->remove('user.login');
$this->assertNull($this->bag->get('user.login'));
}
public function testClear()
{
$this->bag->clear();
$this->assertEquals(array(), $this->bag->all());
}
public function attributesProvider()
{
return array(
array('hello', 'world', true),
array('always', 'be happy', true),
array('user.login', 'drak', true),
array('csrf.token', array('a' => '1234', 'b' => '4321'), true),
array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true),
array('user2.login', null, false),
array('never', null, false),
array('bye', null, false),
array('bye/for/now', null, false),
);
}
/**
* @covers Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag::getIterator
*/
public function testGetIterator()
{
$i = 0;
foreach ($this->bag as $key => $val) {
$this->assertEquals($this->array[$key], $val);
$i++;
}
$this->assertEquals(count($this->array), $i);
}
/**
* @covers Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag::count
*/
public function testCount()
{
$this->assertEquals(count($this->array), count($this->bag));
}
}

View File

@@ -0,0 +1,183 @@
<?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\Tests\Session\Attribute;
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
/**
* Tests NamespacedAttributeBag
*
* @author Drak <drak@zikula.org>
*/
class NamespacedAttributeBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @var array
*/
private $array;
/**
* @var NamespacedAttributeBag
*/
private $bag;
protected function setUp()
{
$this->array = array(
'hello' => 'world',
'always' => 'be happy',
'user.login' => 'drak',
'csrf.token' => array(
'a' => '1234',
'b' => '4321',
),
'category' => array(
'fishing' => array(
'first' => 'cod',
'second' => 'sole')
),
);
$this->bag = new NamespacedAttributeBag('_sf2', '/');
$this->bag->initialize($this->array);
}
protected function tearDown()
{
$this->bag = null;
$this->array = array();
}
public function testInitialize()
{
$bag = new NamespacedAttributeBag();
$bag->initialize($this->array);
$this->assertEquals($this->array, $this->bag->all());
$array = array('should' => 'not stick');
$bag->initialize($array);
// should have remained the same
$this->assertEquals($this->array, $this->bag->all());
}
public function testGetStorageKey()
{
$this->assertEquals('_sf2', $this->bag->getStorageKey());
$attributeBag = new NamespacedAttributeBag('test');
$this->assertEquals('test', $attributeBag->getStorageKey());
}
/**
* @dataProvider attributesProvider
*/
public function testHas($key, $value, $exists)
{
$this->assertEquals($exists, $this->bag->has($key));
}
/**
* @dataProvider attributesProvider
*/
public function testGet($key, $value, $expected)
{
$this->assertEquals($value, $this->bag->get($key));
}
public function testGetDefaults()
{
$this->assertNull($this->bag->get('user2.login'));
$this->assertEquals('default', $this->bag->get('user2.login', 'default'));
}
/**
* @dataProvider attributesProvider
*/
public function testSet($key, $value, $expected)
{
$this->bag->set($key, $value);
$this->assertEquals($value, $this->bag->get($key));
}
public function testAll()
{
$this->assertEquals($this->array, $this->bag->all());
$this->bag->set('hello', 'fabien');
$array = $this->array;
$array['hello'] = 'fabien';
$this->assertEquals($array, $this->bag->all());
}
public function testReplace()
{
$array = array();
$array['name'] = 'jack';
$array['foo.bar'] = 'beep';
$this->bag->replace($array);
$this->assertEquals($array, $this->bag->all());
$this->assertNull($this->bag->get('hello'));
$this->assertNull($this->bag->get('always'));
$this->assertNull($this->bag->get('user.login'));
}
public function testRemove()
{
$this->assertEquals('world', $this->bag->get('hello'));
$this->bag->remove('hello');
$this->assertNull($this->bag->get('hello'));
$this->assertEquals('be happy', $this->bag->get('always'));
$this->bag->remove('always');
$this->assertNull($this->bag->get('always'));
$this->assertEquals('drak', $this->bag->get('user.login'));
$this->bag->remove('user.login');
$this->assertNull($this->bag->get('user.login'));
}
public function testRemoveExistingNamespacedAttribute()
{
$this->assertSame('cod', $this->bag->remove('category/fishing/first'));
}
public function testRemoveNonexistingNamespacedAttribute()
{
$this->assertNull($this->bag->remove('foo/bar/baz'));
}
public function testClear()
{
$this->bag->clear();
$this->assertEquals(array(), $this->bag->all());
}
public function attributesProvider()
{
return array(
array('hello', 'world', true),
array('always', 'be happy', true),
array('user.login', 'drak', true),
array('csrf.token', array('a' => '1234', 'b' => '4321'), true),
array('csrf.token/a', '1234', true),
array('csrf.token/b', '4321', true),
array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true),
array('category/fishing', array('first' => 'cod', 'second' => 'sole'), true),
array('category/fishing/missing/first', null, false),
array('category/fishing/first', 'cod', true),
array('category/fishing/second', 'sole', true),
array('category/fishing/missing/second', null, false),
array('user2.login', null, false),
array('never', null, false),
array('bye', null, false),
array('bye/for/now', null, false),
);
}
}

View File

@@ -0,0 +1,155 @@
<?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\Tests\Session\Flash;
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag as FlashBag;
/**
* AutoExpireFlashBagTest
*
* @author Drak <drak@zikula.org>
*/
class AutoExpireFlashBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag
*/
private $bag;
/**
* @var array
*/
protected $array = array();
protected function setUp()
{
parent::setUp();
$this->bag = new FlashBag();
$this->array = array('new' => array('notice' => array('A previous flash message')));
$this->bag->initialize($this->array);
}
public function tearDown()
{
$this->bag = null;
parent::tearDown();
}
public function testInitialize()
{
$bag = new FlashBag();
$array = array('new' => array('notice' => array('A previous flash message')));
$bag->initialize($array);
$this->assertEquals(array('A previous flash message'), $bag->peek('notice'));
$array = array('new' => array(
'notice' => array('Something else'),
'error' => array('a'),
));
$bag->initialize($array);
$this->assertEquals(array('Something else'), $bag->peek('notice'));
$this->assertEquals(array('a'), $bag->peek('error'));
}
public function testGetStorageKey()
{
$this->assertEquals('_sf2_flashes', $this->bag->getStorageKey());
$attributeBag = new FlashBag('test');
$this->assertEquals('test', $attributeBag->getStorageKey());
}
public function testGetSetName()
{
$this->assertEquals('flashes', $this->bag->getName());
$this->bag->setName('foo');
$this->assertEquals('foo', $this->bag->getName());
}
public function testPeek()
{
$this->assertEquals(array(), $this->bag->peek('non_existing'));
$this->assertEquals(array('default'), $this->bag->peek('non_existing', array('default')));
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
}
public function testSet()
{
$this->bag->set('notice', 'Foo');
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
}
public function testHas()
{
$this->assertFalse($this->bag->has('nothing'));
$this->assertTrue($this->bag->has('notice'));
}
public function testKeys()
{
$this->assertEquals(array('notice'), $this->bag->keys());
}
public function testPeekAll()
{
$array = array(
'new' => array(
'notice' => 'Foo',
'error' => 'Bar',
),
);
$this->bag->initialize($array);
$this->assertEquals(array(
'notice' => 'Foo',
'error' => 'Bar',
), $this->bag->peekAll()
);
$this->assertEquals(array(
'notice' => 'Foo',
'error' => 'Bar',
), $this->bag->peekAll()
);
}
public function testGet()
{
$this->assertEquals(array(), $this->bag->get('non_existing'));
$this->assertEquals(array('default'), $this->bag->get('non_existing', array('default')));
$this->assertEquals(array('A previous flash message'), $this->bag->get('notice'));
$this->assertEquals(array(), $this->bag->get('notice'));
}
public function testSetAll()
{
$this->bag->setAll(array('a' => 'first', 'b' => 'second'));
$this->assertFalse($this->bag->has('a'));
$this->assertFalse($this->bag->has('b'));
}
public function testAll()
{
$this->bag->set('notice', 'Foo');
$this->bag->set('error', 'Bar');
$this->assertEquals(array(
'notice' => array('A previous flash message'),
), $this->bag->all()
);
$this->assertEquals(array(), $this->bag->all());
}
public function testClear()
{
$this->assertEquals(array('notice' => array('A previous flash message')), $this->bag->clear());
}
}

View File

@@ -0,0 +1,155 @@
<?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\Tests\Session\Flash;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
/**
* FlashBagTest
*
* @author Drak <drak@zikula.org>
*/
class FlashBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \Symfony\Component\HttpFoundation\SessionFlash\FlashBagInterface
*/
private $bag;
/**
* @var array
*/
protected $array = array();
protected function setUp()
{
parent::setUp();
$this->bag = new FlashBag();
$this->array = array('notice' => array('A previous flash message'));
$this->bag->initialize($this->array);
}
public function tearDown()
{
$this->bag = null;
parent::tearDown();
}
public function testInitialize()
{
$bag = new FlashBag();
$bag->initialize($this->array);
$this->assertEquals($this->array, $bag->peekAll());
$array = array('should' => array('change'));
$bag->initialize($array);
$this->assertEquals($array, $bag->peekAll());
}
public function testGetStorageKey()
{
$this->assertEquals('_sf2_flashes', $this->bag->getStorageKey());
$attributeBag = new FlashBag('test');
$this->assertEquals('test', $attributeBag->getStorageKey());
}
public function testGetSetName()
{
$this->assertEquals('flashes', $this->bag->getName());
$this->bag->setName('foo');
$this->assertEquals('foo', $this->bag->getName());
}
public function testPeek()
{
$this->assertEquals(array(), $this->bag->peek('non_existing'));
$this->assertEquals(array('default'), $this->bag->peek('not_existing', array('default')));
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
}
public function testGet()
{
$this->assertEquals(array(), $this->bag->get('non_existing'));
$this->assertEquals(array('default'), $this->bag->get('not_existing', array('default')));
$this->assertEquals(array('A previous flash message'), $this->bag->get('notice'));
$this->assertEquals(array(), $this->bag->get('notice'));
}
public function testAll()
{
$this->bag->set('notice', 'Foo');
$this->bag->set('error', 'Bar');
$this->assertEquals(array(
'notice' => array('Foo'),
'error' => array('Bar')), $this->bag->all()
);
$this->assertEquals(array(), $this->bag->all());
}
public function testSet()
{
$this->bag->set('notice', 'Foo');
$this->bag->set('notice', 'Bar');
$this->assertEquals(array('Bar'), $this->bag->peek('notice'));
}
public function testHas()
{
$this->assertFalse($this->bag->has('nothing'));
$this->assertTrue($this->bag->has('notice'));
}
public function testKeys()
{
$this->assertEquals(array('notice'), $this->bag->keys());
}
public function testPeekAll()
{
$this->bag->set('notice', 'Foo');
$this->bag->set('error', 'Bar');
$this->assertEquals(array(
'notice' => array('Foo'),
'error' => array('Bar'),
), $this->bag->peekAll()
);
$this->assertTrue($this->bag->has('notice'));
$this->assertTrue($this->bag->has('error'));
$this->assertEquals(array(
'notice' => array('Foo'),
'error' => array('Bar'),
), $this->bag->peekAll()
);
}
/**
* @covers Symfony\Component\HttpFoundation\Session\Flash\FlashBag::getIterator
*/
public function testGetIterator()
{
$flashes = array('hello' => 'world', 'beep' => 'boop', 'notice' => 'nope');
foreach ($flashes as $key => $val) {
$this->bag->set($key, $val);
}
$i = 0;
foreach ($this->bag as $key => $val) {
$this->assertEquals(array($flashes[$key]), $val);
$i++;
}
$this->assertEquals(count($flashes), $i);
$this->assertEquals(0, count($this->bag->all()));
}
}

View File

@@ -0,0 +1,227 @@
<?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\Tests\Session;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
/**
* SessionTest
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Robert Schönthal <seroscho@googlemail.com>
* @author Drak <drak@zikula.org>
*/
class SessionTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface
*/
protected $storage;
/**
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
*/
protected $session;
protected function setUp()
{
$this->storage = new MockArraySessionStorage();
$this->session = new Session($this->storage, new AttributeBag(), new FlashBag());
}
protected function tearDown()
{
$this->storage = null;
$this->session = null;
}
public function testStart()
{
$this->assertEquals('', $this->session->getId());
$this->assertTrue($this->session->start());
$this->assertNotEquals('', $this->session->getId());
}
public function testIsStarted()
{
$this->assertFalse($this->session->isStarted());
$this->session->start();
$this->assertTrue($this->session->isStarted());
}
public function testSetId()
{
$this->assertEquals('', $this->session->getId());
$this->session->setId('0123456789abcdef');
$this->session->start();
$this->assertEquals('0123456789abcdef', $this->session->getId());
}
public function testSetName()
{
$this->assertEquals('MOCKSESSID', $this->session->getName());
$this->session->setName('session.test.com');
$this->session->start();
$this->assertEquals('session.test.com', $this->session->getName());
}
public function testGet()
{
// tests defaults
$this->assertNull($this->session->get('foo'));
$this->assertEquals(1, $this->session->get('foo', 1));
}
/**
* @dataProvider setProvider
*/
public function testSet($key, $value)
{
$this->session->set($key, $value);
$this->assertEquals($value, $this->session->get($key));
}
/**
* @dataProvider setProvider
*/
public function testHas($key, $value)
{
$this->session->set($key, $value);
$this->assertTrue($this->session->has($key));
$this->assertFalse($this->session->has($key.'non_value'));
}
public function testReplace()
{
$this->session->replace(array('happiness' => 'be good', 'symfony' => 'awesome'));
$this->assertEquals(array('happiness' => 'be good', 'symfony' => 'awesome'), $this->session->all());
$this->session->replace(array());
$this->assertEquals(array(), $this->session->all());
}
/**
* @dataProvider setProvider
*/
public function testAll($key, $value, $result)
{
$this->session->set($key, $value);
$this->assertEquals($result, $this->session->all());
}
/**
* @dataProvider setProvider
*/
public function testClear($key, $value)
{
$this->session->set('hi', 'fabien');
$this->session->set($key, $value);
$this->session->clear();
$this->assertEquals(array(), $this->session->all());
}
public function setProvider()
{
return array(
array('foo', 'bar', array('foo' => 'bar')),
array('foo.bar', 'too much beer', array('foo.bar' => 'too much beer')),
array('great', 'symfony2 is great', array('great' => 'symfony2 is great')),
);
}
/**
* @dataProvider setProvider
*/
public function testRemove($key, $value)
{
$this->session->set('hi.world', 'have a nice day');
$this->session->set($key, $value);
$this->session->remove($key);
$this->assertEquals(array('hi.world' => 'have a nice day'), $this->session->all());
}
public function testInvalidate()
{
$this->session->set('invalidate', 123);
$this->session->invalidate();
$this->assertEquals(array(), $this->session->all());
}
public function testMigrate()
{
$this->session->set('migrate', 321);
$this->session->migrate();
$this->assertEquals(321, $this->session->get('migrate'));
}
public function testMigrateDestroy()
{
$this->session->set('migrate', 333);
$this->session->migrate(true);
$this->assertEquals(333, $this->session->get('migrate'));
}
public function testSave()
{
$this->session->start();
$this->session->save();
}
public function testGetId()
{
$this->assertEquals('', $this->session->getId());
$this->session->start();
$this->assertNotEquals('', $this->session->getId());
}
public function testGetFlashBag()
{
$this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface', $this->session->getFlashBag());
}
/**
* @covers Symfony\Component\HttpFoundation\Session\Session::getIterator
*/
public function testGetIterator()
{
$attributes = array('hello' => 'world', 'symfony2' => 'rocks');
foreach ($attributes as $key => $val) {
$this->session->set($key, $val);
}
$i = 0;
foreach ($this->session as $key => $val) {
$this->assertEquals($attributes[$key], $val);
$i++;
}
$this->assertEquals(count($attributes), $i);
}
/**
* @covers \Symfony\Component\HttpFoundation\Session\Session::count
*/
public function testGetCount()
{
$this->session->set('hello', 'world');
$this->session->set('symfony2', 'rocks');
$this->assertEquals(2, count($this->session));
}
public function testGetMeta()
{
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag());
}
}

View File

@@ -0,0 +1,124 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler;
class MemcacheSessionHandlerTest extends \PHPUnit_Framework_TestCase
{
const PREFIX = 'prefix_';
const TTL = 1000;
/**
* @var MemcacheSessionHandler
*/
protected $storage;
protected $memcache;
protected function setUp()
{
if (!class_exists('Memcache')) {
$this->markTestSkipped('Skipped tests Memcache class is not present');
}
$this->memcache = $this->getMock('Memcache');
$this->storage = new MemcacheSessionHandler(
$this->memcache,
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
);
}
protected function tearDown()
{
$this->memcache = null;
$this->storage = null;
}
public function testOpenSession()
{
$this->assertTrue($this->storage->open('', ''));
}
public function testCloseSession()
{
$this->memcache
->expects($this->once())
->method('close')
->will($this->returnValue(true))
;
$this->assertTrue($this->storage->close());
}
public function testReadSession()
{
$this->memcache
->expects($this->once())
->method('get')
->with(self::PREFIX.'id')
;
$this->assertEquals('', $this->storage->read('id'));
}
public function testWriteSession()
{
$this->memcache
->expects($this->once())
->method('set')
->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2))
->will($this->returnValue(true))
;
$this->assertTrue($this->storage->write('id', 'data'));
}
public function testDestroySession()
{
$this->memcache
->expects($this->once())
->method('delete')
->with(self::PREFIX.'id')
->will($this->returnValue(true))
;
$this->assertTrue($this->storage->destroy('id'));
}
public function testGcSession()
{
$this->assertTrue($this->storage->gc(123));
}
/**
* @dataProvider getOptionFixtures
*/
public function testSupportedOptions($options, $supported)
{
try {
new MemcacheSessionHandler($this->memcache, $options);
$this->assertTrue($supported);
} catch (\InvalidArgumentException $e) {
$this->assertFalse($supported);
}
}
public function getOptionFixtures()
{
return array(
array(array('prefix' => 'session'), true),
array(array('expiretime' => 100), true),
array(array('prefix' => 'session', 'expiretime' => 200), true),
array(array('expiretime' => 100, 'foo' => 'bar'), false),
);
}
}

View File

@@ -0,0 +1,119 @@
<?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\Tests\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler;
class MemcachedSessionHandlerTest extends \PHPUnit_Framework_TestCase
{
const PREFIX = 'prefix_';
const TTL = 1000;
/**
* @var MemcachedSessionHandler
*/
protected $storage;
protected $memcached;
protected function setUp()
{
if (!class_exists('Memcached')) {
$this->markTestSkipped('Skipped tests Memcached class is not present');
}
$this->memcached = $this->getMock('Memcached');
$this->storage = new MemcachedSessionHandler(
$this->memcached,
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
);
}
protected function tearDown()
{
$this->memcached = null;
$this->storage = null;
}
public function testOpenSession()
{
$this->assertTrue($this->storage->open('', ''));
}
public function testCloseSession()
{
$this->assertTrue($this->storage->close());
}
public function testReadSession()
{
$this->memcached
->expects($this->once())
->method('get')
->with(self::PREFIX.'id')
;
$this->assertEquals('', $this->storage->read('id'));
}
public function testWriteSession()
{
$this->memcached
->expects($this->once())
->method('set')
->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2))
->will($this->returnValue(true))
;
$this->assertTrue($this->storage->write('id', 'data'));
}
public function testDestroySession()
{
$this->memcached
->expects($this->once())
->method('delete')
->with(self::PREFIX.'id')
->will($this->returnValue(true))
;
$this->assertTrue($this->storage->destroy('id'));
}
public function testGcSession()
{
$this->assertTrue($this->storage->gc(123));
}
/**
* @dataProvider getOptionFixtures
*/
public function testSupportedOptions($options, $supported)
{
try {
new MemcachedSessionHandler($this->memcached, $options);
$this->assertTrue($supported);
} catch (\InvalidArgumentException $e) {
$this->assertFalse($supported);
}
}
public function getOptionFixtures()
{
return array(
array(array('prefix' => 'session'), true),
array(array('expiretime' => 100), true),
array(array('prefix' => 'session', 'expiretime' => 200), true),
array(array('expiretime' => 100, 'foo' => 'bar'), false),
);
}
}

View File

@@ -0,0 +1,171 @@
<?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\Tests\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
/**
* @author Markus Bachmann <markus.bachmann@bachi.biz>
*/
class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $mongo;
private $storage;
public $options;
protected function setUp()
{
if (!extension_loaded('mongo')) {
$this->markTestSkipped('MongoDbSessionHandler requires the PHP "mongo" extension.');
}
$mongoClass = (version_compare(phpversion('mongo'), '1.3.0', '<')) ? 'Mongo' : 'MongoClient';
$this->mongo = $this->getMockBuilder($mongoClass)
->disableOriginalConstructor()
->getMock();
$this->options = array(
'id_field' => '_id',
'data_field' => 'data',
'time_field' => 'time',
'database' => 'sf2-test',
'collection' => 'session-test'
);
$this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
}
/**
* @expectedException InvalidArgumentException
*/
public function testConstructorShouldThrowExceptionForInvalidMongo()
{
new MongoDbSessionHandler(new \stdClass(), $this->options);
}
/**
* @expectedException InvalidArgumentException
*/
public function testConstructorShouldThrowExceptionForMissingOptions()
{
new MongoDbSessionHandler($this->mongo, array());
}
public function testOpenMethodAlwaysReturnTrue()
{
$this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true');
}
public function testCloseMethodAlwaysReturnTrue()
{
$this->assertTrue($this->storage->close(), 'The "close" method should always return true');
}
public function testWrite()
{
$collection = $this->getMockBuilder('MongoCollection')
->disableOriginalConstructor()
->getMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$that = $this;
$data = array();
$collection->expects($this->once())
->method('update')
->will($this->returnCallback(function($criteria, $updateData, $options) use ($that, &$data) {
$that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
$that->assertEquals(array('upsert' => true, 'multiple' => false), $options);
$data = $updateData['$set'];
}));
$this->assertTrue($this->storage->write('foo', 'bar'));
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
$that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
}
public function testReplaceSessionData()
{
$collection = $this->getMockBuilder('MongoCollection')
->disableOriginalConstructor()
->getMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$data = array();
$collection->expects($this->exactly(2))
->method('update')
->will($this->returnCallback(function($criteria, $updateData, $options) use (&$data) {
$data = $updateData;
}));
$this->storage->write('foo', 'bar');
$this->storage->write('foo', 'foobar');
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
}
public function testDestroy()
{
$collection = $this->getMockBuilder('MongoCollection')
->disableOriginalConstructor()
->getMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$collection->expects($this->once())
->method('remove')
->with(array($this->options['id_field'] => 'foo'));
$this->assertTrue($this->storage->destroy('foo'));
}
public function testGc()
{
$collection = $this->getMockBuilder('MongoCollection')
->disableOriginalConstructor()
->getMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$that = $this;
$collection->expects($this->once())
->method('remove')
->will($this->returnCallback(function($criteria) use ($that) {
$that->assertInstanceOf('MongoDate', $criteria[$that->options['time_field']]['$lt']);
$that->assertGreaterThanOrEqual(time() - -1, $criteria[$that->options['time_field']]['$lt']->sec);
}));
$this->assertTrue($this->storage->gc(-1));
}
}

View File

@@ -0,0 +1,80 @@
<?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\Tests\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
/**
* Test class for NativeFileSessionHandler.
*
* @author Drak <drak@zikula.org>
*
* @runTestsInSeparateProcesses
*/
class NativeFileSessionHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testConstruct()
{
$storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir()));
if (version_compare(phpversion(), '5.4.0', '<')) {
$this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName());
$this->assertEquals('files', ini_get('session.save_handler'));
} else {
$this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName());
$this->assertEquals('user', ini_get('session.save_handler'));
}
$this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path'));
$this->assertEquals('TESTING', ini_get('session.name'));
}
/**
* @dataProvider savePathDataProvider
*/
public function testConstructSavePath($savePath, $expectedSavePath, $path)
{
$handler = new NativeFileSessionHandler($savePath);
$this->assertEquals($expectedSavePath, ini_get('session.save_path'));
$this->assertTrue(is_dir(realpath($path)));
rmdir($path);
}
public function savePathDataProvider()
{
$base = sys_get_temp_dir();
return array(
array("$base/foo", "$base/foo", "$base/foo"),
array("5;$base/foo", "5;$base/foo", "$base/foo"),
array("5;0600;$base/foo", "5;0600;$base/foo", "$base/foo"),
);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testConstructException()
{
$handler = new NativeFileSessionHandler('something;invalid;with;too-many-args');
}
public function testConstructDefault()
{
$path = ini_get('session.save_path');
$storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler());
$this->assertEquals($path, ini_get('session.save_path'));
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
/**
* Test class for NativeSessionHandler.
*
* @author Drak <drak@zikula.org>
*
* @runTestsInSeparateProcesses
*/
class NativeSessionHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testConstruct()
{
$handler = new NativeSessionHandler();
// note for PHPUnit optimisers - the use of assertTrue/False
// here is deliberate since the tests do not require the classes to exist - drak
if (version_compare(phpversion(), '5.4.0', '<')) {
$this->assertFalse($handler instanceof \SessionHandler);
$this->assertTrue($handler instanceof NativeSessionHandler);
} else {
$this->assertTrue($handler instanceof \SessionHandler);
$this->assertTrue($handler instanceof NativeSessionHandler);
}
}
}

View File

@@ -0,0 +1,57 @@
<?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\Tests\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Session;
/**
* Test class for NullSessionHandler.
*
* @author Drak <drak@zikula.org>
*
* @runTestsInSeparateProcesses
*/
class NullSessionStorageTest extends \PHPUnit_Framework_TestCase
{
public function testSaveHandlers()
{
$storage = $this->getStorage();
$this->assertEquals('user', ini_get('session.save_handler'));
}
public function testSession()
{
session_id('nullsessionstorage');
$storage = $this->getStorage();
$session = new Session($storage);
$this->assertNull($session->get('something'));
$session->set('something', 'unique');
$this->assertEquals('unique', $session->get('something'));
}
public function testNothingIsPersisted()
{
session_id('nullsessionstorage');
$storage = $this->getStorage();
$session = new Session($storage);
$session->start();
$this->assertEquals('nullsessionstorage', $session->getId());
$this->assertNull($session->get('something'));
}
public function getStorage()
{
return new NativeSessionStorage(array(), new NullSessionHandler());
}
}

View File

@@ -0,0 +1,101 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase
{
private $pdo;
protected function setUp()
{
if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers())) {
$this->markTestSkipped('This test requires SQLite support in your environment');
}
$this->pdo = new \PDO("sqlite::memory:");
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$sql = "CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)";
$this->pdo->exec($sql);
}
public function testIncompleteOptions()
{
$this->setExpectedException('InvalidArgumentException');
$storage = new PdoSessionHandler($this->pdo, array(), array());
}
public function testWrongPdoErrMode()
{
$pdo = new \PDO("sqlite::memory:");
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
$pdo->exec("CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)");
$this->setExpectedException('InvalidArgumentException');
$storage = new PdoSessionHandler($pdo, array('db_table' => 'sessions'), array());
}
public function testWrongTableOptionsWrite()
{
$storage = new PdoSessionHandler($this->pdo, array('db_table' => 'bad_name'), array());
$this->setExpectedException('RuntimeException');
$storage->write('foo', 'bar');
}
public function testWrongTableOptionsRead()
{
$storage = new PdoSessionHandler($this->pdo, array('db_table' => 'bad_name'), array());
$this->setExpectedException('RuntimeException');
$storage->read('foo', 'bar');
}
public function testWriteRead()
{
$storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array());
$storage->write('foo', 'bar');
$this->assertEquals('bar', $storage->read('foo'), 'written value can be read back correctly');
}
public function testMultipleInstances()
{
$storage1 = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array());
$storage1->write('foo', 'bar');
$storage2 = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array());
$this->assertEquals('bar', $storage2->read('foo'), 'values persist between instances');
}
public function testSessionDestroy()
{
$storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array());
$storage->write('foo', 'bar');
$this->assertEquals(1, count($this->pdo->query('SELECT * FROM sessions')->fetchAll()));
$storage->destroy('foo');
$this->assertEquals(0, count($this->pdo->query('SELECT * FROM sessions')->fetchAll()));
}
public function testSessionGC()
{
$storage = new PdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array());
$storage->write('foo', 'bar');
$storage->write('baz', 'bar');
$this->assertEquals(2, count($this->pdo->query('SELECT * FROM sessions')->fetchAll()));
$storage->gc(-1);
$this->assertEquals(0, count($this->pdo->query('SELECT * FROM sessions')->fetchAll()));
}
}

View File

@@ -0,0 +1,107 @@
<?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\Tests\Session\Storage;
use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
/**
* Test class for MetadataBag.
*/
class MetadataBagTest extends \PHPUnit_Framework_TestCase
{
/**
* @var MetadataBag
*/
protected $bag;
/**
* @var array
*/
protected $array = array();
protected function setUp()
{
$this->bag = new MetadataBag();
$this->array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 0);
$this->bag->initialize($this->array);
}
protected function tearDown()
{
$this->array = array();
$this->bag = null;
}
public function testInitialize()
{
$p = new \ReflectionProperty('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', 'meta');
$p->setAccessible(true);
$bag1 = new MetadataBag();
$array = array();
$bag1->initialize($array);
$this->assertGreaterThanOrEqual(time(), $bag1->getCreated());
$this->assertEquals($bag1->getCreated(), $bag1->getLastUsed());
sleep(1);
$bag2 = new MetadataBag();
$array2 = $p->getValue($bag1);
$bag2->initialize($array2);
$this->assertEquals($bag1->getCreated(), $bag2->getCreated());
$this->assertEquals($bag1->getLastUsed(), $bag2->getLastUsed());
$this->assertEquals($bag2->getCreated(), $bag2->getLastUsed());
sleep(1);
$bag3 = new MetadataBag();
$array3 = $p->getValue($bag2);
$bag3->initialize($array3);
$this->assertEquals($bag1->getCreated(), $bag3->getCreated());
$this->assertGreaterThan($bag2->getLastUsed(), $bag3->getLastUsed());
$this->assertNotEquals($bag3->getCreated(), $bag3->getLastUsed());
}
public function testGetSetName()
{
$this->assertEquals('__metadata', $this->bag->getName());
$this->bag->setName('foo');
$this->assertEquals('foo', $this->bag->getName());
}
public function testGetStorageKey()
{
$this->assertEquals('_sf2_meta', $this->bag->getStorageKey());
}
public function testGetLifetime()
{
$bag = new MetadataBag();
$array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 1000);
$bag->initialize($array);
$this->assertEquals(1000, $bag->getLifetime());
}
public function testGetCreated()
{
$this->assertEquals(1234567, $this->bag->getCreated());
}
public function testGetLastUsed()
{
$this->assertLessThanOrEqual(time(), $this->bag->getLastUsed());
}
public function testClear()
{
$this->bag->clear();
}
}

View File

@@ -0,0 +1,106 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
/**
* Test class for MockArraySessionStorage.
*
* @author Drak <drak@zikula.org>
*/
class MockArraySessionStorageTest extends \PHPUnit_Framework_TestCase
{
/**
* @var MockArraySessionStorage
*/
private $storage;
/**
* @var AttributeBag
*/
private $attributes;
/**
* @var FlashBag
*/
private $flashes;
private $data;
protected function setUp()
{
$this->attributes = new AttributeBag();
$this->flashes = new FlashBag();
$this->data = array(
$this->attributes->getStorageKey() => array('foo' => 'bar'),
$this->flashes->getStorageKey() => array('notice' => 'hello'),
);
$this->storage = new MockArraySessionStorage();
$this->storage->registerBag($this->flashes);
$this->storage->registerBag($this->attributes);
$this->storage->setSessionData($this->data);
}
protected function tearDown()
{
$this->data = null;
$this->flashes = null;
$this->attributes = null;
$this->storage = null;
}
public function testStart()
{
$this->assertEquals('', $this->storage->getId());
$this->storage->start();
$id = $this->storage->getId();
$this->assertNotEquals('', $id);
$this->storage->start();
$this->assertEquals($id, $this->storage->getId());
}
public function testRegenerate()
{
$this->storage->start();
$id = $this->storage->getId();
$this->storage->regenerate();
$this->assertNotEquals($id, $this->storage->getId());
$this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all());
$this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll());
$id = $this->storage->getId();
$this->storage->regenerate(true);
$this->assertNotEquals($id, $this->storage->getId());
$this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all());
$this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll());
}
public function testGetId()
{
$this->assertEquals('', $this->storage->getId());
$this->storage->start();
$this->assertNotEquals('', $this->storage->getId());
}
/**
* @expectedException RuntimeException
*/
public function testUnstartedSave()
{
$this->storage->save();
}
}

Some files were not shown because too many files have changed in this diff Show More