updated backend

This commit is contained in:
Torsten 2020-01-17 10:58:01 +01:00
parent 1fea0f4fe7
commit 11521c2339
3459 changed files with 72735 additions and 168535 deletions

View file

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

View file

@ -24,7 +24,7 @@ class AcceptHeader
/**
* @var AcceptHeaderItem[]
*/
private $items = array();
private $items = [];
/**
* @var bool
@ -52,12 +52,17 @@ class AcceptHeader
{
$index = 0;
return new self(array_map(function ($itemValue) use (&$index) {
$item = AcceptHeaderItem::fromString($itemValue);
$parts = HeaderUtils::split((string) $headerValue, ',;=');
return new self(array_map(function ($subParts) use (&$index) {
$part = array_shift($subParts);
$attributes = HeaderUtils::combine($subParts);
$item = new AcceptHeaderItem($part[0], $attributes);
$item->setIndex($index++);
return $item;
}, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
}, $parts));
}
/**
@ -91,7 +96,7 @@ class AcceptHeader
*/
public function get($value)
{
return isset($this->items[$value]) ? $this->items[$value] : null;
return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null;
}
/**
@ -148,7 +153,7 @@ class AcceptHeader
/**
* Sorts items by descending quality.
*/
private function sort()
private function sort(): void
{
if (!$this->sorted) {
uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) {

View file

@ -21,13 +21,9 @@ class AcceptHeaderItem
private $value;
private $quality = 1.0;
private $index = 0;
private $attributes = array();
private $attributes = [];
/**
* @param string $value
* @param array $attributes
*/
public function __construct($value, array $attributes = array())
public function __construct(string $value, array $attributes = [])
{
$this->value = $value;
foreach ($attributes as $name => $value) {
@ -44,28 +40,16 @@ class 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();
$parts = HeaderUtils::split($itemValue, ';=');
$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] : '';
}
}
$part = array_shift($parts);
$attributes = HeaderUtils::combine($parts);
return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ('"' === $start || '\'' === $start) ? substr($value, 1, -1) : $value, $attributes);
return new self($part[0], $attributes);
}
/**
* Returns header value's string representation.
* Returns header value's string representation.
*
* @return string
*/
@ -73,9 +57,7 @@ class AcceptHeaderItem
{
$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));
$string .= '; '.HeaderUtils::toString($this->attributes, ';');
}
return $string;

View file

@ -11,9 +11,13 @@
namespace Symfony\Component\HttpFoundation;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ApacheRequest::class, Request::class), E_USER_DEPRECATED);
/**
* Request represents an HTTP request from an Apache server.
*
* @deprecated since Symfony 4.4. Use the Request class instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ApacheRequest extends Request

View file

@ -31,8 +31,8 @@ class BinaryFileResponse extends Response
* @var File
*/
protected $file;
protected $offset;
protected $maxlen;
protected $offset = 0;
protected $maxlen = -1;
protected $deleteFileAfterSend = false;
/**
@ -40,11 +40,11 @@ class BinaryFileResponse extends Response
* @param int $status The response status code
* @param array $headers An array of response headers
* @param bool $public Files are public by default
* @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param bool $autoEtag Whether the ETag header should be automatically set
* @param bool $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)
public function __construct($file, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
{
parent::__construct(null, $status, $headers);
@ -60,13 +60,13 @@ class BinaryFileResponse extends Response
* @param int $status The response status code
* @param array $headers An array of response headers
* @param bool $public Files are public by default
* @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param bool $autoEtag Whether the ETag header should be automatically set
* @param bool $autoLastModified Whether the Last-Modified header should be automatically set
*
* @return static
*/
public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
{
return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
}
@ -204,7 +204,7 @@ class BinaryFileResponse extends Response
if (!$this->headers->has('Accept-Ranges')) {
// Only accept ranges on safe HTTP methods
$this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
$this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none');
}
if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
@ -218,28 +218,28 @@ class BinaryFileResponse extends Response
if ('x-accel-redirect' === strtolower($type)) {
// Do X-Accel-Mapping substitutions.
// @link http://wiki.nginx.org/X-accel#X-Accel-Redirect
foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
$mapping = explode('=', $mapping, 2);
if (2 === \count($mapping)) {
$pathPrefix = trim($mapping[0]);
$location = trim($mapping[1]);
if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
$path = $location.substr($path, \strlen($pathPrefix));
break;
}
$parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',=');
foreach ($parts as $part) {
list($pathPrefix, $location) = $part;
if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
$path = $location.substr($path, \strlen($pathPrefix));
// Only set X-Accel-Redirect header if a valid URI can be produced
// as nginx does not serve arbitrary file paths.
$this->headers->set($type, $path);
$this->maxlen = 0;
break;
}
}
} else {
$this->headers->set($type, $path);
$this->maxlen = 0;
}
$this->headers->set($type, $path);
$this->maxlen = 0;
} elseif ($request->headers->has('Range')) {
// Process the range headers.
if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
$range = $request->headers->get('Range');
list($start, $end) = explode('-', substr($range, 6), 2) + array(0);
list($start, $end) = explode('-', substr($range, 6), 2) + [0];
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
@ -269,7 +269,7 @@ class BinaryFileResponse extends Response
return $this;
}
private function hasValidIfRangeHeader($header)
private function hasValidIfRangeHeader(?string $header): bool
{
if ($this->getEtag() === $header) {
return true;
@ -305,7 +305,7 @@ class BinaryFileResponse extends Response
fclose($out);
fclose($file);
if ($this->deleteFileAfterSend) {
if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) {
unlink($this->file->getPathname());
}
@ -322,12 +322,12 @@ class BinaryFileResponse extends Response
if (null !== $content) {
throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
}
return $this;
}
/**
* {@inheritdoc}
*
* @return false
*/
public function getContent()
{
@ -343,14 +343,14 @@ class BinaryFileResponse extends Response
}
/**
* If this is set to true, the file will be unlinked after the request is send
* If this is set to true, the file will be unlinked after the request is sent
* Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
*
* @param bool $shouldDelete
*
* @return $this
*/
public function deleteFileAfterSend($shouldDelete)
public function deleteFileAfterSend($shouldDelete = true)
{
$this->deleteFileAfterSend = $shouldDelete;

View file

@ -1,12 +1,84 @@
CHANGELOG
=========
3.4.14
------
4.4.0
-----
* passing arguments to `Request::isMethodSafe()` is deprecated.
* `ApacheRequest` is deprecated, use the `Request` class instead.
* passing a third argument to `HeaderBag::get()` is deprecated, use method `all()` instead
* [BC BREAK] `PdoSessionHandler` with MySQL changed the type of the lifetime column,
make sure to run `ALTER TABLE sessions MODIFY sess_lifetime INTEGER UNSIGNED NOT NULL` to
update your database.
* `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column,
make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database
to speed up garbage collection of expired sessions.
* added `SessionHandlerFactory` to create session handlers with a DSN
* added `IpUtils::anonymize()` to help with GDPR compliance.
4.3.0
-----
* added PHPUnit constraints: `RequestAttributeValueSame`, `ResponseCookieValueSame`, `ResponseHasCookie`,
`ResponseHasHeader`, `ResponseHeaderSame`, `ResponseIsRedirected`, `ResponseIsSuccessful`, and `ResponseStatusCodeSame`
* deprecated `MimeTypeGuesserInterface` and `ExtensionGuesserInterface` in favor of `Symfony\Component\Mime\MimeTypesInterface`.
* deprecated `MimeType` and `MimeTypeExtensionGuesser` in favor of `Symfony\Component\Mime\MimeTypes`.
* deprecated `FileBinaryMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileBinaryMimeTypeGuesser`.
* deprecated `FileinfoMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileinfoMimeTypeGuesser`.
* added `UrlHelper` that allows to get an absolute URL and a relative path for a given path
4.2.0
-----
* the default value of the "$secure" and "$samesite" arguments of Cookie's constructor
will respectively change from "false" to "null" and from "null" to "lax" in Symfony
5.0, you should define their values explicitly or use "Cookie::create()" instead.
* added `matchPort()` in RequestMatcher
4.1.3
-----
* [BC BREAK] Support for the IIS-only `X_ORIGINAL_URL` and `X_REWRITE_URL`
HTTP headers has been dropped for security reasons.
4.1.0
-----
* Query string normalization uses `parse_str()` instead of custom parsing logic.
* Passing the file size to the constructor of the `UploadedFile` class is deprecated.
* The `getClientSize()` method of the `UploadedFile` class is deprecated. Use `getSize()` instead.
* added `RedisSessionHandler` to use Redis as a session storage
* The `get()` method of the `AcceptHeader` class now takes into account the
`*` and `*/*` default values (if they are present in the Accept HTTP header)
when looking for items.
* deprecated `Request::getSession()` when no session has been set. Use `Request::hasSession()` instead.
* added `CannotWriteFileException`, `ExtensionFileException`, `FormSizeFileException`,
`IniSizeFileException`, `NoFileException`, `NoTmpDirFileException`, `PartialFileException` to
handle failed `UploadedFile`.
* added `MigratingSessionHandler` for migrating between two session handlers without losing sessions
* added `HeaderUtils`.
4.0.0
-----
* the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()`
methods have been removed
* the `Request::HEADER_CLIENT_IP` constant has been removed, use
`Request::HEADER_X_FORWARDED_FOR` instead
* the `Request::HEADER_CLIENT_HOST` constant has been removed, use
`Request::HEADER_X_FORWARDED_HOST` instead
* the `Request::HEADER_CLIENT_PROTO` constant has been removed, use
`Request::HEADER_X_FORWARDED_PROTO` instead
* the `Request::HEADER_CLIENT_PORT` constant has been removed, use
`Request::HEADER_X_FORWARDED_PORT` instead
* checking for cacheable HTTP methods using the `Request::isMethodSafe()`
method (by not passing `false` as its argument) is not supported anymore and
throws a `\BadMethodCallException`
* the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes have been removed
* setting session save handlers that do not implement `\SessionHandlerInterface` in
`NativeSessionStorage::setSaveHandler()` is not supported anymore and throws a
`\TypeError`
3.4.0
-----
@ -21,7 +93,7 @@ CHANGELOG
-----
* the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument,
see http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info,
see https://symfony.com/doc/current/deployment/proxies.html for more info,
* deprecated the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods,
* added `File\Stream`, to be passed to `BinaryFileResponse` when the size of the served file is unknown,
disabling `Range` and `Content-Length` handling, switching to chunked encoding instead
@ -144,10 +216,10 @@ CHANGELOG
* 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
behavior 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).
attributes storage behavior 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

View file

@ -18,6 +18,10 @@ namespace Symfony\Component\HttpFoundation;
*/
class Cookie
{
const SAMESITE_NONE = 'none';
const SAMESITE_LAX = 'lax';
const SAMESITE_STRICT = 'strict';
protected $name;
protected $value;
protected $domain;
@ -25,11 +29,14 @@ class Cookie
protected $path;
protected $secure;
protected $httpOnly;
private $raw;
private $sameSite;
private $secureDefault = false;
const SAMESITE_LAX = 'lax';
const SAMESITE_STRICT = 'strict';
private static $reservedCharsList = "=,; \t\r\n\v\f";
private static $reservedCharsFrom = ['=', ',', ';', ' ', "\t", "\r", "\n", "\v", "\f"];
private static $reservedCharsTo = ['%3D', '%2C', '%3B', '%20', '%09', '%0D', '%0A', '%0B', '%0C'];
/**
* Creates cookie from raw header string.
@ -41,7 +48,7 @@ class Cookie
*/
public static function fromString($cookie, $decode = false)
{
$data = array(
$data = [
'expires' => 0,
'path' => '/',
'domain' => null,
@ -49,35 +56,26 @@ class Cookie
'httponly' => false,
'raw' => !$decode,
'samesite' => null,
);
foreach (explode(';', $cookie) as $part) {
if (false === strpos($part, '=')) {
$key = trim($part);
$value = true;
} else {
list($key, $value) = explode('=', trim($part), 2);
$key = trim($key);
$value = trim($value);
}
if (!isset($data['name'])) {
$data['name'] = $decode ? urldecode($key) : $key;
$data['value'] = true === $value ? null : ($decode ? urldecode($value) : $value);
continue;
}
switch ($key = strtolower($key)) {
case 'name':
case 'value':
break;
case 'max-age':
$data['expires'] = time() + (int) $value;
break;
default:
$data[$key] = $value;
break;
}
];
$parts = HeaderUtils::split($cookie, ';=');
$part = array_shift($parts);
$name = $decode ? urldecode($part[0]) : $part[0];
$value = isset($part[1]) ? ($decode ? urldecode($part[1]) : $part[1]) : null;
$data = HeaderUtils::combine($parts) + $data;
if (isset($data['max-age'])) {
$data['expires'] = time() + (int) $data['max-age'];
}
return new static($data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
return new static($name, $value, $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
}
public static function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = self::SAMESITE_LAX): self
{
return new self($name, $value, $expire, $path, $domain, $secure, $httpOnly, $raw, $sameSite);
}
/**
@ -86,17 +84,21 @@ class Cookie
* @param int|string|\DateTimeInterface $expire The time the cookie expires
* @param string $path The path on the server in which the cookie will be available on
* @param string|null $domain The domain that the cookie is available to
* @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client
* @param bool|null $secure Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
* @param bool $raw Whether the cookie value should be sent with no url encoding
* @param string|null $sameSite Whether the cookie will be available for cross-site requests
*
* @throws \InvalidArgumentException
*/
public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null)
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null)
{
if (9 > \func_num_args()) {
@trigger_error(sprintf('The default value of the "$secure" and "$samesite" arguments of "%s"\'s constructor will respectively change from "false" to "null" and from "null" to "lax" in Symfony 5.0, you should define their values explicitly or use "Cookie::create()" instead.', __METHOD__), E_USER_DEPRECATED);
}
// from PHP source code
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
if ($raw && false !== strpbrk($name, self::$reservedCharsList)) {
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
}
@ -120,15 +122,17 @@ class Cookie
$this->domain = $domain;
$this->expire = 0 < $expire ? (int) $expire : 0;
$this->path = empty($path) ? '/' : $path;
$this->secure = (bool) $secure;
$this->httpOnly = (bool) $httpOnly;
$this->raw = (bool) $raw;
$this->secure = $secure;
$this->httpOnly = $httpOnly;
$this->raw = $raw;
if (null !== $sameSite) {
if ('' === $sameSite) {
$sameSite = null;
} elseif (null !== $sameSite) {
$sameSite = strtolower($sameSite);
}
if (!\in_array($sameSite, array(self::SAMESITE_LAX, self::SAMESITE_STRICT, null), true)) {
if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE, null], true)) {
throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
}
@ -142,7 +146,13 @@ class Cookie
*/
public function __toString()
{
$str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
if ($this->isRaw()) {
$str = $this->getName();
} else {
$str = str_replace(self::$reservedCharsFrom, self::$reservedCharsTo, $this->getName());
}
$str .= '=';
if ('' === (string) $this->getValue()) {
$str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0';
@ -246,7 +256,7 @@ class Cookie
*/
public function isSecure()
{
return $this->secure;
return $this->secure ?? $this->secureDefault;
}
/**
@ -288,4 +298,12 @@ class Cookie
{
return $this->sameSite;
}
/**
* @param bool $default The default value of the "secure" flag when it is set to null
*/
public function setSecureDefault(bool $default): void
{
$this->secureDefault = $default;
}
}

View file

@ -35,13 +35,13 @@ class ExpressionRequestMatcher extends RequestMatcher
throw new \LogicException('Unable to match the request as the expression language is not available.');
}
return $this->language->evaluate($this->expression, array(
return $this->language->evaluate($this->expression, [
'request' => $request,
'method' => $request->getMethod(),
'path' => rawurldecode($request->getPathInfo()),
'host' => $request->getHost(),
'ip' => $request->getClientIp(),
'attributes' => $request->attributes->all(),
)) && parent::matches($request);
]) && parent::matches($request);
}
}

View file

@ -18,10 +18,7 @@ namespace Symfony\Component\HttpFoundation\File\Exception;
*/
class AccessDeniedException extends FileException
{
/**
* @param string $path The path to the accessed file
*/
public function __construct($path)
public function __construct(string $path)
{
parent::__construct(sprintf('The file %s could not be accessed', $path));
}

View file

@ -18,10 +18,7 @@ namespace Symfony\Component\HttpFoundation\File\Exception;
*/
class FileNotFoundException extends FileException
{
/**
* @param string $path The path to the file that was not found
*/
public function __construct($path)
public function __construct(string $path)
{
parent::__construct(sprintf('The file "%s" does not exist', $path));
}

View file

@ -13,7 +13,7 @@ namespace Symfony\Component\HttpFoundation\File\Exception;
class UnexpectedTypeException extends FileException
{
public function __construct($value, $expectedType)
public function __construct($value, string $expectedType)
{
parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, \is_object($value) ? \get_class($value) : \gettype($value)));
}

View file

@ -13,8 +13,7 @@ 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;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
use Symfony\Component\Mime\MimeTypes;
/**
* A file in the file system.
@ -31,7 +30,7 @@ class File extends \SplFileInfo
*
* @throws FileNotFoundException If the given path is not a file
*/
public function __construct($path, $checkPath = true)
public function __construct(string $path, bool $checkPath = true)
{
if ($checkPath && !is_file($path)) {
throw new FileNotFoundException($path);
@ -50,33 +49,28 @@ class File extends \SplFileInfo
*
* @return string|null The guessed extension or null if it cannot be guessed
*
* @see ExtensionGuesser
* @see MimeTypes
* @see getMimeType()
*/
public function guessExtension()
{
$type = $this->getMimeType();
$guesser = ExtensionGuesser::getInstance();
return $guesser->guess($type);
return MimeTypes::getDefault()->getExtensions($this->getMimeType())[0] ?? null;
}
/**
* 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.
* The mime type is guessed using a MimeTypeGuesserInterface instance,
* which uses finfo_file() then the "file" system binary,
* depending on which of those are available.
*
* @return string|null The guessed mime type (e.g. "application/pdf")
*
* @see MimeTypeGuesser
* @see MimeTypes
*/
public function getMimeType()
{
$guesser = MimeTypeGuesser::getInstance();
return $guesser->guess($this->getPathname());
return MimeTypes::getDefault()->guessMimeType($this->getPathname());
}
/**
@ -105,6 +99,9 @@ class File extends \SplFileInfo
return $target;
}
/**
* @return self
*/
protected function getTargetFile($directory, $name = null)
{
if (!is_dir($directory)) {
@ -125,7 +122,7 @@ class File extends \SplFileInfo
*
* @param string $name The new file name
*
* @return string containing
* @return string
*/
protected function getName($name)
{

View file

@ -11,6 +11,10 @@
namespace Symfony\Component\HttpFoundation\File\MimeType;
use Symfony\Component\Mime\MimeTypes;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', ExtensionGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
/**
* A singleton mime type to file extension guesser.
*
@ -22,6 +26,8 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
* $guesser->register(new MyCustomExtensionGuesser());
*
* The last registered guesser is preferred over previously registered ones.
*
* @deprecated since Symfony 4.3, use {@link MimeTypes} instead
*/
class ExtensionGuesser implements ExtensionGuesserInterface
{
@ -37,7 +43,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface
*
* @var array
*/
protected $guessers = array();
protected $guessers = [];
/**
* Returns the singleton instance.
@ -90,5 +96,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface
return $extension;
}
}
return null;
}
}

View file

@ -11,8 +11,12 @@
namespace Symfony\Component\HttpFoundation\File\MimeType;
use Symfony\Component\Mime\MimeTypesInterface;
/**
* Guesses the file extension corresponding to a given mime type.
*
* @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead
*/
interface ExtensionGuesserInterface
{

View file

@ -13,11 +13,16 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\Mime\FileBinaryMimeTypeGuesser as NewFileBinaryMimeTypeGuesser;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileBinaryMimeTypeGuesser::class, NewFileBinaryMimeTypeGuesser::class), E_USER_DEPRECATED);
/**
* Guesses the mime type with the binary "file" (only available on *nix).
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated since Symfony 4.3, use {@link NewFileBinaryMimeTypeGuesser} instead
*/
class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
{
@ -31,7 +36,7 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
*
* @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')
public function __construct(string $cmd = 'file -b --mime -- %s 2>/dev/null')
{
$this->cmd = $cmd;
}
@ -74,24 +79,24 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
}
if (!self::isSupported()) {
return;
return null;
}
ob_start();
// need to use --mime instead of -i. see #6641
passthru(sprintf($this->cmd, escapeshellarg($path)), $return);
passthru(sprintf($this->cmd, escapeshellarg((0 === strpos($path, '-') ? './' : '').$path)), $return);
if ($return > 0) {
ob_end_clean();
return;
return null;
}
$type = trim(ob_get_clean());
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) {
// it's not a type, but an error message
return;
return null;
}
return $match[1];

View file

@ -13,11 +13,16 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\Mime\FileinfoMimeTypeGuesser as NewFileinfoMimeTypeGuesser;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileinfoMimeTypeGuesser::class, NewFileinfoMimeTypeGuesser::class), E_USER_DEPRECATED);
/**
* Guesses the mime type using the PECL extension FileInfo.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated since Symfony 4.3, use {@link NewFileinfoMimeTypeGuesser} instead
*/
class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
{
@ -26,9 +31,9 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
/**
* @param string $magicFile A magic file to use with the finfo instance
*
* @see http://www.php.net/manual/en/function.finfo-open.php
* @see https://php.net/finfo-open
*/
public function __construct($magicFile = null)
public function __construct(string $magicFile = null)
{
$this->magicFile = $magicFile;
}
@ -57,11 +62,11 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
}
if (!self::isSupported()) {
return;
return null;
}
if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
return;
return null;
}
return $finfo->file($path);

View file

@ -11,8 +11,14 @@
namespace Symfony\Component\HttpFoundation\File\MimeType;
use Symfony\Component\Mime\MimeTypes;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeExtensionGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
/**
* Provides a best-guess mapping of mime type to file extension.
*
* @deprecated since Symfony 4.3, use {@link MimeTypes} instead
*/
class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
{
@ -20,11 +26,11 @@ 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.
* This list has been updated from upstream on 2019-01-14.
*
* @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
* @see https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
*/
protected $defaultExtensions = array(
protected $defaultExtensions = [
'application/andrew-inset' => 'ez',
'application/applixware' => 'aw',
'application/atom+xml' => 'atom',
@ -618,7 +624,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'audio/adpcm' => 'adp',
'audio/basic' => 'au',
'audio/midi' => 'mid',
'audio/mp4' => 'mp4a',
'audio/mp4' => 'm4a',
'audio/mpeg' => 'mpga',
'audio/ogg' => 'oga',
'audio/s3m' => 's3m',
@ -639,6 +645,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'audio/x-aiff' => 'aif',
'audio/x-caf' => 'caf',
'audio/x-flac' => 'flac',
'audio/x-hx-aac-adts' => 'aac',
'audio/x-matroska' => 'mka',
'audio/x-mpegurl' => 'm3u',
'audio/x-ms-wax' => 'wax',
@ -653,6 +660,11 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'chemical/x-cml' => 'cml',
'chemical/x-csml' => 'csml',
'chemical/x-xyz' => 'xyz',
'font/collection' => 'ttc',
'font/otf' => 'otf',
'font/ttf' => 'ttf',
'font/woff' => 'woff',
'font/woff2' => 'woff2',
'image/bmp' => 'bmp',
'image/x-ms-bmp' => 'bmp',
'image/cgm' => 'cgm',
@ -669,8 +681,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'image/tiff' => 'tiff',
'image/vnd.adobe.photoshop' => 'psd',
'image/vnd.dece.graphic' => 'uvi',
'image/vnd.dvb.subtitle' => 'sub',
'image/vnd.djvu' => 'djvu',
'image/vnd.dvb.subtitle' => 'sub',
'image/vnd.dwg' => 'dwg',
'image/vnd.dxf' => 'dxf',
'image/vnd.fastbidsheet' => 'fbs',
@ -732,8 +744,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'text/vcard' => 'vcard',
'text/vnd.curl' => 'curl',
'text/vnd.curl.dcurl' => 'dcurl',
'text/vnd.curl.scurl' => 'scurl',
'text/vnd.curl.mcurl' => 'mcurl',
'text/vnd.curl.scurl' => 'scurl',
'text/vnd.dvb.subtitle' => 'sub',
'text/vnd.fly' => 'fly',
'text/vnd.fmi.flexstor' => 'flx',
@ -747,10 +759,10 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'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-opml' => 'opml',
'text/x-pascal' => 'p',
'text/x-setext' => 'etx',
'text/x-sfv' => 'sfv',
'text/x-uuencode' => 'uu',
@ -796,13 +808,19 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'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;
if (isset($this->defaultExtensions[$mimeType])) {
return $this->defaultExtensions[$mimeType];
}
$lcMimeType = strtolower($mimeType);
return isset($this->defaultExtensions[$lcMimeType]) ? $this->defaultExtensions[$lcMimeType] : null;
}
}

View file

@ -13,6 +13,9 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\Mime\MimeTypes;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
/**
* A singleton mime type guesser.
@ -51,7 +54,7 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface
*
* @var array
*/
protected $guessers = array();
protected $guessers = [];
/**
* Returns the singleton instance.
@ -129,5 +132,7 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface
if (2 === \count($this->guessers) && !FileBinaryMimeTypeGuesser::isSupported() && !FileinfoMimeTypeGuesser::isSupported()) {
throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)');
}
return null;
}
}

View file

@ -13,11 +13,14 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\Mime\MimeTypesInterface;
/**
* Guesses the mime type of a file.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead
*/
interface MimeTypeGuesserInterface
{
@ -26,7 +29,7 @@ interface MimeTypeGuesserInterface
*
* @param string $path The path to the file
*
* @return string The mime type or NULL, if none could be guessed
* @return string|null 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

View file

@ -11,9 +11,16 @@
namespace Symfony\Component\HttpFoundation\File;
use Symfony\Component\HttpFoundation\File\Exception\CannotWriteFileException;
use Symfony\Component\HttpFoundation\File\Exception\ExtensionFileException;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
use Symfony\Component\HttpFoundation\File\Exception\FormSizeFileException;
use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException;
use Symfony\Component\HttpFoundation\File\Exception\NoFileException;
use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException;
use Symfony\Component\HttpFoundation\File\Exception\PartialFileException;
use Symfony\Component\Mime\MimeTypes;
/**
* A file uploaded through a form.
@ -27,7 +34,6 @@ class UploadedFile extends File
private $test = false;
private $originalName;
private $mimeType;
private $size;
private $error;
/**
@ -47,7 +53,6 @@ class UploadedFile extends File
* @param string $path The full temporary path to the file
* @param string $originalName The original file name of the uploaded file
* @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream
* @param int|null $size The file size provided by the uploader
* @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
* @param bool $test Whether the test mode is active
* Local files are used in test mode hence the code should not enforce HTTP uploads
@ -55,13 +60,19 @@ class UploadedFile extends File
* @throws FileException If file_uploads is disabled
* @throws FileNotFoundException If the file does not exist
*/
public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false)
public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false)
{
$this->originalName = $this->getName($originalName);
$this->mimeType = $mimeType ?: 'application/octet-stream';
$this->size = $size;
if (4 < \func_num_args() ? !\is_bool($test) : null !== $error && @filesize($path) === $error) {
@trigger_error(sprintf('Passing a size as 4th argument to the constructor of "%s" is deprecated since Symfony 4.1.', __CLASS__), E_USER_DEPRECATED);
$error = $test;
$test = 5 < \func_num_args() ? func_get_arg(5) : false;
}
$this->error = $error ?: UPLOAD_ERR_OK;
$this->test = (bool) $test;
$this->test = $test;
parent::__construct($path, UPLOAD_ERR_OK === $this->error);
}
@ -129,10 +140,7 @@ class UploadedFile extends File
*/
public function guessClientExtension()
{
$type = $this->getClientMimeType();
$guesser = ExtensionGuesser::getInstance();
return $guesser->guess($type);
return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
}
/**
@ -141,11 +149,15 @@ class UploadedFile extends File
* It is extracted from the request from which the file has been uploaded.
* Then it should not be considered as a safe value.
*
* @return int|null The file size
* @deprecated since Symfony 4.1, use getSize() instead.
*
* @return int|null The file sizes
*/
public function getClientSize()
{
return $this->size;
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED);
return $this->getSize();
}
/**
@ -204,6 +216,23 @@ class UploadedFile extends File
return $target;
}
switch ($this->error) {
case UPLOAD_ERR_INI_SIZE:
throw new IniSizeFileException($this->getErrorMessage());
case UPLOAD_ERR_FORM_SIZE:
throw new FormSizeFileException($this->getErrorMessage());
case UPLOAD_ERR_PARTIAL:
throw new PartialFileException($this->getErrorMessage());
case UPLOAD_ERR_NO_FILE:
throw new NoFileException($this->getErrorMessage());
case UPLOAD_ERR_CANT_WRITE:
throw new CannotWriteFileException($this->getErrorMessage());
case UPLOAD_ERR_NO_TMP_DIR:
throw new NoTmpDirFileException($this->getErrorMessage());
case UPLOAD_ERR_EXTENSION:
throw new ExtensionFileException($this->getErrorMessage());
}
throw new FileException($this->getErrorMessage());
}
@ -214,13 +243,24 @@ class UploadedFile extends File
*/
public static function getMaxFilesize()
{
$iniMax = strtolower(ini_get('upload_max_filesize'));
$sizePostMax = self::parseFilesize(ini_get('post_max_size'));
$sizeUploadMax = self::parseFilesize(ini_get('upload_max_filesize'));
if ('' === $iniMax) {
return PHP_INT_MAX;
return min($sizePostMax ?: PHP_INT_MAX, $sizeUploadMax ?: PHP_INT_MAX);
}
/**
* Returns the given size from an ini value in bytes.
*/
private static function parseFilesize($size): int
{
if ('' === $size) {
return 0;
}
$max = ltrim($iniMax, '+');
$size = strtolower($size);
$max = ltrim($size, '+');
if (0 === strpos($max, '0x')) {
$max = \intval($max, 16);
} elseif (0 === strpos($max, '0')) {
@ -229,7 +269,7 @@ class UploadedFile extends File
$max = (int) $max;
}
switch (substr($iniMax, -1)) {
switch (substr($size, -1)) {
case 't': $max *= 1024;
// no break
case 'g': $max *= 1024;
@ -249,7 +289,7 @@ class UploadedFile extends File
*/
public function getErrorMessage()
{
static $errors = array(
static $errors = [
UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
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.',
@ -257,7 +297,7 @@ class UploadedFile extends File
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.',
);
];
$errorCode = $this->error;
$maxFilesize = UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;

View file

@ -21,12 +21,12 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
class FileBag extends ParameterBag
{
private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
private static $fileKeys = ['error', 'name', 'size', 'tmp_name', 'type'];
/**
* @param array $parameters An array of HTTP files
* @param array|UploadedFile[] $parameters An array of HTTP files
*/
public function __construct(array $parameters = array())
public function __construct(array $parameters = [])
{
$this->replace($parameters);
}
@ -34,9 +34,9 @@ class FileBag extends ParameterBag
/**
* {@inheritdoc}
*/
public function replace(array $files = array())
public function replace(array $files = [])
{
$this->parameters = array();
$this->parameters = [];
$this->add($files);
}
@ -55,7 +55,7 @@ class FileBag extends ParameterBag
/**
* {@inheritdoc}
*/
public function add(array $files = array())
public function add(array $files = [])
{
foreach ($files as $key => $file) {
$this->set($key, $file);
@ -75,8 +75,8 @@ class FileBag extends ParameterBag
return $file;
}
$file = $this->fixPhpFilesArray($file);
if (\is_array($file)) {
$file = $this->fixPhpFilesArray($file);
$keys = array_keys($file);
sort($keys);
@ -84,10 +84,10 @@ class FileBag extends ParameterBag
if (UPLOAD_ERR_NO_FILE == $file['error']) {
$file = null;
} else {
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']);
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error'], false);
}
} else {
$file = array_map(array($this, 'convertFileInformation'), $file);
$file = array_map([$this, 'convertFileInformation'], $file);
if (array_keys($keys) === $keys) {
$file = array_filter($file);
}
@ -109,14 +109,12 @@ class FileBag extends ParameterBag
* 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);
@ -130,13 +128,13 @@ class FileBag extends ParameterBag
}
foreach ($data['name'] as $key => $name) {
$files[$key] = $this->fixPhpFilesArray(array(
$files[$key] = $this->fixPhpFilesArray([
'error' => $data['error'][$key],
'name' => $name,
'type' => $data['type'][$key],
'tmp_name' => $data['tmp_name'][$key],
'size' => $data['size'][$key],
));
]);
}
return $files;

View file

@ -18,13 +18,13 @@ namespace Symfony\Component\HttpFoundation;
*/
class HeaderBag implements \IteratorAggregate, \Countable
{
protected $headers = array();
protected $cacheControl = array();
protected const UPPER = '_ABCDEFGHIJKLMNOPQRSTUVWXYZ';
protected const LOWER = '-abcdefghijklmnopqrstuvwxyz';
/**
* @param array $headers An array of HTTP headers
*/
public function __construct(array $headers = array())
protected $headers = [];
protected $cacheControl = [];
public function __construct(array $headers = [])
{
foreach ($headers as $key => $values) {
$this->set($key, $values);
@ -46,7 +46,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
$max = max(array_map('strlen', array_keys($headers))) + 1;
$content = '';
foreach ($headers as $name => $values) {
$name = implode('-', array_map('ucfirst', explode('-', $name)));
$name = ucwords($name, '-');
foreach ($values as $value) {
$content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
}
@ -58,10 +58,16 @@ class HeaderBag implements \IteratorAggregate, \Countable
/**
* Returns the headers.
*
* @param string|null $key The name of the headers to return or null to get them all
*
* @return array An array of headers
*/
public function all()
public function all(/*string $key = null*/)
{
if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) {
return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? [];
}
return $this->headers;
}
@ -77,19 +83,15 @@ class HeaderBag implements \IteratorAggregate, \Countable
/**
* Replaces the current HTTP headers by a new set.
*
* @param array $headers An array of HTTP headers
*/
public function replace(array $headers = array())
public function replace(array $headers = [])
{
$this->headers = array();
$this->headers = [];
$this->add($headers);
}
/**
* Adds new headers the current HTTP headers set.
*
* @param array $headers An array of HTTP headers
*/
public function add(array $headers)
{
@ -101,30 +103,31 @@ class HeaderBag implements \IteratorAggregate, \Countable
/**
* Returns a header value by name.
*
* @param string $key The header name
* @param string|string[]|null $default The default value
* @param bool $first Whether to return the first value or all header values
* @param string $key The header name
* @param string|null $default The default value
*
* @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise
* @return string|null The first header value or default value
*/
public function get($key, $default = null, $first = true)
public function get($key, $default = null)
{
$key = str_replace('_', '-', strtolower($key));
$headers = $this->all();
$headers = $this->all((string) $key);
if (2 < \func_num_args()) {
@trigger_error(sprintf('Passing a third argument to "%s()" is deprecated since Symfony 4.4, use method "all()" instead', __METHOD__), E_USER_DEPRECATED);
if (!array_key_exists($key, $headers)) {
if (null === $default) {
return $first ? null : array();
if (!func_get_arg(2)) {
return $headers;
}
return $first ? $default : array($default);
}
if ($first) {
return \count($headers[$key]) ? $headers[$key][0] : $default;
if (!$headers) {
return $default;
}
return $headers[$key];
if (null === $headers[0]) {
return null;
}
return (string) $headers[0];
}
/**
@ -136,7 +139,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function set($key, $values, $replace = true)
{
$key = str_replace('_', '-', strtolower($key));
$key = strtr($key, self::UPPER, self::LOWER);
if (\is_array($values)) {
$values = array_values($values);
@ -148,7 +151,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
}
} else {
if (true === $replace || !isset($this->headers[$key])) {
$this->headers[$key] = array($values);
$this->headers[$key] = [$values];
} else {
$this->headers[$key][] = $values;
}
@ -168,7 +171,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function has($key)
{
return array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
return \array_key_exists(strtr($key, self::UPPER, self::LOWER), $this->all());
}
/**
@ -181,7 +184,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function contains($key, $value)
{
return \in_array($value, $this->get($key, null, false));
return \in_array($value, $this->all((string) $key));
}
/**
@ -191,22 +194,21 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function remove($key)
{
$key = str_replace('_', '-', strtolower($key));
$key = strtr($key, self::UPPER, self::LOWER);
unset($this->headers[$key]);
if ('cache-control' === $key) {
$this->cacheControl = array();
$this->cacheControl = [];
}
}
/**
* Returns the HTTP header value converted to a date.
*
* @param string $key The parameter key
* @param \DateTime $default The default value
* @param string $key The parameter key
*
* @return null|\DateTime The parsed DateTime or the default value if the header does not exist
* @return \DateTimeInterface|null The parsed DateTime or the default value if the header does not exist
*
* @throws \RuntimeException When the HTTP header is not parseable
*/
@ -245,7 +247,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function hasCacheControlDirective($key)
{
return array_key_exists($key, $this->cacheControl);
return \array_key_exists($key, $this->cacheControl);
}
/**
@ -257,7 +259,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function getCacheControlDirective($key)
{
return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
}
/**
@ -294,21 +296,9 @@ class HeaderBag implements \IteratorAggregate, \Countable
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);
return HeaderUtils::toString($this->cacheControl, ',');
}
/**
@ -320,12 +310,8 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
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);
}
$parts = HeaderUtils::split($header, ',=');
return $cacheControl;
return HeaderUtils::combine($parts);
}
}

View file

@ -18,7 +18,7 @@ namespace Symfony\Component\HttpFoundation;
*/
class IpUtils
{
private static $checkedIps = array();
private static $checkedIps = [];
/**
* This class should not be instantiated.
@ -38,7 +38,7 @@ class IpUtils
public static function checkIp($requestIp, $ips)
{
if (!\is_array($ips)) {
$ips = array($ips);
$ips = [$ips];
}
$method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
@ -153,4 +153,36 @@ class IpUtils
return self::$checkedIps[$cacheKey] = true;
}
/**
* Anonymizes an IP/IPv6.
*
* Removes the last byte for v4 and the last 8 bytes for v6 IPs
*/
public static function anonymize(string $ip): string
{
$wrappedIPv6 = false;
if ('[' === substr($ip, 0, 1) && ']' === substr($ip, -1, 1)) {
$wrappedIPv6 = true;
$ip = substr($ip, 1, -1);
}
$packedAddress = inet_pton($ip);
if (4 === \strlen($packedAddress)) {
$mask = '255.255.255.0';
} elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff:ffff'))) {
$mask = '::ffff:ffff:ff00';
} elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff'))) {
$mask = '::ffff:ff00';
} else {
$mask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000';
}
$ip = inet_ntop($packedAddress & inet_pton($mask));
if ($wrappedIPv6) {
$ip = '['.$ip.']';
}
return $ip;
}
}

View file

@ -18,7 +18,7 @@ namespace Symfony\Component\HttpFoundation;
* 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
* @see https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/AJAX_Security_Cheat_Sheet.md#always-return-json-with-an-object-on-the-outside
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
@ -39,7 +39,7 @@ class JsonResponse extends Response
* @param array $headers An array of response headers
* @param bool $json If the data is already a JSON string
*/
public function __construct($data = null, $status = 200, $headers = array(), $json = false)
public function __construct($data = null, int $status = 200, array $headers = [], bool $json = false)
{
parent::__construct('', $status, $headers);
@ -55,24 +55,35 @@ class JsonResponse extends Response
*
* Example:
*
* return JsonResponse::create($data, 200)
* return JsonResponse::create(['key' => 'value'])
* ->setSharedMaxAge(300);
*
* @param mixed $data The json response data
* @param mixed $data The JSON response data
* @param int $status The response status code
* @param array $headers An array of response headers
*
* @return static
*/
public static function create($data = null, $status = 200, $headers = array())
public static function create($data = null, $status = 200, $headers = [])
{
return new static($data, $status, $headers);
}
/**
* Make easier the creation of JsonResponse from raw json.
* Factory method for chainability.
*
* Example:
*
* return JsonResponse::fromJsonString('{"key": "value"}')
* ->setSharedMaxAge(300);
*
* @param string|null $data The JSON response string
* @param int $status The response status code
* @param array $headers An array of response headers
*
* @return static
*/
public static function fromJsonString($data = null, $status = 200, $headers = array())
public static function fromJsonString($data = null, $status = 200, $headers = [])
{
return new static($data, $status, $headers, true);
}
@ -89,16 +100,16 @@ class JsonResponse extends Response
public function setCallback($callback = null)
{
if (null !== $callback) {
// partially taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/
// partially taken from https://geekality.net/2011/08/03/valid-javascript-identifier/
// partially taken from https://github.com/willdurand/JsonpCallbackValidator
// JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details.
// (c) William Durand <william.durand1@gmail.com>
$pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u';
$reserved = array(
$reserved = [
'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while',
'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export',
'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false',
);
];
$parts = explode('.', $callback);
foreach ($parts as $part) {
if (!preg_match($pattern, $part) || \in_array($part, $reserved, true)) {
@ -137,31 +148,19 @@ class JsonResponse extends Response
*
* @throws \InvalidArgumentException
*/
public function setData($data = array())
public function setData($data = [])
{
if (\defined('HHVM_VERSION')) {
// HHVM does not trigger any warnings and let exceptions
// thrown from a JsonSerializable object pass through.
// If only PHP did the same...
try {
$data = json_encode($data, $this->encodingOptions);
} else {
if (!interface_exists('JsonSerializable', false)) {
set_error_handler(function () { return false; });
try {
$data = @json_encode($data, $this->encodingOptions);
} finally {
restore_error_handler();
}
} else {
try {
$data = json_encode($data, $this->encodingOptions);
} catch (\Exception $e) {
if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
throw $e->getPrevious() ?: $e;
}
throw $e;
}
} catch (\Exception $e) {
if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
throw $e->getPrevious() ?: $e;
}
throw $e;
}
if (\PHP_VERSION_ID >= 70300 && (JSON_THROW_ON_ERROR & $this->encodingOptions)) {
return $this->setJson($data);
}
if (JSON_ERROR_NONE !== json_last_error()) {

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2018 Fabien Potencier
Copyright (c) 2004-2019 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

View file

@ -23,10 +23,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
*/
protected $parameters;
/**
* @param array $parameters An array of parameters
*/
public function __construct(array $parameters = array())
public function __construct(array $parameters = [])
{
$this->parameters = $parameters;
}
@ -53,20 +50,16 @@ class ParameterBag implements \IteratorAggregate, \Countable
/**
* Replaces the current parameters by a new set.
*
* @param array $parameters An array of parameters
*/
public function replace(array $parameters = array())
public function replace(array $parameters = [])
{
$this->parameters = $parameters;
}
/**
* Adds parameters.
*
* @param array $parameters An array of parameters
*/
public function add(array $parameters = array())
public function add(array $parameters = [])
{
$this->parameters = array_replace($this->parameters, $parameters);
}
@ -81,7 +74,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
*/
public function get($key, $default = null)
{
return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
}
/**
@ -104,7 +97,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
*/
public function has($key)
{
return array_key_exists($key, $this->parameters);
return \array_key_exists($key, $this->parameters);
}
/**
@ -154,7 +147,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
public function getDigits($key, $default = '')
{
// we need to remove - and + because they're allowed in the filter
return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
return str_replace(['-', '+'], '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
}
/**
@ -174,7 +167,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
* Returns the parameter value converted to boolean.
*
* @param string $key The parameter key
* @param mixed $default The default value if the parameter key does not exist
* @param bool $default The default value if the parameter key does not exist
*
* @return bool The filtered value
*/
@ -191,17 +184,17 @@ class ParameterBag implements \IteratorAggregate, \Countable
* @param int $filter FILTER_* constant
* @param mixed $options Filter options
*
* @see http://php.net/manual/en/function.filter-var.php
* @see https://php.net/filter-var
*
* @return mixed
*/
public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array())
public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = [])
{
$value = $this->get($key, $default);
// Always turn $options into an array - this allows filter_var option shortcuts.
if (!\is_array($options) && $options) {
$options = array('flags' => $options);
$options = ['flags' => $options];
}
// Add a convenience check for arrays.

View file

@ -30,10 +30,15 @@ class RedirectResponse extends Response
*
* @throws \InvalidArgumentException
*
* @see http://tools.ietf.org/html/rfc2616#section-10.3
* @see https://tools.ietf.org/html/rfc2616#section-10.3
*/
public function __construct($url, $status = 302, $headers = array())
public function __construct(?string $url, int $status = 302, array $headers = [])
{
if (null === $url) {
@trigger_error(sprintf('Passing a null url when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED);
$url = '';
}
parent::__construct('', $status, $headers);
$this->setTargetUrl($url);
@ -42,7 +47,7 @@ class RedirectResponse extends Response
throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
}
if (301 == $status && !array_key_exists('cache-control', $headers)) {
if (301 == $status && !\array_key_exists('cache-control', array_change_key_case($headers, \CASE_LOWER))) {
$this->headers->remove('cache-control');
}
}
@ -56,7 +61,7 @@ class RedirectResponse extends Response
*
* @return static
*/
public static function create($url = '', $status = 302, $headers = array())
public static function create($url = '', $status = 302, $headers = [])
{
return new static($url, $status, $headers);
}
@ -82,7 +87,7 @@ class RedirectResponse extends Response
*/
public function setTargetUrl($url)
{
if (empty($url)) {
if ('' === ($url ?? '')) {
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
}
@ -93,7 +98,7 @@ class RedirectResponse extends Response
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="refresh" content="0;url=%1$s" />
<meta http-equiv="refresh" content="0;url=\'%1$s\'" />
<title>Redirecting to %1$s</title>
</head>

File diff suppressed because it is too large Load diff

View file

@ -29,40 +29,43 @@ class RequestMatcher implements RequestMatcherInterface
private $host;
/**
* @var string[]
* @var int|null
*/
private $methods = array();
private $port;
/**
* @var string[]
*/
private $ips = array();
private $methods = [];
/**
* @var string[]
*/
private $ips = [];
/**
* @var array
*/
private $attributes = array();
private $attributes = [];
/**
* @var string[]
*/
private $schemes = array();
private $schemes = [];
/**
* @param string|null $path
* @param string|null $host
* @param string|string[]|null $methods
* @param string|string[]|null $ips
* @param array $attributes
* @param string|string[]|null $schemes
*/
public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null)
public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null, int $port = null)
{
$this->matchPath($path);
$this->matchHost($host);
$this->matchMethod($methods);
$this->matchIps($ips);
$this->matchScheme($schemes);
$this->matchPort($port);
foreach ($attributes as $k => $v) {
$this->matchAttribute($k, $v);
@ -76,7 +79,7 @@ class RequestMatcher implements RequestMatcherInterface
*/
public function matchScheme($scheme)
{
$this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : array();
$this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : [];
}
/**
@ -89,6 +92,16 @@ class RequestMatcher implements RequestMatcherInterface
$this->host = $regexp;
}
/**
* Adds a check for the the URL port.
*
* @param int|null $port The port number to connect to
*/
public function matchPort(?int $port)
{
$this->port = $port;
}
/**
* Adds a check for the URL path info.
*
@ -116,7 +129,7 @@ class RequestMatcher implements RequestMatcherInterface
*/
public function matchIps($ips)
{
$this->ips = null !== $ips ? (array) $ips : array();
$this->ips = null !== $ips ? (array) $ips : [];
}
/**
@ -126,7 +139,7 @@ class RequestMatcher implements RequestMatcherInterface
*/
public function matchMethod($method)
{
$this->methods = null !== $method ? array_map('strtoupper', (array) $method) : array();
$this->methods = null !== $method ? array_map('strtoupper', (array) $method) : [];
}
/**
@ -167,6 +180,10 @@ class RequestMatcher implements RequestMatcherInterface
return false;
}
if (null !== $this->port && 0 < $this->port && $request->getPort() !== $this->port) {
return false;
}
if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
return true;
}

View file

@ -21,7 +21,7 @@ class RequestStack
/**
* @var Request[]
*/
private $requests = array();
private $requests = [];
/**
* Pushes a Request on the stack.
@ -47,7 +47,7 @@ class RequestStack
public function pop()
{
if (!$this->requests) {
return;
return null;
}
return array_pop($this->requests);
@ -73,7 +73,7 @@ class RequestStack
public function getMasterRequest()
{
if (!$this->requests) {
return;
return null;
}
return $this->requests[0];
@ -95,7 +95,7 @@ class RequestStack
$pos = \count($this->requests) - 2;
if (!isset($this->requests[$pos])) {
return;
return null;
}
return $this->requests[$pos];

View file

@ -88,7 +88,7 @@ class Response
const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
/**
* @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
* @var ResponseHeaderBag
*/
public $headers;
@ -121,14 +121,14 @@ class Response
* Status codes translation table.
*
* The list of codes is complete according to the
* {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry}
* {@link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml Hypertext Transfer Protocol (HTTP) Status Code Registry}
* (last updated 2016-03-01).
*
* Unless otherwise noted, the status code is defined in RFC2616.
*
* @var array
*/
public static $statusTexts = array(
public static $statusTexts = [
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing', // RFC2518
@ -191,16 +191,12 @@ class Response
508 => 'Loop Detected', // RFC5842
510 => 'Not Extended', // RFC2774
511 => 'Network Authentication Required', // RFC6585
);
];
/**
* @param mixed $content The response content, see setContent()
* @param int $status The response status code
* @param array $headers An array of response headers
*
* @throws \InvalidArgumentException When the HTTP status code is not valid
*/
public function __construct($content = '', $status = 200, $headers = array())
public function __construct($content = '', int $status = 200, array $headers = [])
{
$this->headers = new ResponseHeaderBag($headers);
$this->setContent($content);
@ -222,7 +218,7 @@ class Response
*
* @return static
*/
public static function create($content = '', $status = 200, $headers = array())
public static function create($content = '', $status = 200, $headers = [])
{
return new static($content, $status, $headers);
}
@ -274,7 +270,7 @@ class Response
} else {
// Content-type based on the Request
if (!$headers->has('Content-Type')) {
$format = $request->getRequestFormat();
$format = $request->getPreferredFormat();
if (null !== $format && $mimeType = $request->getMimeType($format)) {
$headers->set('Content-Type', $mimeType);
}
@ -310,13 +306,19 @@ class Response
}
// Check if we need to send extra expire info headers
if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) {
$this->headers->set('pragma', 'no-cache');
$this->headers->set('expires', -1);
if ('1.0' == $this->getProtocolVersion() && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
$headers->set('pragma', 'no-cache');
$headers->set('expires', -1);
}
$this->ensureIEOverSSLCompatibility($request);
if ($request->isSecure()) {
foreach ($headers->getCookies() as $cookie) {
$cookie->setSecureDefault(true);
}
}
return $this;
}
@ -334,14 +336,15 @@ class Response
// headers
foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
$replace = 0 === strcasecmp($name, 'Content-Type');
foreach ($values as $value) {
header($name.': '.$value, false, $this->statusCode);
header($name.': '.$value, $replace, $this->statusCode);
}
}
// cookies
foreach ($this->headers->getCookies() as $cookie) {
header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode);
header('Set-Cookie: '.$cookie, false, $this->statusCode);
}
// status
@ -374,7 +377,7 @@ class Response
if (\function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} elseif (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) {
} elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
static::closeOutputBuffers(0, true);
}
@ -394,7 +397,7 @@ class Response
*/
public function setContent($content)
{
if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable(array($content, '__toString'))) {
if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable([$content, '__toString'])) {
throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', \gettype($content)));
}
@ -406,7 +409,7 @@ class Response
/**
* Gets the current response content.
*
* @return string Content
* @return string|false
*/
public function getContent()
{
@ -416,13 +419,11 @@ class Response
/**
* Sets the HTTP protocol version (1.0 or 1.1).
*
* @param string $version The HTTP protocol version
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setProtocolVersion($version)
public function setProtocolVersion(string $version)
{
$this->version = $version;
@ -432,11 +433,9 @@ class Response
/**
* Gets the HTTP protocol version.
*
* @return string The HTTP protocol version
*
* @final since version 3.2
* @final
*/
public function getProtocolVersion()
public function getProtocolVersion(): string
{
return $this->version;
}
@ -447,18 +446,15 @@ class Response
* If the status text is null it will be automatically populated for the known
* status codes and left empty otherwise.
*
* @param int $code HTTP status code
* @param mixed $text HTTP status text
*
* @return $this
*
* @throws \InvalidArgumentException When the HTTP status code is not valid
*
* @final since version 3.2
* @final
*/
public function setStatusCode($code, $text = null)
public function setStatusCode(int $code, $text = null)
{
$this->statusCode = $code = (int) $code;
$this->statusCode = $code;
if ($this->isInvalid()) {
throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
}
@ -483,11 +479,9 @@ class Response
/**
* Retrieves the status code for the current web response.
*
* @return int Status code
*
* @final since version 3.2
* @final
*/
public function getStatusCode()
public function getStatusCode(): int
{
return $this->statusCode;
}
@ -495,13 +489,11 @@ class Response
/**
* Sets the response charset.
*
* @param string $charset Character set
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setCharset($charset)
public function setCharset(string $charset)
{
$this->charset = $charset;
@ -511,11 +503,9 @@ class Response
/**
* Retrieves the response charset.
*
* @return string Character set
*
* @final since version 3.2
* @final
*/
public function getCharset()
public function getCharset(): ?string
{
return $this->charset;
}
@ -535,13 +525,11 @@ class Response
* can be reused by a cache with heuristic expiration unless otherwise indicated"
* (https://tools.ietf.org/html/rfc7231#section-6.1)
*
* @return bool true if the response is worth caching, false otherwise
*
* @final since version 3.3
* @final
*/
public function isCacheable()
public function isCacheable(): bool
{
if (!\in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410])) {
return false;
}
@ -559,11 +547,9 @@ class Response
* origin. A response is considered fresh when it includes a Cache-Control/max-age
* indicator or Expires header and the calculated age is less than the freshness lifetime.
*
* @return bool true if the response is fresh, false otherwise
*
* @final since version 3.3
* @final
*/
public function isFresh()
public function isFresh(): bool
{
return $this->getTtl() > 0;
}
@ -572,11 +558,9 @@ class Response
* Returns true if the response includes headers that can be used to validate
* the response with the origin server using a conditional GET request.
*
* @return bool true if the response is validateable, false otherwise
*
* @final since version 3.3
* @final
*/
public function isValidateable()
public function isValidateable(): bool
{
return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
}
@ -588,7 +572,7 @@ class Response
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setPrivate()
{
@ -605,7 +589,7 @@ class Response
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setPublic()
{
@ -618,13 +602,11 @@ class Response
/**
* Marks the response as "immutable".
*
* @param bool $immutable enables or disables the immutable directive
*
* @return $this
*
* @final
*/
public function setImmutable($immutable = true)
public function setImmutable(bool $immutable = true)
{
if ($immutable) {
$this->headers->addCacheControlDirective('immutable');
@ -638,11 +620,9 @@ class Response
/**
* Returns true if the response is marked as "immutable".
*
* @return bool returns true if the response is marked as "immutable"; otherwise false
*
* @final
*/
public function isImmutable()
public function isImmutable(): bool
{
return $this->headers->hasCacheControlDirective('immutable');
}
@ -655,11 +635,9 @@ class Response
* When present, the TTL of the response should not be overridden to be
* greater than the value provided by the origin.
*
* @return bool true if the response must be revalidated by a cache, false otherwise
*
* @final since version 3.3
* @final
*/
public function mustRevalidate()
public function mustRevalidate(): bool
{
return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate');
}
@ -667,13 +645,11 @@ class Response
/**
* Returns the Date header as a DateTime instance.
*
* @return \DateTime A \DateTime instance
*
* @throws \RuntimeException When the header is not parseable
*
* @final since version 3.2
* @final
*/
public function getDate()
public function getDate(): ?\DateTimeInterface
{
return $this->headers->getDate('Date');
}
@ -683,30 +659,32 @@ class Response
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setDate(\DateTime $date)
public function setDate(\DateTimeInterface $date)
{
$date->setTimezone(new \DateTimeZone('UTC'));
if ($date instanceof \DateTime) {
$date = \DateTimeImmutable::createFromMutable($date);
}
$date = $date->setTimezone(new \DateTimeZone('UTC'));
$this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
return $this;
}
/**
* Returns the age of the response.
* Returns the age of the response in seconds.
*
* @return int The age of the response in seconds
*
* @final since version 3.2
* @final
*/
public function getAge()
public function getAge(): int
{
if (null !== $age = $this->headers->get('Age')) {
return (int) $age;
}
return max(time() - $this->getDate()->format('U'), 0);
return max(time() - (int) $this->getDate()->format('U'), 0);
}
/**
@ -718,6 +696,7 @@ class Response
{
if ($this->isFresh()) {
$this->headers->set('Age', $this->getMaxAge());
$this->headers->remove('Expires');
}
return $this;
@ -726,17 +705,15 @@ class Response
/**
* Returns the value of the Expires header as a DateTime instance.
*
* @return \DateTime|null A DateTime instance or null if the header does not exist
*
* @final since version 3.2
* @final
*/
public function getExpires()
public function getExpires(): ?\DateTimeInterface
{
try {
return $this->headers->getDate('Expires');
} catch (\RuntimeException $e) {
// according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past
return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
return \DateTime::createFromFormat('U', time() - 172800);
}
}
@ -745,22 +722,25 @@ class Response
*
* Passing null as value will remove the header.
*
* @param \DateTime|null $date A \DateTime instance or null to remove the header
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setExpires(\DateTime $date = null)
public function setExpires(\DateTimeInterface $date = null)
{
if (null === $date) {
$this->headers->remove('Expires');
} else {
$date = clone $date;
$date->setTimezone(new \DateTimeZone('UTC'));
$this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
return $this;
}
if ($date instanceof \DateTime) {
$date = \DateTimeImmutable::createFromMutable($date);
}
$date = $date->setTimezone(new \DateTimeZone('UTC'));
$this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
return $this;
}
@ -771,11 +751,9 @@ class Response
* First, it checks for a s-maxage directive, then a max-age directive, and then it falls
* back on an expires header. It returns null when no maximum age can be established.
*
* @return int|null Number of seconds
*
* @final since version 3.2
* @final
*/
public function getMaxAge()
public function getMaxAge(): ?int
{
if ($this->headers->hasCacheControlDirective('s-maxage')) {
return (int) $this->headers->getCacheControlDirective('s-maxage');
@ -786,8 +764,10 @@ class Response
}
if (null !== $this->getExpires()) {
return $this->getExpires()->format('U') - $this->getDate()->format('U');
return (int) $this->getExpires()->format('U') - (int) $this->getDate()->format('U');
}
return null;
}
/**
@ -795,13 +775,11 @@ class Response
*
* This methods sets the Cache-Control max-age directive.
*
* @param int $value Number of seconds
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setMaxAge($value)
public function setMaxAge(int $value)
{
$this->headers->addCacheControlDirective('max-age', $value);
@ -813,13 +791,11 @@ class Response
*
* This methods sets the Cache-Control s-maxage directive.
*
* @param int $value Number of seconds
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setSharedMaxAge($value)
public function setSharedMaxAge(int $value)
{
$this->setPublic();
$this->headers->addCacheControlDirective('s-maxage', $value);
@ -835,29 +811,25 @@ class Response
* When the responses TTL is <= 0, the response may not be served from cache without first
* revalidating with the origin.
*
* @return int|null The TTL in seconds
*
* @final since version 3.2
* @final
*/
public function getTtl()
public function getTtl(): ?int
{
if (null !== $maxAge = $this->getMaxAge()) {
return $maxAge - $this->getAge();
}
$maxAge = $this->getMaxAge();
return null !== $maxAge ? $maxAge - $this->getAge() : null;
}
/**
* Sets the response's time-to-live for shared caches.
* Sets the response's time-to-live for shared caches in seconds.
*
* This method adjusts the Cache-Control/s-maxage directive.
*
* @param int $seconds Number of seconds
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setTtl($seconds)
public function setTtl(int $seconds)
{
$this->setSharedMaxAge($this->getAge() + $seconds);
@ -865,17 +837,15 @@ class Response
}
/**
* Sets the response's time-to-live for private/client caches.
* Sets the response's time-to-live for private/client caches in seconds.
*
* This method adjusts the Cache-Control/max-age directive.
*
* @param int $seconds Number of seconds
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setClientTtl($seconds)
public function setClientTtl(int $seconds)
{
$this->setMaxAge($this->getAge() + $seconds);
@ -885,13 +855,11 @@ class Response
/**
* Returns the Last-Modified HTTP header as a DateTime instance.
*
* @return \DateTime|null A DateTime instance or null if the header does not exist
*
* @throws \RuntimeException When the HTTP header is not parseable
*
* @final since version 3.2
* @final
*/
public function getLastModified()
public function getLastModified(): ?\DateTimeInterface
{
return $this->headers->getDate('Last-Modified');
}
@ -901,33 +869,34 @@ class Response
*
* Passing null as value will remove the header.
*
* @param \DateTime|null $date A \DateTime instance or null to remove the header
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setLastModified(\DateTime $date = null)
public function setLastModified(\DateTimeInterface $date = null)
{
if (null === $date) {
$this->headers->remove('Last-Modified');
} else {
$date = clone $date;
$date->setTimezone(new \DateTimeZone('UTC'));
$this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
return $this;
}
if ($date instanceof \DateTime) {
$date = \DateTimeImmutable::createFromMutable($date);
}
$date = $date->setTimezone(new \DateTimeZone('UTC'));
$this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
return $this;
}
/**
* Returns the literal value of the ETag HTTP header.
*
* @return string|null The ETag HTTP header or null if it does not exist
*
* @final since version 3.2
* @final
*/
public function getEtag()
public function getEtag(): ?string
{
return $this->headers->get('ETag');
}
@ -940,9 +909,9 @@ class Response
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setEtag($etag = null, $weak = false)
public function setEtag(string $etag = null, bool $weak = false)
{
if (null === $etag) {
$this->headers->remove('Etag');
@ -962,18 +931,16 @@ class Response
*
* Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable.
*
* @param array $options An array of cache options
*
* @return $this
*
* @throws \InvalidArgumentException
*
* @final since version 3.3
* @final
*/
public function setCache(array $options)
{
if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'))) {
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff))));
if ($diff = array_diff(array_keys($options), ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'])) {
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff)));
}
if (isset($options['etag'])) {
@ -1023,9 +990,9 @@ class Response
*
* @return $this
*
* @see http://tools.ietf.org/html/rfc2616#section-10.3.5
* @see https://tools.ietf.org/html/rfc2616#section-10.3.5
*
* @final since version 3.3
* @final
*/
public function setNotModified()
{
@ -1033,7 +1000,7 @@ class Response
$this->setContent(null);
// remove headers that MUST NOT be included with 304 Not Modified responses
foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
foreach (['Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified'] as $header) {
$this->headers->remove($header);
}
@ -1043,11 +1010,9 @@ class Response
/**
* Returns true if the response includes a Vary header.
*
* @return bool true if the response includes a Vary header, false otherwise
*
* @final since version 3.2
* @final
*/
public function hasVary()
public function hasVary(): bool
{
return null !== $this->headers->get('Vary');
}
@ -1055,17 +1020,15 @@ class Response
/**
* Returns an array of header names given in the Vary header.
*
* @return array An array of Vary names
*
* @final since version 3.2
* @final
*/
public function getVary()
public function getVary(): array
{
if (!$vary = $this->headers->get('Vary', null, false)) {
return array();
if (!$vary = $this->headers->all('Vary')) {
return [];
}
$ret = array();
$ret = [];
foreach ($vary as $item) {
$ret = array_merge($ret, preg_split('/[\s,]+/', $item));
}
@ -1081,9 +1044,9 @@ class Response
*
* @return $this
*
* @final since version 3.2
* @final
*/
public function setVary($headers, $replace = true)
public function setVary($headers, bool $replace = true)
{
$this->headers->set('Vary', $headers, $replace);
@ -1099,9 +1062,9 @@ class Response
*
* @return bool true if the Response validators match the Request, false otherwise
*
* @final since version 3.3
* @final
*/
public function isNotModified(Request $request)
public function isNotModified(Request $request): bool
{
if (!$request->isMethodCacheable()) {
return false;
@ -1129,13 +1092,11 @@ class Response
/**
* Is response invalid?
*
* @return bool
* @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
*
* @final since version 3.2
* @final
*/
public function isInvalid()
public function isInvalid(): bool
{
return $this->statusCode < 100 || $this->statusCode >= 600;
}
@ -1143,11 +1104,9 @@ class Response
/**
* Is response informative?
*
* @return bool
*
* @final since version 3.3
* @final
*/
public function isInformational()
public function isInformational(): bool
{
return $this->statusCode >= 100 && $this->statusCode < 200;
}
@ -1155,11 +1114,9 @@ class Response
/**
* Is response successful?
*
* @return bool
*
* @final since version 3.2
* @final
*/
public function isSuccessful()
public function isSuccessful(): bool
{
return $this->statusCode >= 200 && $this->statusCode < 300;
}
@ -1167,11 +1124,9 @@ class Response
/**
* Is the response a redirect?
*
* @return bool
*
* @final since version 3.2
* @final
*/
public function isRedirection()
public function isRedirection(): bool
{
return $this->statusCode >= 300 && $this->statusCode < 400;
}
@ -1179,11 +1134,9 @@ class Response
/**
* Is there a client error?
*
* @return bool
*
* @final since version 3.2
* @final
*/
public function isClientError()
public function isClientError(): bool
{
return $this->statusCode >= 400 && $this->statusCode < 500;
}
@ -1191,11 +1144,9 @@ class Response
/**
* Was there a server side error?
*
* @return bool
*
* @final since version 3.3
* @final
*/
public function isServerError()
public function isServerError(): bool
{
return $this->statusCode >= 500 && $this->statusCode < 600;
}
@ -1203,11 +1154,9 @@ class Response
/**
* Is the response OK?
*
* @return bool
*
* @final since version 3.2
* @final
*/
public function isOk()
public function isOk(): bool
{
return 200 === $this->statusCode;
}
@ -1215,11 +1164,9 @@ class Response
/**
* Is the response forbidden?
*
* @return bool
*
* @final since version 3.2
* @final
*/
public function isForbidden()
public function isForbidden(): bool
{
return 403 === $this->statusCode;
}
@ -1227,11 +1174,9 @@ class Response
/**
* Is the response a not found error?
*
* @return bool
*
* @final since version 3.2
* @final
*/
public function isNotFound()
public function isNotFound(): bool
{
return 404 === $this->statusCode;
}
@ -1239,27 +1184,21 @@ class Response
/**
* Is the response a redirect of some form?
*
* @param string $location
*
* @return bool
*
* @final since version 3.2
* @final
*/
public function isRedirect($location = null)
public function isRedirect(string $location = null): bool
{
return \in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location'));
}
/**
* Is the response empty?
*
* @return bool
*
* @final since version 3.2
* @final
*/
public function isEmpty()
public function isEmpty(): bool
{
return \in_array($this->statusCode, array(204, 304));
return \in_array($this->statusCode, [204, 304]);
}
/**
@ -1267,17 +1206,13 @@ class Response
*
* Resulting level can be greater than target level if a non-removable buffer has been encountered.
*
* @param int $targetLevel The target output buffering level
* @param bool $flush Whether to flush or clean the buffers
*
* @final since version 3.3
* @final
*/
public static function closeOutputBuffers($targetLevel, $flush)
public static function closeOutputBuffers(int $targetLevel, bool $flush): void
{
$status = ob_get_status(true);
$level = \count($status);
// PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3
$flags = \defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
$flags = PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE);
while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) {
if ($flush) {
@ -1293,9 +1228,9 @@ class Response
*
* @see http://support.microsoft.com/kb/323308
*
* @final since version 3.3
* @final
*/
protected function ensureIEOverSSLCompatibility(Request $request)
protected function ensureIEOverSSLCompatibility(Request $request): void
{
if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) && true === $request->isSecure()) {
if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) {

View file

@ -24,11 +24,11 @@ class ResponseHeaderBag extends HeaderBag
const DISPOSITION_ATTACHMENT = 'attachment';
const DISPOSITION_INLINE = 'inline';
protected $computedCacheControl = array();
protected $cookies = array();
protected $headerNames = array();
protected $computedCacheControl = [];
protected $cookies = [];
protected $headerNames = [];
public function __construct(array $headers = array())
public function __construct(array $headers = [])
{
parent::__construct($headers);
@ -49,9 +49,9 @@ class ResponseHeaderBag extends HeaderBag
*/
public function allPreserveCase()
{
$headers = array();
$headers = [];
foreach ($this->all() as $name => $value) {
$headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value;
$headers[$this->headerNames[$name] ?? $name] = $value;
}
return $headers;
@ -70,9 +70,9 @@ class ResponseHeaderBag extends HeaderBag
/**
* {@inheritdoc}
*/
public function replace(array $headers = array())
public function replace(array $headers = [])
{
$this->headerNames = array();
$this->headerNames = [];
parent::replace($headers);
@ -87,10 +87,19 @@ class ResponseHeaderBag extends HeaderBag
/**
* {@inheritdoc}
*
* @param string|null $key The name of the headers to return or null to get them all
*/
public function all()
public function all(/*string $key = null*/)
{
$headers = parent::all();
if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) {
$key = strtr($key, self::UPPER, self::LOWER);
return 'set-cookie' !== $key ? $headers[$key] ?? [] : array_map('strval', $this->getCookies());
}
foreach ($this->getCookies() as $cookie) {
$headers['set-cookie'][] = (string) $cookie;
}
@ -103,11 +112,11 @@ class ResponseHeaderBag extends HeaderBag
*/
public function set($key, $values, $replace = true)
{
$uniqueKey = str_replace('_', '-', strtolower($key));
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
if ('set-cookie' === $uniqueKey) {
if ($replace) {
$this->cookies = array();
$this->cookies = [];
}
foreach ((array) $values as $cookie) {
$this->setCookie(Cookie::fromString($cookie));
@ -122,9 +131,8 @@ class ResponseHeaderBag extends HeaderBag
parent::set($key, $values, $replace);
// ensure the cache-control header has sensible defaults
if (\in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'), true)) {
$computed = $this->computeCacheControlValue();
$this->headers['cache-control'] = array($computed);
if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true) && '' !== $computed = $this->computeCacheControlValue()) {
$this->headers['cache-control'] = [$computed];
$this->headerNames['cache-control'] = 'Cache-Control';
$this->computedCacheControl = $this->parseCacheControl($computed);
}
@ -135,11 +143,11 @@ class ResponseHeaderBag extends HeaderBag
*/
public function remove($key)
{
$uniqueKey = str_replace('_', '-', strtolower($key));
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
unset($this->headerNames[$uniqueKey]);
if ('set-cookie' === $uniqueKey) {
$this->cookies = array();
$this->cookies = [];
return;
}
@ -147,7 +155,7 @@ class ResponseHeaderBag extends HeaderBag
parent::remove($key);
if ('cache-control' === $uniqueKey) {
$this->computedCacheControl = array();
$this->computedCacheControl = [];
}
if ('date' === $uniqueKey) {
@ -160,7 +168,7 @@ class ResponseHeaderBag extends HeaderBag
*/
public function hasCacheControlDirective($key)
{
return array_key_exists($key, $this->computedCacheControl);
return \array_key_exists($key, $this->computedCacheControl);
}
/**
@ -168,7 +176,7 @@ class ResponseHeaderBag extends HeaderBag
*/
public function getCacheControlDirective($key)
{
return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
}
public function setCookie(Cookie $cookie)
@ -216,15 +224,15 @@ class ResponseHeaderBag extends HeaderBag
*/
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 (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) {
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY])));
}
if (self::COOKIES_ARRAY === $format) {
return $this->cookies;
}
$flattenedCookies = array();
$flattenedCookies = [];
foreach ($this->cookies as $path) {
foreach ($path as $cookies) {
foreach ($cookies as $cookie) {
@ -247,56 +255,15 @@ class ResponseHeaderBag extends HeaderBag
*/
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
{
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
}
/**
* 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
* @see HeaderUtils::makeDisposition()
*/
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;
return HeaderUtils::makeDisposition((string) $disposition, (string) $filename, (string) $filenameFallback);
}
/**
@ -309,13 +276,13 @@ class ResponseHeaderBag extends HeaderBag
*/
protected function computeCacheControlValue()
{
if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
return 'no-cache, private';
}
if (!$this->cacheControl) {
if ($this->has('Last-Modified') || $this->has('Expires')) {
return 'private, must-revalidate'; // allows for heuristic expiration (RFC 7234 Section 4.2.2) in the case of "Last-Modified"
}
// conservative by default
return 'private, must-revalidate';
return 'no-cache, private';
}
$header = $this->getCacheControlHeader();
@ -331,7 +298,7 @@ class ResponseHeaderBag extends HeaderBag
return $header;
}
private function initDate()
private function initDate(): void
{
$now = \DateTime::createFromFormat('U', time());
$now->setTimezone(new \DateTimeZone('UTC'));

View file

@ -27,14 +27,11 @@ class ServerBag extends ParameterBag
*/
public function getHeaders()
{
$headers = array();
$contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
$headers = [];
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])) {
} elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) {
$headers[$key] = $value;
}
}
@ -46,13 +43,13 @@ class ServerBag extends ParameterBag
/*
* 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}]
* RewriteCond %{HTTP:Authorization} .+
* RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
*
* A sample .htaccess file:
* RewriteEngine On
* RewriteCond %{HTTP:Authorization} ^(.+)$
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
* RewriteCond %{HTTP:Authorization} .+
* RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
* RewriteCond %{REQUEST_FILENAME} !-f
* RewriteRule ^(.*)$ app.php [QSA,L]
*/
@ -79,7 +76,7 @@ class ServerBag extends ParameterBag
/*
* XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables,
* I'll just set $headers['AUTHORIZATION'] here.
* http://php.net/manual/en/reserved.variables.server.php
* https://php.net/reserved.variables.server
*/
$headers['AUTHORIZATION'] = $authorizationHeader;
}

View file

@ -19,12 +19,12 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
private $name = 'attributes';
private $storageKey;
protected $attributes = array();
protected $attributes = [];
/**
* @param string $storageKey The key used to store attributes in the session
*/
public function __construct($storageKey = '_sf2_attributes')
public function __construct(string $storageKey = '_sf2_attributes')
{
$this->storageKey = $storageKey;
}
@ -63,7 +63,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
*/
public function has($name)
{
return array_key_exists($name, $this->attributes);
return \array_key_exists($name, $this->attributes);
}
/**
@ -71,7 +71,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
*/
public function get($name, $default = null)
{
return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
}
/**
@ -95,7 +95,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
*/
public function replace(array $attributes)
{
$this->attributes = array();
$this->attributes = [];
foreach ($attributes as $key => $value) {
$this->set($key, $value);
}
@ -107,7 +107,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
public function remove($name)
{
$retval = null;
if (array_key_exists($name, $this->attributes)) {
if (\array_key_exists($name, $this->attributes)) {
$retval = $this->attributes[$name];
unset($this->attributes[$name]);
}
@ -121,7 +121,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
public function clear()
{
$return = $this->attributes;
$this->attributes = array();
$this->attributes = [];
return $return;
}

View file

@ -50,15 +50,10 @@ interface AttributeBagInterface extends SessionBagInterface
/**
* Returns attributes.
*
* @return array Attributes
* @return array
*/
public function all();
/**
* Sets attributes.
*
* @param array $attributes Attributes
*/
public function replace(array $attributes);
/**

View file

@ -25,7 +25,7 @@ class NamespacedAttributeBag extends AttributeBag
* @param string $storageKey Session storage key
* @param string $namespaceCharacter Namespace character to use in keys
*/
public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/')
public function __construct(string $storageKey = '_sf2_attributes', string $namespaceCharacter = '/')
{
$this->namespaceCharacter = $namespaceCharacter;
parent::__construct($storageKey);
@ -44,7 +44,7 @@ class NamespacedAttributeBag extends AttributeBag
return false;
}
return array_key_exists($name, $attributes);
return \array_key_exists($name, $attributes);
}
/**
@ -60,7 +60,7 @@ class NamespacedAttributeBag extends AttributeBag
return $default;
}
return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
return \array_key_exists($name, $attributes) ? $attributes[$name] : $default;
}
/**
@ -81,7 +81,7 @@ class NamespacedAttributeBag extends AttributeBag
$retval = null;
$attributes = &$this->resolveAttributePath($name);
$name = $this->resolveKey($name);
if (null !== $attributes && array_key_exists($name, $attributes)) {
if (null !== $attributes && \array_key_exists($name, $attributes)) {
$retval = $attributes[$name];
unset($attributes[$name]);
}
@ -97,7 +97,7 @@ class NamespacedAttributeBag extends AttributeBag
* @param string $name Key name
* @param bool $writeContext Write context, default false
*
* @return array
* @return array|null
*/
protected function &resolveAttributePath($name, $writeContext = false)
{
@ -115,7 +115,7 @@ class NamespacedAttributeBag extends AttributeBag
return $array;
}
$array[$parts[0]] = array();
$array[$parts[0]] = [];
return $array;
}
@ -123,14 +123,14 @@ class NamespacedAttributeBag extends AttributeBag
unset($parts[\count($parts) - 1]);
foreach ($parts as $part) {
if (null !== $array && !array_key_exists($part, $array)) {
if (null !== $array && !\array_key_exists($part, $array)) {
if (!$writeContext) {
$null = null;
return $null;
}
$array[$part] = array();
$array[$part] = [];
}
$array = &$array[$part];

View file

@ -19,13 +19,13 @@ namespace Symfony\Component\HttpFoundation\Session\Flash;
class AutoExpireFlashBag implements FlashBagInterface
{
private $name = 'flashes';
private $flashes = array('display' => array(), 'new' => array());
private $flashes = ['display' => [], 'new' => []];
private $storageKey;
/**
* @param string $storageKey The key used to store flashes in the session
*/
public function __construct($storageKey = '_symfony_flashes')
public function __construct(string $storageKey = '_symfony_flashes')
{
$this->storageKey = $storageKey;
}
@ -53,8 +53,8 @@ class AutoExpireFlashBag implements FlashBagInterface
// 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();
$this->flashes['display'] = \array_key_exists('new', $this->flashes) ? $this->flashes['new'] : [];
$this->flashes['new'] = [];
}
/**
@ -68,7 +68,7 @@ class AutoExpireFlashBag implements FlashBagInterface
/**
* {@inheritdoc}
*/
public function peek($type, array $default = array())
public function peek($type, array $default = [])
{
return $this->has($type) ? $this->flashes['display'][$type] : $default;
}
@ -78,13 +78,13 @@ class AutoExpireFlashBag implements FlashBagInterface
*/
public function peekAll()
{
return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array();
return \array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : [];
}
/**
* {@inheritdoc}
*/
public function get($type, array $default = array())
public function get($type, array $default = [])
{
$return = $default;
@ -106,7 +106,7 @@ class AutoExpireFlashBag implements FlashBagInterface
public function all()
{
$return = $this->flashes['display'];
$this->flashes['display'] = array();
$this->flashes['display'] = [];
return $return;
}
@ -132,7 +132,7 @@ class AutoExpireFlashBag implements FlashBagInterface
*/
public function has($type)
{
return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
}
/**

View file

@ -19,13 +19,13 @@ namespace Symfony\Component\HttpFoundation\Session\Flash;
class FlashBag implements FlashBagInterface
{
private $name = 'flashes';
private $flashes = array();
private $flashes = [];
private $storageKey;
/**
* @param string $storageKey The key used to store flashes in the session
*/
public function __construct($storageKey = '_symfony_flashes')
public function __construct(string $storageKey = '_symfony_flashes')
{
$this->storageKey = $storageKey;
}
@ -62,7 +62,7 @@ class FlashBag implements FlashBagInterface
/**
* {@inheritdoc}
*/
public function peek($type, array $default = array())
public function peek($type, array $default = [])
{
return $this->has($type) ? $this->flashes[$type] : $default;
}
@ -78,7 +78,7 @@ class FlashBag implements FlashBagInterface
/**
* {@inheritdoc}
*/
public function get($type, array $default = array())
public function get($type, array $default = [])
{
if (!$this->has($type)) {
return $default;
@ -97,7 +97,7 @@ class FlashBag implements FlashBagInterface
public function all()
{
$return = $this->peekAll();
$this->flashes = array();
$this->flashes = [];
return $return;
}
@ -123,7 +123,7 @@ class FlashBag implements FlashBagInterface
*/
public function has($type)
{
return array_key_exists($type, $this->flashes) && $this->flashes[$type];
return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
}
/**

View file

@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
interface FlashBagInterface extends SessionBagInterface
{
/**
* Adds a flash message for type.
* Adds a flash message for the given type.
*
* @param string $type
* @param mixed $message
@ -29,12 +29,12 @@ interface FlashBagInterface extends SessionBagInterface
public function add($type, $message);
/**
* Registers a message for a given type.
* Registers one or more messages for a given type.
*
* @param string $type
* @param string|array $message
* @param string|array $messages
*/
public function set($type, $message);
public function set($type, $messages);
/**
* Gets flash messages for a given type.
@ -44,7 +44,7 @@ interface FlashBagInterface extends SessionBagInterface
*
* @return array
*/
public function peek($type, array $default = array());
public function peek($type, array $default = []);
/**
* Gets all flash messages.
@ -61,7 +61,7 @@ interface FlashBagInterface extends SessionBagInterface
*
* @return array
*/
public function get($type, array $default = array());
public function get($type, array $default = []);
/**
* Gets and clears flashes from the stack.

View file

@ -28,14 +28,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
private $flashName;
private $attributeName;
private $data = array();
private $data = [];
private $usageIndex = 0;
/**
* @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();
@ -134,29 +129,22 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
/**
* Returns the number of attributes.
*
* @return int The number of attributes
* @return int
*/
public function count()
{
return \count($this->getAttributeBag()->all());
}
/**
* @return int
*
* @internal
*/
public function getUsageIndex()
public function &getUsageIndex(): int
{
return $this->usageIndex;
}
/**
* @return bool
*
* @internal
*/
public function isEmpty()
public function isEmpty(): bool
{
if ($this->isStarted()) {
++$this->usageIndex;
@ -209,7 +197,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function setId($id)
{
$this->storage->setId($id);
if ($this->storage->getId() !== $id) {
$this->storage->setId($id);
}
}
/**
@ -251,7 +241,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function getBag($name)
{
return $this->storage->getBag($name)->getBag();
$bag = $this->storage->getBag($name);
return method_exists($bag, 'getBag') ? $bag->getBag() : $bag;
}
/**
@ -268,10 +260,8 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
* Gets the attributebag interface.
*
* Note that this method was added to help with IDE autocompletion.
*
* @return AttributeBagInterface
*/
private function getAttributeBag()
private function getAttributeBag(): AttributeBagInterface
{
return $this->getBag($this->attributeName);
}

View file

@ -23,7 +23,7 @@ interface SessionInterface
/**
* Starts the session storage.
*
* @return bool True if session started
* @return bool
*
* @throws \RuntimeException if session fails to start
*/
@ -32,7 +32,7 @@ interface SessionInterface
/**
* Returns the session ID.
*
* @return string The session ID
* @return string
*/
public function getId();
@ -46,7 +46,7 @@ interface SessionInterface
/**
* Returns the session name.
*
* @return mixed The session name
* @return string
*/
public function getName();
@ -68,7 +68,7 @@ interface SessionInterface
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*
* @return bool True if session invalidated, false if error
* @return bool
*/
public function invalidate($lifetime = null);
@ -82,7 +82,7 @@ interface SessionInterface
* to expire with browser session. Time is in seconds, and is
* not a Unix timestamp.
*
* @return bool True if session migrated, false if error
* @return bool
*/
public function migrate($destroy = false, $lifetime = null);
@ -100,7 +100,7 @@ interface SessionInterface
*
* @param string $name The attribute name
*
* @return bool true if the attribute is defined, false otherwise
* @return bool
*/
public function has($name);
@ -125,14 +125,12 @@ interface SessionInterface
/**
* Returns attributes.
*
* @return array Attributes
* @return array
*/
public function all();
/**
* Sets attributes.
*
* @param array $attributes Attributes
*/
public function replace(array $attributes);

View file

@ -1,120 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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;
@trigger_error(sprintf('The class %s is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.', MemcacheSessionHandler::class), E_USER_DEPRECATED);
/**
* @author Drak <drak@zikula.org>
*
* @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.
*/
class MemcacheSessionHandler implements \SessionHandlerInterface
{
private $memcache;
/**
* @var int 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 true;
}
/**
* {@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)
{
$this->memcache->delete($this->prefix.$sessionId);
return true;
}
/**
* {@inheritdoc}
*/
public function gc($maxlifetime)
{
// not required here because memcache will auto expire the records anyhow.
return true;
}
/**
* Return a Memcache instance.
*
* @return \Memcache
*/
protected function getMemcache()
{
return $this->memcache;
}
}

View file

@ -15,7 +15,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
* Memcached based session storage handler based on the Memcached class
* provided by the PHP memcached extension.
*
* @see http://php.net/memcached
* @see https://php.net/memcached
*
* @author Drak <drak@zikula.org>
*/
@ -40,19 +40,14 @@ class MemcachedSessionHandler extends AbstractSessionHandler
* * 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())
public function __construct(\Memcached $memcached, array $options = [])
{
$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)
));
if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) {
throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
}
$this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
@ -60,11 +55,11 @@ class MemcachedSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function close()
{
return true;
return $this->memcached->quit();
}
/**
@ -76,7 +71,7 @@ class MemcachedSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function updateTimestamp($sessionId, $data)
{
@ -104,7 +99,7 @@ class MemcachedSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function gc($maxlifetime)
{

View file

@ -17,14 +17,14 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
* @author Markus Bachmann <markus.bachmann@bachi.biz>
*
* @see https://packagist.org/packages/mongodb/mongodb
* @see http://php.net/manual/en/set.mongodb.php
* @see https://php.net/mongodb
*/
class MongoDbSessionHandler extends AbstractSessionHandler
{
private $mongo;
/**
* @var \MongoCollection
* @var \MongoDB\Collection
*/
private $collection;
@ -56,43 +56,31 @@ class MongoDbSessionHandler extends AbstractSessionHandler
* { "expireAfterSeconds": 0 }
* )
*
* More details on: http://docs.mongodb.org/manual/tutorial/expire-data/
* More details on: https://docs.mongodb.org/manual/tutorial/expire-data/
*
* If you use such an index, you can drop `gc_probability` to 0 since
* no garbage-collection is required.
*
* @param \MongoDB\Client $mongo A MongoDB\Client 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)
public function __construct(\MongoDB\Client $mongo, array $options)
{
if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) {
@trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED);
}
if (!($mongo instanceof \MongoDB\Client || $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(
$this->options = array_merge([
'id_field' => '_id',
'data_field' => 'data',
'time_field' => 'time',
'expiry_field' => 'expires_at',
), $options);
], $options);
}
/**
* {@inheritdoc}
* @return bool
*/
public function close()
{
@ -104,25 +92,21 @@ class MongoDbSessionHandler extends AbstractSessionHandler
*/
protected function doDestroy($sessionId)
{
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
$this->getCollection()->$methodName(array(
$this->getCollection()->deleteOne([
$this->options['id_field'] => $sessionId,
));
]);
return true;
}
/**
* {@inheritdoc}
* @return bool
*/
public function gc($maxlifetime)
{
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove';
$this->getCollection()->$methodName(array(
$this->options['expiry_field'] => array('$lt' => $this->createDateTime()),
));
$this->getCollection()->deleteMany([
$this->options['expiry_field'] => ['$lt' => new \MongoDB\BSON\UTCDateTime()],
]);
return true;
}
@ -132,55 +116,36 @@ class MongoDbSessionHandler extends AbstractSessionHandler
*/
protected function doWrite($sessionId, $data)
{
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
$fields = array(
$this->options['time_field'] => $this->createDateTime(),
$fields = [
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
$this->options['expiry_field'] => $expiry,
);
$this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY),
];
$options = array('upsert' => true);
if ($this->mongo instanceof \MongoDB\Client) {
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
} else {
$fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY);
$options['multiple'] = false;
}
$methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update';
$this->getCollection()->$methodName(
array($this->options['id_field'] => $sessionId),
array('$set' => $fields),
$options
$this->getCollection()->updateOne(
[$this->options['id_field'] => $sessionId],
['$set' => $fields],
['upsert' => true]
);
return true;
}
/**
* {@inheritdoc}
* @return bool
*/
public function updateTimestamp($sessionId, $data)
{
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
if ($this->mongo instanceof \MongoDB\Client) {
$methodName = 'updateOne';
$options = array();
} else {
$methodName = 'update';
$options = array('multiple' => false);
}
$this->getCollection()->$methodName(
array($this->options['id_field'] => $sessionId),
array('$set' => array(
$this->options['time_field'] => $this->createDateTime(),
$this->getCollection()->updateOne(
[$this->options['id_field'] => $sessionId],
['$set' => [
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
$this->options['expiry_field'] => $expiry,
)),
$options
]]
);
return true;
@ -191,28 +156,19 @@ class MongoDbSessionHandler extends AbstractSessionHandler
*/
protected function doRead($sessionId)
{
$dbData = $this->getCollection()->findOne(array(
$dbData = $this->getCollection()->findOne([
$this->options['id_field'] => $sessionId,
$this->options['expiry_field'] => array('$gte' => $this->createDateTime()),
));
$this->options['expiry_field'] => ['$gte' => new \MongoDB\BSON\UTCDateTime()],
]);
if (null === $dbData) {
return '';
}
if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) {
return $dbData[$this->options['data_field']]->getData();
}
return $dbData[$this->options['data_field']]->bin;
return $dbData[$this->options['data_field']]->getData();
}
/**
* Return a "MongoCollection" instance.
*
* @return \MongoCollection
*/
private function getCollection()
private function getCollection(): \MongoDB\Collection
{
if (null === $this->collection) {
$this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
@ -222,34 +178,10 @@ class MongoDbSessionHandler extends AbstractSessionHandler
}
/**
* Return a Mongo instance.
*
* @return \Mongo|\MongoClient|\MongoDB\Client
* @return \MongoDB\Client
*/
protected function getMongo()
{
return $this->mongo;
}
/**
* Create a date object using the class appropriate for the current mongo connection.
*
* Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime
*
* @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now.
*
* @return \MongoDate|\MongoDB\BSON\UTCDateTime
*/
private function createDateTime($seconds = null)
{
if (null === $seconds) {
$seconds = time();
}
if ($this->mongo instanceof \MongoDB\Client) {
return new \MongoDB\BSON\UTCDateTime($seconds * 1000);
}
return new \MongoDate($seconds);
}
}

View file

@ -16,19 +16,19 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
*
* @author Drak <drak@zikula.org>
*/
class NativeFileSessionHandler extends NativeSessionHandler
class NativeFileSessionHandler extends \SessionHandler
{
/**
* @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.
* @see https://php.net/session.configuration#ini.session.save-path for further details.
*
* @throws \InvalidArgumentException On invalid $savePath
* @throws \RuntimeException When failing to create the save directory
*/
public function __construct($savePath = null)
public function __construct(string $savePath = null)
{
if (null === $savePath) {
$savePath = ini_get('session.save_path');

View file

@ -1,24 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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;
/**
* @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead.
* @see http://php.net/sessionhandler
*/
class NativeSessionHandler extends \SessionHandler
{
public function __construct()
{
@trigger_error('The '.__NAMESPACE__.'\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.', E_USER_DEPRECATED);
}
}

View file

@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
class NullSessionHandler extends AbstractSessionHandler
{
/**
* {@inheritdoc}
* @return bool
*/
public function close()
{
@ -27,7 +27,7 @@ class NullSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function validateId($sessionId)
{
@ -43,7 +43,7 @@ class NullSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function updateTimestamp($sessionId, $data)
{
@ -67,7 +67,7 @@ class NullSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function gc($maxlifetime)
{

View file

@ -32,7 +32,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
* Saving it in a character column could corrupt the data. You can use createTable()
* to initialize a correctly defined table.
*
* @see http://php.net/sessionhandlerinterface
* @see https://php.net/sessionhandlerinterface
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Michael Williams <michael.williams@funsational.com>
@ -65,13 +65,15 @@ class PdoSessionHandler extends AbstractSessionHandler
*/
const LOCK_TRANSACTIONAL = 2;
private const MAX_LIFETIME = 315576000;
/**
* @var \PDO|null PDO instance or null when not connected yet
*/
private $pdo;
/**
* @var string|null|false DSN string or null for session.save_path or false when lazy connection disabled
* @var string|false|null DSN string or null for session.save_path or false when lazy connection disabled
*/
private $dsn = false;
@ -118,7 +120,7 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* @var array Connection options when lazy-connect
*/
private $connectionOptions = array();
private $connectionOptions = [];
/**
* @var int The strategy for locking, see constants
@ -130,7 +132,7 @@ class PdoSessionHandler extends AbstractSessionHandler
*
* @var \PDOStatement[] An array of statements to release advisory locks
*/
private $unlockStatements = array();
private $unlockStatements = [];
/**
* @var bool True when the current session exists but expired according to session.gc_maxlifetime
@ -161,15 +163,14 @@ class PdoSessionHandler extends AbstractSessionHandler
* * db_time_col: The column where to store the timestamp [default: sess_time]
* * db_username: The username when lazy-connect [default: '']
* * db_password: The password when lazy-connect [default: '']
* * db_connection_options: An array of driver-specific connection options [default: array()]
* * db_connection_options: An array of driver-specific connection options [default: []]
* * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL]
*
* @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null
* @param array $options An associative array of options
*
* @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
*/
public function __construct($pdoOrDsn = null, array $options = array())
public function __construct($pdoOrDsn = null, array $options = [])
{
if ($pdoOrDsn instanceof \PDO) {
if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
@ -218,7 +219,7 @@ class PdoSessionHandler extends AbstractSessionHandler
// - trailing space removal
// - case-insensitivity
// - language processing like é == e
$sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB";
$sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_bin, ENGINE = InnoDB";
break;
case 'sqlite':
$sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
@ -238,6 +239,7 @@ class PdoSessionHandler extends AbstractSessionHandler
try {
$this->pdo->exec($sql);
$this->pdo->exec("CREATE INDEX EXPIRY ON $this->table ($this->lifetimeCol)");
} catch (\PDOException $e) {
$this->rollback();
@ -258,7 +260,7 @@ class PdoSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function open($savePath, $sessionName)
{
@ -272,7 +274,7 @@ class PdoSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return string
*/
public function read($sessionId)
{
@ -286,7 +288,7 @@ class PdoSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function gc($maxlifetime)
{
@ -365,18 +367,18 @@ class PdoSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function updateTimestamp($sessionId, $data)
{
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
$expiry = time() + (int) ini_get('session.gc_maxlifetime');
try {
$updateStmt = $this->pdo->prepare(
"UPDATE $this->table SET $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"
"UPDATE $this->table SET $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id"
);
$updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
$updateStmt->bindParam(':expiry', $expiry, \PDO::PARAM_INT);
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$updateStmt->execute();
} catch (\PDOException $e) {
@ -389,7 +391,7 @@ class PdoSessionHandler extends AbstractSessionHandler
}
/**
* {@inheritdoc}
* @return bool
*/
public function close()
{
@ -403,14 +405,21 @@ class PdoSessionHandler extends AbstractSessionHandler
$this->gcCalled = false;
// delete the session records that have expired
if ('mysql' === $this->driver) {
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time";
} else {
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time - $this->timeCol";
}
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time AND $this->lifetimeCol > :min";
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
$stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT);
$stmt->execute();
// to be removed in 6.0
if ('mysql' === $this->driver) {
$legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol + $this->timeCol < :time";
} else {
$legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol < :time - $this->timeCol";
}
$stmt = $this->pdo->prepare($legacySql);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
$stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT);
$stmt->execute();
}
@ -423,10 +432,8 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Lazy-connects to the database.
*
* @param string $dsn DSN string
*/
private function connect($dsn)
private function connect(string $dsn): void
{
$this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
@ -436,13 +443,9 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Builds a PDO DSN from a URL-like connection string.
*
* @param string $dsnOrUrl
*
* @return string
*
* @todo implement missing support for oci DSN (which look totally different from other PDO ones)
*/
private function buildDsnFromUrl($dsnOrUrl)
private function buildDsnFromUrl(string $dsnOrUrl): string
{
// (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
$url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl);
@ -468,13 +471,13 @@ class PdoSessionHandler extends AbstractSessionHandler
throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler');
}
$driverAliasMap = array(
$driverAliasMap = [
'mssql' => 'sqlsrv',
'mysql2' => 'mysql', // Amazon RDS, for some weird reason
'postgres' => 'pgsql',
'postgresql' => 'pgsql',
'sqlite3' => 'sqlite',
);
];
$driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme'];
@ -538,10 +541,10 @@ class PdoSessionHandler extends AbstractSessionHandler
* PDO::rollback or PDO::inTransaction for SQLite.
*
* Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions
* due to http://www.mysqlperformanceblog.com/2013/12/12/one-more-innodb-gap-lock-to-avoid/ .
* due to https://percona.com/blog/2013/12/12/one-more-innodb-gap-lock-to-avoid/ .
* So we change it to READ COMMITTED.
*/
private function beginTransaction()
private function beginTransaction(): void
{
if (!$this->inTransaction) {
if ('sqlite' === $this->driver) {
@ -559,7 +562,7 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Helper method to commit a transaction.
*/
private function commit()
private function commit(): void
{
if ($this->inTransaction) {
try {
@ -581,7 +584,7 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Helper method to rollback a transaction.
*/
private function rollback()
private function rollback(): void
{
// We only need to rollback if we are in a transaction. Otherwise the resulting
// error would hide the real problem why rollback was called. We might not be
@ -623,7 +626,12 @@ class PdoSessionHandler extends AbstractSessionHandler
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
if ($sessionRows) {
if ($sessionRows[0][1] + $sessionRows[0][2] < time()) {
$expiry = (int) $sessionRows[0][1];
if ($expiry <= self::MAX_LIFETIME) {
$expiry += $sessionRows[0][2];
}
if ($expiry < time()) {
$this->sessionExpired = true;
return '';
@ -637,7 +645,7 @@ class PdoSessionHandler extends AbstractSessionHandler
throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.');
}
if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
if (!filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN) && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
// In strict mode, session fixation is not possible: new sessions always start with a unique
// random id, so that concurrency is not possible and this code path can be skipped.
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
@ -668,8 +676,6 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Executes an application-level lock on the database.
*
* @param string $sessionId Session ID
*
* @return \PDOStatement The statement that needs to be executed later to release the lock
*
* @throws \DomainException When an unsupported PDO driver is used
@ -678,12 +684,12 @@ class PdoSessionHandler extends AbstractSessionHandler
* - for oci using DBMS_LOCK.REQUEST
* - for sqlsrv using sp_getapplock with LockOwner = Session
*/
private function doAdvisoryLock($sessionId)
private function doAdvisoryLock(string $sessionId): \PDOStatement
{
switch ($this->driver) {
case 'mysql':
// MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced.
$lockId = \substr($sessionId, 0, 64);
$lockId = substr($sessionId, 0, 64);
// should we handle the return value? 0 on timeout, null on error
// we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout
$stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)');
@ -733,12 +739,8 @@ class PdoSessionHandler extends AbstractSessionHandler
* Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer.
*
* Keep in mind, PHP integers are signed.
*
* @param string $string
*
* @return int
*/
private function convertStringToInt($string)
private function convertStringToInt(string $string): int
{
if (4 === \PHP_INT_SIZE) {
return (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]);
@ -753,15 +755,14 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Return a locking or nonlocking SQL query to read session information.
*
* @return string The SQL string
*
* @throws \DomainException When an unsupported PDO driver is used
*/
private function getSelectSql()
private function getSelectSql(): string
{
if (self::LOCK_TRANSACTIONAL === $this->lockMode) {
$this->beginTransaction();
// selecting the time column should be removed in 6.0
switch ($this->driver) {
case 'mysql':
case 'oci':
@ -782,32 +783,26 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Returns an insert statement supported by the database for writing session data.
*
* @param string $sessionId Session ID
* @param string $sessionData Encoded session data
* @param int $maxlifetime session.gc_maxlifetime
*
* @return \PDOStatement The insert statement
*/
private function getInsertStatement($sessionId, $sessionData, $maxlifetime)
private function getInsertStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement
{
switch ($this->driver) {
case 'oci':
$data = fopen('php://memory', 'r+');
fwrite($data, $sessionData);
rewind($data);
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :lifetime, :time) RETURNING $this->dataCol into :data";
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :expiry, :time) RETURNING $this->dataCol into :data";
break;
default:
$data = $sessionData;
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)";
break;
}
$stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
$stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
$stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
return $stmt;
@ -815,32 +810,26 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Returns an update statement supported by the database for writing session data.
*
* @param string $sessionId Session ID
* @param string $sessionData Encoded session data
* @param int $maxlifetime session.gc_maxlifetime
*
* @return \PDOStatement The update statement
*/
private function getUpdateStatement($sessionId, $sessionData, $maxlifetime)
private function getUpdateStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement
{
switch ($this->driver) {
case 'oci':
$data = fopen('php://memory', 'r+');
fwrite($data, $sessionData);
rewind($data);
$sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
$sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
break;
default:
$data = $sessionData;
$sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id";
$sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id";
break;
}
$stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
$stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
$stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
return $stmt;
@ -848,36 +837,30 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
*
* @param string $sessionId Session ID
* @param string $data Encoded session data
* @param int $maxlifetime session.gc_maxlifetime
*
* @return \PDOStatement|null The merge statement or null when not supported
*/
private function getMergeStatement($sessionId, $data, $maxlifetime)
private function getMergeStatement(string $sessionId, string $data, int $maxlifetime): ?\PDOStatement
{
switch (true) {
case 'mysql' === $this->driver:
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ".
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
break;
case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='):
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
// It also requires HOLDLOCK according to https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/
$mergeSql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
break;
case 'sqlite' === $this->driver:
$mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
$mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)";
break;
case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ".
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
break;
default:
// MERGE is not supported with LOBs: http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html
// MERGE is not supported with LOBs: https://oracle.com/technetwork/articles/fuecks-lobs-095315.html
return null;
}
@ -887,15 +870,15 @@ class PdoSessionHandler extends AbstractSessionHandler
$mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
$mergeStmt->bindParam(4, $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(4, time() + $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(5, time(), \PDO::PARAM_INT);
$mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB);
$mergeStmt->bindParam(7, $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(7, time() + $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(8, time(), \PDO::PARAM_INT);
} else {
$mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
$mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
}

View file

@ -1,92 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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;
/**
* Wraps another SessionHandlerInterface to only write the session when it has been modified.
*
* @author Adrien Brault <adrien.brault@gmail.com>
*
* @deprecated since version 3.4, to be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.
*/
class WriteCheckSessionHandler implements \SessionHandlerInterface
{
private $wrappedSessionHandler;
/**
* @var array sessionId => session
*/
private $readSessions;
public function __construct(\SessionHandlerInterface $wrappedSessionHandler)
{
@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', self::class), E_USER_DEPRECATED);
$this->wrappedSessionHandler = $wrappedSessionHandler;
}
/**
* {@inheritdoc}
*/
public function close()
{
return $this->wrappedSessionHandler->close();
}
/**
* {@inheritdoc}
*/
public function destroy($sessionId)
{
return $this->wrappedSessionHandler->destroy($sessionId);
}
/**
* {@inheritdoc}
*/
public function gc($maxlifetime)
{
return $this->wrappedSessionHandler->gc($maxlifetime);
}
/**
* {@inheritdoc}
*/
public function open($savePath, $sessionName)
{
return $this->wrappedSessionHandler->open($savePath, $sessionName);
}
/**
* {@inheritdoc}
*/
public function read($sessionId)
{
$session = $this->wrappedSessionHandler->read($sessionId);
$this->readSessions[$sessionId] = $session;
return $session;
}
/**
* {@inheritdoc}
*/
public function write($sessionId, $data)
{
if (isset($this->readSessions[$sessionId]) && $data === $this->readSessions[$sessionId]) {
return true;
}
return $this->wrappedSessionHandler->write($sessionId, $data);
}
}

View file

@ -39,7 +39,7 @@ class MetadataBag implements SessionBagInterface
/**
* @var array
*/
protected $meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0);
protected $meta = [self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0];
/**
* Unix timestamp.
@ -57,7 +57,7 @@ class MetadataBag implements SessionBagInterface
* @param string $storageKey The key used to store bag in the session
* @param int $updateThreshold The time to wait between two UPDATED updates
*/
public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0)
public function __construct(string $storageKey = '_sf2_meta', int $updateThreshold = 0)
{
$this->storageKey = $storageKey;
$this->updateThreshold = $updateThreshold;
@ -159,7 +159,7 @@ class MetadataBag implements SessionBagInterface
$this->name = $name;
}
private function stampCreated($lifetime = null)
private function stampCreated(int $lifetime = null): void
{
$timeStamp = time();
$this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp;

View file

@ -50,7 +50,7 @@ class MockArraySessionStorage implements SessionStorageInterface
/**
* @var array
*/
protected $data = array();
protected $data = [];
/**
* @var MetadataBag
@ -60,13 +60,9 @@ class MockArraySessionStorage implements SessionStorageInterface
/**
* @var array|SessionBagInterface[]
*/
protected $bags = array();
protected $bags = [];
/**
* @param string $name Session name
* @param MetadataBag $metaBag MetadataBag instance
*/
public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null)
public function __construct(string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
{
$this->name = $name;
$this->setMetadataBag($metaBag);
@ -170,7 +166,7 @@ class MockArraySessionStorage implements SessionStorageInterface
}
// clear out the session
$this->data = array();
$this->data = [];
// reconnect the bags to the session
$this->loadSession();
@ -242,11 +238,11 @@ class MockArraySessionStorage implements SessionStorageInterface
protected function loadSession()
{
$bags = array_merge($this->bags, array($this->metadataBag));
$bags = array_merge($this->bags, [$this->metadataBag]);
foreach ($bags as $bag) {
$key = $bag->getStorageKey();
$this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array();
$this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : [];
$bag->initialize($this->data[$key]);
}

View file

@ -27,11 +27,10 @@ class MockFileSessionStorage extends MockArraySessionStorage
private $savePath;
/**
* @param string $savePath Path of directory to save session files
* @param string $name Session name
* @param MetadataBag $metaBag MetadataBag instance
* @param string $savePath Path of directory to save session files
* @param string $name Session name
*/
public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null)
public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
{
if (null === $savePath) {
$savePath = sys_get_temp_dir();
@ -98,7 +97,7 @@ class MockFileSessionStorage extends MockArraySessionStorage
unset($data[$key]);
}
}
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($data)) {
if ([$key = $this->metadataBag->getStorageKey()] === array_keys($data)) {
unset($data[$key]);
}
@ -122,7 +121,7 @@ class MockFileSessionStorage extends MockArraySessionStorage
* Deletes a session from persistent storage.
* Deliberately leaves session data in memory intact.
*/
private function destroy()
private function destroy(): void
{
if (is_file($this->getFilePath())) {
unlink($this->getFilePath());
@ -131,10 +130,8 @@ class MockFileSessionStorage extends MockArraySessionStorage
/**
* Calculate path to file.
*
* @return string File path
*/
private function getFilePath()
private function getFilePath(): string
{
return $this->savePath.'/'.$this->id.'.mocksess';
}
@ -142,10 +139,10 @@ class MockFileSessionStorage extends MockArraySessionStorage
/**
* Reads session from storage and loads session.
*/
private function read()
private function read(): void
{
$filePath = $this->getFilePath();
$this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array();
$this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : [];
$this->loadSession();
}

View file

@ -12,6 +12,7 @@
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionUtils;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
@ -26,7 +27,7 @@ class NativeSessionStorage implements SessionStorageInterface
/**
* @var SessionBagInterface[]
*/
protected $bags = array();
protected $bags = [];
/**
* @var bool
@ -48,13 +49,18 @@ class NativeSessionStorage implements SessionStorageInterface
*/
protected $metadataBag;
/**
* @var string|null
*/
private $emulateSameSite;
/**
* 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
* @see https://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
@ -67,13 +73,10 @@ class NativeSessionStorage implements SessionStorageInterface
* cookie_lifetime, "0"
* cookie_path, "/"
* cookie_secure, ""
* entropy_file, ""
* entropy_length, "0"
* cookie_samesite, null
* gc_divisor, "100"
* gc_maxlifetime, "1440"
* gc_probability, "1"
* hash_bits_per_character, "4"
* hash_function, "0"
* lazy_write, "1"
* name, "PHPSESSID"
* referer_check, ""
@ -94,18 +97,21 @@ class NativeSessionStorage implements SessionStorageInterface
* trans_sid_hosts, $_SERVER['HTTP_HOST']
* trans_sid_tags, "a=href,area=href,frame=src,form="
*
* @param array $options Session configuration options
* @param \SessionHandlerInterface|null $handler
* @param MetadataBag $metaBag MetadataBag
* @param AbstractProxy|\SessionHandlerInterface|null $handler
*/
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null)
{
$options += array(
if (!\extension_loaded('session')) {
throw new \LogicException('PHP extension "session" is required.');
}
$options += [
'cache_limiter' => '',
'cache_expire' => 0,
'use_cookies' => 1,
'lazy_write' => 1,
);
'use_strict_mode' => 1,
];
session_register_shutdown();
@ -137,7 +143,7 @@ class NativeSessionStorage implements SessionStorageInterface
throw new \RuntimeException('Failed to start the session: already started by PHP.');
}
if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
if (filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN) && 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));
}
@ -146,6 +152,13 @@ class NativeSessionStorage implements SessionStorageInterface
throw new \RuntimeException('Failed to start the session');
}
if (null !== $this->emulateSameSite) {
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
if (null !== $originalCookie) {
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
}
}
$this->loadSession();
return true;
@ -208,9 +221,16 @@ class NativeSessionStorage implements SessionStorageInterface
$isRegenerated = session_regenerate_id($destroy);
// The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
// @see https://bugs.php.net/bug.php?id=70013
// @see https://bugs.php.net/70013
$this->loadSession();
if (null !== $this->emulateSameSite) {
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
if (null !== $originalCookie) {
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
}
}
return $isRegenerated;
}
@ -219,6 +239,7 @@ class NativeSessionStorage implements SessionStorageInterface
*/
public function save()
{
// Store a copy so we can restore the bags in case the session was not left empty
$session = $_SESSION;
foreach ($this->bags as $bag) {
@ -226,32 +247,29 @@ class NativeSessionStorage implements SessionStorageInterface
unset($_SESSION[$key]);
}
}
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) {
if ([$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) {
unset($_SESSION[$key]);
}
// Register custom error handler to catch a possible failure warning during session write
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline);
}, E_WARNING);
try {
$e = null;
session_write_close();
} catch (\ErrorException $e) {
} finally {
restore_error_handler();
$_SESSION = $session;
}
if (null !== $e) {
// The default PHP error message is not very helpful, as it does not give any information on the current save handler.
// Therefore, we catch this error and trigger a warning with a better error message
$handler = $this->getSaveHandler();
if ($handler instanceof SessionHandlerProxy) {
$handler = $handler->getHandler();
// Register error handler to add information about the current save handler
$previousHandler = set_error_handler(function ($type, $msg, $file, $line) use (&$previousHandler) {
if (E_WARNING === $type && 0 === strpos($msg, 'session_write_close():')) {
$handler = $this->saveHandler instanceof SessionHandlerProxy ? $this->saveHandler->getHandler() : $this->saveHandler;
$msg = sprintf('session_write_close(): Failed to write session data with "%s" handler', \get_class($handler));
}
trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', \get_class($handler)), E_USER_WARNING);
return $previousHandler ? $previousHandler($type, $msg, $file, $line) : false;
});
try {
session_write_close();
} finally {
restore_error_handler();
// Restore only if not empty
if ($_SESSION) {
$_SESSION = $session;
}
}
$this->closed = true;
@ -269,7 +287,7 @@ class NativeSessionStorage implements SessionStorageInterface
}
// clear out the session
$_SESSION = array();
$_SESSION = [];
// reconnect the bags to the session
$this->loadSession();
@ -338,9 +356,9 @@ class NativeSessionStorage implements SessionStorageInterface
* 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)
* @param array $options Session ini directives [key => value]
*
* @see http://php.net/session.configuration
* @see https://php.net/session.configuration
*/
public function setOptions(array $options)
{
@ -348,21 +366,26 @@ class NativeSessionStorage implements SessionStorageInterface
return;
}
$validOptions = array_flip(array(
$validOptions = array_flip([
'cache_expire', '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', 'lazy_write', 'name', 'referer_check',
'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite',
'gc_divisor', 'gc_maxlifetime', 'gc_probability',
'lazy_write', 'name', 'referer_check',
'serialize_handler', 'use_strict_mode', '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',
'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
));
]);
foreach ($options as $key => $value) {
if (isset($validOptions[$key])) {
if ('cookie_samesite' === $key && \PHP_VERSION_ID < 70300) {
// PHP < 7.3 does not support same_site cookies. We will emulate it in
// the start() method instead.
$this->emulateSameSite = $value;
continue;
}
ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
}
}
@ -378,15 +401,13 @@ class NativeSessionStorage implements SessionStorageInterface
* ini_set('session.save_path', '/tmp');
*
* or pass in a \SessionHandler instance which configures session.save_handler in the
* constructor, for a template see NativeFileSessionHandler or use handlers in
* composer package drak/native-session
* constructor, for a template see NativeFileSessionHandler.
*
* @see http://php.net/session-set-save-handler
* @see http://php.net/sessionhandlerinterface
* @see http://php.net/sessionhandler
* @see http://github.com/drak/NativeSession
* @see https://php.net/session-set-save-handler
* @see https://php.net/sessionhandlerinterface
* @see https://php.net/sessionhandler
*
* @param \SessionHandlerInterface|null $saveHandler
* @param AbstractProxy|\SessionHandlerInterface|null $saveHandler
*
* @throws \InvalidArgumentException
*/
@ -429,11 +450,11 @@ class NativeSessionStorage implements SessionStorageInterface
$session = &$_SESSION;
}
$bags = array_merge($this->bags, array($this->metadataBag));
$bags = array_merge($this->bags, [$this->metadataBag]);
foreach ($bags as $bag) {
$key = $bag->getStorageKey();
$session[$key] = isset($session[$key]) ? $session[$key] : array();
$session[$key] = isset($session[$key]) && \is_array($session[$key]) ? $session[$key] : [];
$bag->initialize($session[$key]);
}

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
/**
* Allows session to be started by PHP and managed by Symfony.
*
@ -19,11 +21,14 @@ namespace Symfony\Component\HttpFoundation\Session\Storage;
class PhpBridgeSessionStorage extends NativeSessionStorage
{
/**
* @param \SessionHandlerInterface|null $handler
* @param MetadataBag $metaBag MetadataBag
* @param AbstractProxy|\SessionHandlerInterface|null $handler
*/
public function __construct($handler = null, MetadataBag $metaBag = null)
{
if (!\extension_loaded('session')) {
throw new \LogicException('PHP extension "session" is required.');
}
$this->setMetadataBag($metaBag);
$this->setSaveHandler($handler);
}

View file

@ -31,7 +31,7 @@ abstract class AbstractProxy
/**
* Gets the session.save_handler name.
*
* @return string
* @return string|null
*/
public function getSaveHandlerName()
{

View file

@ -1,40 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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;
@trigger_error('The '.__NAMESPACE__.'\NativeProxy class is deprecated since Symfony 3.4 and will be removed in 4.0. Use your session handler implementation directly.', E_USER_DEPRECATED);
/**
* This proxy is built-in session handlers in PHP 5.3.x.
*
* @deprecated since version 3.4, to be removed in 4.0. Use your session handler implementation directly.
*
* @author Drak <drak@zikula.org>
*/
class NativeProxy extends AbstractProxy
{
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 bool False
*/
public function isWrapper()
{
return false;
}
}

View file

@ -36,7 +36,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
// \SessionHandlerInterface
/**
* {@inheritdoc}
* @return bool
*/
public function open($savePath, $sessionName)
{
@ -44,7 +44,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
}
/**
* {@inheritdoc}
* @return bool
*/
public function close()
{
@ -52,7 +52,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
}
/**
* {@inheritdoc}
* @return string
*/
public function read($sessionId)
{
@ -60,7 +60,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
}
/**
* {@inheritdoc}
* @return bool
*/
public function write($sessionId, $data)
{
@ -68,7 +68,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
}
/**
* {@inheritdoc}
* @return bool
*/
public function destroy($sessionId)
{
@ -76,7 +76,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
}
/**
* {@inheritdoc}
* @return bool
*/
public function gc($maxlifetime)
{
@ -84,7 +84,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
}
/**
* {@inheritdoc}
* @return bool
*/
public function validateId($sessionId)
{
@ -92,7 +92,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
}
/**
* {@inheritdoc}
* @return bool
*/
public function updateTimestamp($sessionId, $data)
{

View file

@ -77,7 +77,7 @@ interface SessionStorageInterface
* only delete the session data from persistent storage.
*
* Care: When regenerating the session ID no locking is involved in PHP's
* session design. See https://bugs.php.net/bug.php?id=61470 for a discussion.
* session design. See https://bugs.php.net/61470 for a discussion.
* So you must make sure the regenerated session is saved BEFORE sending the
* headers with the new ID. Symfony's HttpKernel offers a listener for this.
* See Symfony\Component\HttpKernel\EventListener\SaveSessionListener.

View file

@ -17,7 +17,7 @@ namespace Symfony\Component\HttpFoundation;
* 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
* to stream the response back to the client. The flush() function
* can also be used if needed.
*
* @see flush()
@ -35,7 +35,7 @@ class StreamedResponse extends Response
* @param int $status The response status code
* @param array $headers An array of response headers
*/
public function __construct(callable $callback = null, $status = 200, $headers = array())
public function __construct(callable $callback = null, int $status = 200, array $headers = [])
{
parent::__construct(null, $status, $headers);
@ -55,7 +55,7 @@ class StreamedResponse extends Response
*
* @return static
*/
public static function create($callback = null, $status = 200, $headers = array())
public static function create($callback = null, $status = 200, $headers = [])
{
return new static($callback, $status, $headers);
}
@ -63,8 +63,6 @@ class StreamedResponse extends Response
/**
* Sets the PHP callback associated with this Response.
*
* @param callable $callback A valid PHP callback
*
* @return $this
*/
public function setCallback(callable $callback)
@ -111,7 +109,7 @@ class StreamedResponse extends Response
throw new \LogicException('The Response callback must not be null.');
}
\call_user_func($this->callback);
($this->callback)();
return $this;
}
@ -129,28 +127,16 @@ class StreamedResponse extends Response
throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
}
$this->streamed = true;
return $this;
}
/**
* {@inheritdoc}
*
* @return false
*/
public function getContent()
{
return false;
}
/**
* {@inheritdoc}
*
* @return $this
*/
public function setNotModified()
{
$this->setCallback(function () {});
return parent::setNotModified();
}
}

View file

@ -1,113 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
class AcceptHeaderItemTest extends 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

@ -1,103 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\AcceptHeader;
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
class AcceptHeaderTest extends 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

@ -1,93 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\ApacheRequest;
class ApacheRequestTest extends 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

@ -1,352 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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\File\Stream;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\Tests\File\FakeFile;
class BinaryFileResponseTest extends ResponseTestCase
{
public function testConstruction()
{
$file = __DIR__.'/../README.md';
$response = new BinaryFileResponse($file, 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($file, 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'));
}
public function testConstructWithNonAsciiFilename()
{
touch(sys_get_temp_dir().'/fööö.html');
$response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, array(), true, 'attachment');
@unlink(sys_get_temp_dir().'/fööö.html');
$this->assertSame('fööö.html', $response->getFile()->getFilename());
}
/**
* @expectedException \LogicException
*/
public function testSetContent()
{
$response = new BinaryFileResponse(__FILE__);
$response->setContent('foo');
}
public function testGetContent()
{
$response = new BinaryFileResponse(__FILE__);
$this->assertFalse($response->getContent());
}
public function testSetContentDispositionGeneratesSafeFallbackFilename()
{
$response = new BinaryFileResponse(__FILE__);
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'föö.html');
$this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition'));
}
public function testSetContentDispositionGeneratesSafeFallbackFilenameForWronglyEncodedFilename()
{
$response = new BinaryFileResponse(__FILE__);
$iso88591EncodedFilename = utf8_decode('föö.html');
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $iso88591EncodedFilename);
// the parameter filename* is invalid in this case (rawurldecode('f%F6%F6') does not provide a UTF-8 string but an ISO-8859-1 encoded one)
$this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%F6%F6.html', $response->headers->get('Content-Disposition'));
}
/**
* @dataProvider provideRanges
*/
public function testRequests($requestRange, $offset, $length, $responseRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->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($responseRange, $response->headers->get('Content-Range'));
$this->assertSame($length, $response->headers->get('Content-Length'));
}
/**
* @dataProvider provideRanges
*/
public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
// do a request to get the LastModified
$request = Request::create('/');
$response->prepare($request);
$lastModified = $response->headers->get('Last-Modified');
// prepare a request for a range of the testing file
$request = Request::create('/');
$request->headers->set('If-Range', $lastModified);
$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($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=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'),
);
}
public function testRangeRequestsWithoutLastModifiedDate()
{
// prevent auto last modified
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'), true, null, false, false);
// prepare a request for a range of the testing file
$request = Request::create('/');
$request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT');
$request->headers->set('Range', 'bytes=1-4');
$this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif'));
$response = clone $response;
$response->prepare($request);
$response->sendContent();
$this->assertEquals(200, $response->getStatusCode());
$this->assertNull($response->headers->get('Content-Range'));
}
/**
* @dataProvider provideFullFileRanges
*/
public function testFullFileRequests($requestRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
// prepare a request for a range of the testing file
$request = Request::create('/');
$request->headers->set('Range', $requestRange);
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
$data = fread($file, 35);
fclose($file);
$this->expectOutputString($data);
$response = clone $response;
$response->prepare($request);
$response->sendContent();
$this->assertEquals(200, $response->getStatusCode());
}
public function provideFullFileRanges()
{
return array(
array('bytes=0-'),
array('bytes=0-34'),
array('bytes=-35'),
// Syntactical invalid range-request should also return the full resource
array('bytes=20-10'),
array('bytes=50-40'),
);
}
/**
* @dataProvider provideInvalidRanges
*/
public function testInvalidRequests($requestRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
// prepare a request for a range of the testing file
$request = Request::create('/');
$request->headers->set('Range', $requestRange);
$response = clone $response;
$response->prepare($request);
$response->sendContent();
$this->assertEquals(416, $response->getStatusCode());
$this->assertEquals('bytes */35', $response->headers->get('Content-Range'));
}
public function provideInvalidRanges()
{
return array(
array('bytes=-40'),
array('bytes=30-40'),
);
}
/**
* @dataProvider provideXSendfileFiles
*/
public function testXSendfile($file)
{
$request = Request::create('/');
$request->headers->set('X-Sendfile-Type', 'X-Sendfile');
BinaryFileResponse::trustXSendfileTypeHeader();
$response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream'));
$response->prepare($request);
$this->expectOutputString('');
$response->sendContent();
$this->assertContains('README.md', $response->headers->get('X-Sendfile'));
}
public function provideXSendfileFiles()
{
return array(
array(__DIR__.'/../README.md'),
array('file://'.__DIR__.'/../README.md'),
);
}
/**
* @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 = new FakeFile($realpath, __DIR__.'/File/Fixtures/test');
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream'));
$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 testDeleteFileAfterSend()
{
$request = Request::create('/');
$path = __DIR__.'/File/Fixtures/to_delete';
touch($path);
$realPath = realpath($path);
$this->assertFileExists($realPath);
$response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream'));
$response->deleteFileAfterSend(true);
$response->prepare($request);
$response->sendContent();
$this->assertFileNotExists($path);
}
public function testAcceptRangeOnUnsafeMethods()
{
$request = Request::create('/', 'POST');
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
$response->prepare($request);
$this->assertEquals('none', $response->headers->get('Accept-Ranges'));
}
public function testAcceptRangeNotOverriden()
{
$request = Request::create('/', 'POST');
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
$response->headers->set('Accept-Ranges', 'foo');
$response->prepare($request);
$this->assertEquals('foo', $response->headers->get('Accept-Ranges'));
}
public function getSampleXAccelMappings()
{
return array(
array('/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'),
array('/home/foo/bar.txt', '/var/www/=/files/,/home/foo/=/baz/', '/baz/bar.txt'),
);
}
public function testStream()
{
$request = Request::create('/');
$response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, array('Content-Type' => 'text/plain'));
$response->prepare($request);
$this->assertNull($response->headers->get('Content-Length'));
}
protected function provideResponse()
{
return new BinaryFileResponse(__DIR__.'/../README.md', 200, array('Content-Type' => 'application/octet-stream'));
}
public static function tearDownAfterClass()
{
$path = __DIR__.'/../Fixtures/to_delete';
if (file_exists($path)) {
@unlink($path);
}
}
}

View file

@ -1,235 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Cookie;
/**
* CookieTest.
*
* @author John Kary <john@johnkary.net>
* @author Hugo Hamon <hugo.hamon@sensio.com>
*
* @group time-sensitive
*/
class CookieTest extends 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
*/
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
{
new Cookie($name);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testInvalidExpiration()
{
new Cookie('MyCookie', 'foo', 'bar');
}
public function testNegativeExpirationIsNotPossible()
{
$cookie = new Cookie('foo', 'bar', -100);
$this->assertSame(0, $cookie->getExpiresTime());
}
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');
$this->assertEquals(0, $cookie->getExpiresTime(), '->getExpiresTime() returns the default expire date');
$cookie = new Cookie('foo', 'bar', $expire = time() + 3600);
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
public function testGetExpiresTimeIsCastToInt()
{
$cookie = new Cookie('foo', 'bar', 3600.9);
$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
}
public function testConstructorWithDateTime()
{
$expire = new \DateTime();
$cookie = new Cookie('foo', 'bar', $expire);
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
/**
* @requires PHP 5.5
*/
public function testConstructorWithDateTimeImmutable()
{
$expire = new \DateTimeImmutable();
$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', 1);
}
public function testGetDomain()
{
$cookie = new Cookie('foo', 'bar', 0, '/', '.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', 0, '/', '.myfoodomain.com', true);
$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
}
public function testIsHttpOnly()
{
$cookie = new Cookie('foo', 'bar', 0, '/', '.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');
$cookie = new Cookie('foo', 'bar');
$this->assertFalse($cookie->isCleared());
$cookie = new Cookie('foo', 'bar', 0);
$this->assertFalse($cookie->isCleared());
$cookie = new Cookie('foo', 'bar', -1);
$this->assertFalse($cookie->isCleared());
}
public function testToString()
{
$cookie = new Cookie('foo', 'bar', $expire = 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; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
$cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
$this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
$cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com');
$this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
$cookie = new Cookie('foo', 'bar', 0, '/', '');
$this->assertEquals('foo=bar; path=/; httponly', (string) $cookie);
}
public function testRawCookie()
{
$cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false);
$this->assertFalse($cookie->isRaw());
$this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie);
$cookie = new Cookie('foo', 'b+a+r', 0, '/', null, false, false, true);
$this->assertTrue($cookie->isRaw());
$this->assertEquals('foo=b+a+r; path=/', (string) $cookie);
}
public function testGetMaxAge()
{
$cookie = new Cookie('foo', 'bar');
$this->assertEquals(0, $cookie->getMaxAge());
$cookie = new Cookie('foo', 'bar', $expire = time() + 100);
$this->assertEquals($expire - time(), $cookie->getMaxAge());
$cookie = new Cookie('foo', 'bar', $expire = time() - 100);
$this->assertEquals(0, $cookie->getMaxAge());
}
public function testFromString()
{
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
$this->assertEquals(new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true), $cookie);
$cookie = Cookie::fromString('foo=bar', true);
$this->assertEquals(new Cookie('foo', 'bar', 0, '/', null, false, false), $cookie);
}
public function testFromStringWithHttpOnly()
{
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
$this->assertTrue($cookie->isHttpOnly());
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure');
$this->assertFalse($cookie->isHttpOnly());
}
public function testSameSiteAttributeIsCaseInsensitive()
{
$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, 'Lax');
$this->assertEquals('lax', $cookie->getSameSite());
}
}

View file

@ -1,69 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\HttpFoundation\ExpressionRequestMatcher;
use Symfony\Component\HttpFoundation\Request;
class ExpressionRequestMatcherTest extends TestCase
{
/**
* @expectedException \LogicException
*/
public function testWhenNoExpressionIsSet()
{
$expressionRequestMatcher = new ExpressionRequestMatcher();
$expressionRequestMatcher->matches(new Request());
}
/**
* @dataProvider provideExpressions
*/
public function testMatchesWhenParentMatchesIsTrue($expression, $expected)
{
$request = Request::create('/foo');
$expressionRequestMatcher = new ExpressionRequestMatcher();
$expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
$this->assertSame($expected, $expressionRequestMatcher->matches($request));
}
/**
* @dataProvider provideExpressions
*/
public function testMatchesWhenParentMatchesIsFalse($expression)
{
$request = Request::create('/foo');
$request->attributes->set('foo', 'foo');
$expressionRequestMatcher = new ExpressionRequestMatcher();
$expressionRequestMatcher->matchAttribute('foo', 'bar');
$expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
$this->assertFalse($expressionRequestMatcher->matches($request));
}
public function provideExpressions()
{
return array(
array('request.getMethod() == method', true),
array('request.getPathInfo() == path', true),
array('request.getHost() == host', true),
array('request.getClientIp() == ip', true),
array('request.attributes.all() == attributes', true),
array('request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', true),
array('request.getMethod() != method', false),
array('request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', false),
);
}
}

View file

@ -1,45 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 as OrigFile;
class FakeFile extends OrigFile
{
private $realpath;
public function __construct($realpath, $path)
{
$this->realpath = $realpath;
parent::__construct($path, false);
}
public function isReadable()
{
return true;
}
public function getRealpath()
{
return $this->realpath;
}
public function getSize()
{
return 42;
}
public function getMTime()
{
return time();
}
}

View file

@ -1,180 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
class FileTest extends 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());
}
/**
* @requires extension fileinfo
*/
public function testGuessExtensionWithReset()
{
$file = new File(__DIR__.'/Fixtures/other-file.example');
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
MimeTypeGuesser::getInstance()->register($guesser);
$this->assertEquals('gif', $file->guessExtension());
MimeTypeGuesser::reset();
$this->assertNull($file->guessExtension());
}
public function testConstructWhenFileNotExists()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : '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->assertFileExists($targetPath);
$this->assertFileNotExists($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->assertFileExists($targetPath);
$this->assertFileNotExists($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->assertFileExists($targetPath);
$this->assertFileNotExists($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);
}
protected function createMockGuesser($path, $mimeType)
{
$guesser = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface')->getMock();
$guesser
->expects($this->once())
->method('guess')
->with($this->equalTo($path))
->will($this->returnValue($mimeType))
;
return $guesser;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 B

View file

@ -1,90 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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\MimeType;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
/**
* @requires extension fileinfo
*/
class MimeTypeTest extends TestCase
{
protected $path;
public function testGuessImageWithoutExtension()
{
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
}
public function testGuessImageWithDirectory()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory');
}
public function testGuessImageWithFileBinaryMimeTypeGuesser()
{
$guesser = MimeTypeGuesser::getInstance();
$guesser->register(new FileBinaryMimeTypeGuesser());
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
}
public function testGuessImageWithKnownExtension()
{
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif'));
}
public function testGuessFileWithUnknownExtension()
{
$this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
}
public function testGuessWithIncorrectPath()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here');
}
public function testGuessWithNonReadablePath()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Can not verify chmod operations on Windows');
}
if (!getenv('USER') || 'root' === getenv('USER')) {
$this->markTestSkipped('This test will fail if run under superuser');
}
$path = __DIR__.'/../Fixtures/to_delete';
touch($path);
@chmod($path, 0333);
if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) {
$this->{method_exists($this, $_ = 'expectException') ? $_ : '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

@ -1,273 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class UploadedFileTest extends TestCase
{
protected function setUp()
{
if (!ini_get('file_uploads')) {
$this->markTestSkipped('file_uploads is disabled in php.ini');
}
}
public function testConstructWhenFileNotExists()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : '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->assertFileExists($targetPath);
$this->assertFileNotExists($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

@ -1,175 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
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 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 testShouldRemoveEmptyUploadedFilesForMultiUpload()
{
$bag = new FileBag(array('files' => array(
'name' => array(''),
'type' => array(''),
'tmp_name' => array(''),
'error' => array(UPLOAD_ERR_NO_FILE),
'size' => array(0),
)));
$this->assertSame(array(), $bag->get('files'));
}
public function testShouldNotRemoveEmptyUploadedFilesForAssociativeArray()
{
$bag = new FileBag(array('files' => array(
'name' => array('file1' => ''),
'type' => array('file1' => ''),
'tmp_name' => array('file1' => ''),
'error' => array('file1' => UPLOAD_ERR_NO_FILE),
'size' => array('file1' => 0),
)));
$this->assertSame(array('file1' => null), $bag->get('files'));
}
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

@ -1,205 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\HeaderBag;
class HeaderBagTest extends TestCase
{
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'));
}
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');
}
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');
}
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');
}
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'));
}
public function testCacheControlClone()
{
$headers = array('foo' => 'bar');
$bag1 = new HeaderBag($headers);
$bag2 = new HeaderBag($bag1->all());
$this->assertEquals($bag1->all(), $bag2->all());
}
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);
}
public function testCount()
{
$headers = array('foo' => 'bar', 'HELLO' => 'WORLD');
$headerBag = new HeaderBag($headers);
$this->assertCount(\count($headers), $headerBag);
}
}

View file

@ -1,104 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\IpUtils;
class IpUtilsTest extends TestCase
{
/**
* @dataProvider getIpv4Data
*/
public function testIpv4($matches, $remoteAddr, $cidr)
{
$this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
}
public function getIpv4Data()
{
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.1/33'), // invalid subnet
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')),
array(true, '1.2.3.4', '0.0.0.0/0'),
array(true, '1.2.3.4', '192.168.1.0/0'),
array(false, '1.2.3.4', '256.256.256/0'), // invalid CIDR notation
array(false, 'an_invalid_ip', '192.168.1.0/24'),
);
}
/**
* @dataProvider getIpv6Data
*/
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 getIpv6Data()
{
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, '0:0:603:0:396e:4789:8e99:0001', '::/0'),
array(true, '0:0:603:0:396e:4789:8e99:0001', '2a01:198:603:0::/0'),
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')),
array(false, '}__test|O:21:&quot;JDatabaseDriverMysqli&quot;:3:{s:2', '::1'),
array(false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'),
);
}
/**
* @expectedException \RuntimeException
* @requires extension sockets
*/
public function testAnIpv6WithOptionDisabledIpv6()
{
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');
}
/**
* @dataProvider invalidIpAddressData
*/
public function testInvalidIpAddressesDoNotMatch($requestIp, $proxyIp)
{
$this->assertFalse(IpUtils::checkIp4($requestIp, $proxyIp));
}
public function invalidIpAddressData()
{
return array(
'invalid proxy wildcard' => array('192.168.20.13', '*'),
'invalid proxy missing netmask' => array('192.168.20.13', '0.0.0.0'),
'invalid request IP with invalid proxy wildcard' => array('0.0.0.0', '*'),
);
}
}

View file

@ -1,266 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\JsonResponse;
class JsonResponseTest extends TestCase
{
protected function setUp()
{
parent::setUp();
if (!\defined('HHVM_VERSION')) {
$this->iniSet('serialize_precision', 14);
}
}
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 testSetJson()
{
$response = new JsonResponse('1', 200, array(), true);
$this->assertEquals('1', $response->getContent());
$response = new JsonResponse('[1]', 200, array(), true);
$this->assertEquals('[1]', $response->getContent());
$response = new JsonResponse(null, 200, array());
$response->setJson('true');
$this->assertEquals('true', $response->getContent());
}
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 testJsonEncodeFlags()
{
$response = new JsonResponse('<>\'&"');
$this->assertEquals('"\u003C\u003E\u0027\u0026\u0022"', $response->getContent());
}
public function testGetEncodingOptions()
{
$response = new JsonResponse();
$this->assertEquals(JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT, $response->getEncodingOptions());
}
public function testSetEncodingOptions()
{
$response = new JsonResponse();
$response->setData(array(array(1, 2, 3)));
$this->assertEquals('[[1,2,3]]', $response->getContent());
$response->setEncodingOptions(JSON_FORCE_OBJECT);
$this->assertEquals('{"0":{"0":1,"1":2,"2":3}}', $response->getContent());
}
public function testItAcceptsJsonAsString()
{
$response = JsonResponse::fromJsonString('{"foo":"bar"}');
$this->assertSame('{"foo":"bar"}', $response->getContent());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testSetCallbackInvalidIdentifier()
{
$response = new JsonResponse('foo');
$response->setCallback('+invalid');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testSetContent()
{
JsonResponse::create("\xB1\x31");
}
/**
* @expectedException \Exception
* @expectedExceptionMessage This error is expected
*/
public function testSetContentJsonSerializeError()
{
if (!interface_exists('JsonSerializable', false)) {
$this->markTestSkipped('JsonSerializable is required.');
}
$serializable = new JsonSerializableObject();
JsonResponse::create($serializable);
}
public function testSetComplexCallback()
{
$response = JsonResponse::create(array('foo' => 'bar'));
$response->setCallback('ಠ_ಠ["foo"].bar[0]');
$this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent());
}
}
if (interface_exists('JsonSerializable', false)) {
class JsonSerializableObject implements \JsonSerializable
{
public function jsonSerialize()
{
throw new \Exception('This error is expected');
}
}
}

View file

@ -1,194 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\ParameterBag;
class ParameterBagTest extends TestCase
{
public function testConstructor()
{
$this->testAll();
}
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());
}
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');
}
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]'));
}
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');
}
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');
}
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');
}
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');
}
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');
}
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');
}
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', '', FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters');
$this->assertEquals('example@example.com', $bag->filter('email', '', FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email');
$this->assertEquals('http://example.com/foo', $bag->filter('url', '', 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', '', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as URL with a path');
$this->assertFalse($bag->filter('dec', '', 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', '', 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', ''), '->filter() gets a value of parameter as an array');
}
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);
}
public function testCount()
{
$parameters = array('foo' => 'bar', 'hello' => 'world');
$bag = new ParameterBag($parameters);
$this->assertCount(\count($parameters), $bag);
}
public function testGetBoolean()
{
$parameters = array('string_true' => 'true', 'string_false' => 'false');
$bag = new ParameterBag($parameters);
$this->assertTrue($bag->getBoolean('string_true'), '->getBoolean() gets the string true as boolean true');
$this->assertFalse($bag->getBoolean('string_false'), '->getBoolean() gets the string false as boolean false');
$this->assertFalse($bag->getBoolean('unknown'), '->getBoolean() returns false if a parameter is not defined');
}
}

View file

@ -1,97 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\RedirectResponse;
class RedirectResponseTest extends 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());
}
public function testCacheHeaders()
{
$response = new RedirectResponse('foo.bar', 301);
$this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
$response = new RedirectResponse('foo.bar', 301, array('cache-control' => 'max-age=86400'));
$this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
$this->assertTrue($response->headers->hasCacheControlDirective('max-age'));
$response = new RedirectResponse('foo.bar', 302);
$this->assertTrue($response->headers->hasCacheControlDirective('no-cache'));
}
}

View file

@ -1,151 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestMatcher;
class RequestMatcherTest extends TestCase
{
/**
* @dataProvider getMethodData
*/
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 getMethodData()
{
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),
);
}
public function testScheme()
{
$httpRequest = $request = $request = Request::create('');
$httpsRequest = $request = $request = Request::create('', 'get', array(), array(), array(), array('HTTPS' => 'on'));
$matcher = new RequestMatcher();
$matcher->matchScheme('https');
$this->assertFalse($matcher->matches($httpRequest));
$this->assertTrue($matcher->matches($httpsRequest));
$matcher->matchScheme('http');
$this->assertFalse($matcher->matches($httpsRequest));
$this->assertTrue($matcher->matches($httpRequest));
$matcher = new RequestMatcher();
$this->assertTrue($matcher->matches($httpsRequest));
$this->assertTrue($matcher->matches($httpRequest));
}
/**
* @dataProvider getHostData
*/
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 getHostData()
{
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));
}
}

View file

@ -1,70 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
class RequestStackTest extends TestCase
{
public function testGetCurrentRequest()
{
$requestStack = new RequestStack();
$this->assertNull($requestStack->getCurrentRequest());
$request = Request::create('/foo');
$requestStack->push($request);
$this->assertSame($request, $requestStack->getCurrentRequest());
$this->assertSame($request, $requestStack->pop());
$this->assertNull($requestStack->getCurrentRequest());
$this->assertNull($requestStack->pop());
}
public function testGetMasterRequest()
{
$requestStack = new RequestStack();
$this->assertNull($requestStack->getMasterRequest());
$masterRequest = Request::create('/foo');
$subRequest = Request::create('/bar');
$requestStack->push($masterRequest);
$requestStack->push($subRequest);
$this->assertSame($masterRequest, $requestStack->getMasterRequest());
}
public function testGetParentRequest()
{
$requestStack = new RequestStack();
$this->assertNull($requestStack->getParentRequest());
$masterRequest = Request::create('/foo');
$requestStack->push($masterRequest);
$this->assertNull($requestStack->getParentRequest());
$firstSubRequest = Request::create('/bar');
$requestStack->push($firstSubRequest);
$this->assertSame($masterRequest, $requestStack->getParentRequest());
$secondSubRequest = Request::create('/baz');
$requestStack->push($secondSubRequest);
$this->assertSame($firstSubRequest, $requestStack->getParentRequest());
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,363 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
/**
* @group time-sensitive
*/
class ResponseHeaderBagTest extends TestCase
{
public function testAllPreserveCase()
{
$headers = array(
'fOo' => 'BAR',
'ETag' => 'xyzzy',
'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==',
'P3P' => 'CP="CAO PSA OUR"',
'WWW-Authenticate' => 'Basic realm="WallyWorld"',
'X-UA-Compatible' => 'IE=edge,chrome=1',
'X-XSS-Protection' => '1; mode=block',
);
$bag = new ResponseHeaderBag($headers);
$allPreservedCase = $bag->allPreserveCase();
foreach (array_keys($headers) as $headerName) {
$this->assertArrayHasKey($headerName, $allPreservedCase, '->allPreserveCase() gets all input keys in original case');
}
}
public function testCacheControlHeader()
{
$bag = new ResponseHeaderBag(array());
$this->assertEquals('no-cache, private', $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'));
$bag = new ResponseHeaderBag();
$bag->set('Cache-Control', array('public', 'must-revalidate'));
$this->assertCount(1, $bag->get('Cache-Control', null, false));
$this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag();
$bag->set('Cache-Control', 'public');
$bag->set('Cache-Control', 'must-revalidate', false);
$this->assertCount(1, $bag->get('Cache-Control', null, false));
$this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
}
public function testCacheControlClone()
{
$headers = array('foo' => 'bar');
$bag1 = new ResponseHeaderBag($headers);
$bag2 = new ResponseHeaderBag($bag1->allPreserveCase());
$this->assertEquals($bag1->allPreserveCase(), $bag2->allPreserveCase());
}
public function testToStringIncludesCookieHeaders()
{
$bag = new ResponseHeaderBag(array());
$bag->setCookie(new Cookie('foo', 'bar'));
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
$bag->clearCookie('foo');
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; httponly', $bag);
}
public function testClearCookieSecureNotHttpOnly()
{
$bag = new ResponseHeaderBag(array());
$bag->clearCookie('foo', '/', null, true, false);
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag);
}
public function testReplace()
{
$bag = new ResponseHeaderBag(array());
$this->assertEquals('no-cache, private', $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, private', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
$bag->remove('Cache-Control');
$bag->replace(array());
$this->assertEquals('no-cache, private', $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());
$this->assertEquals('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag->get('set-cookie'));
$this->assertEquals(array(
'foo=bar; path=/path/foo; domain=foo.bar; httponly',
'foo=bar; path=/path/bar; domain=foo.bar; httponly',
'foo=bar; path=/path/bar; domain=bar.foo; httponly',
'foo=bar; path=/; httponly',
), $bag->get('set-cookie', null, false));
$this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/foo']);
$this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/bar']);
$this->assertArrayHasKey('foo', $cookies['bar.foo']['/path/bar']);
$this->assertArrayHasKey('foo', $cookies['']['/']);
}
public function testRemoveCookie()
{
$bag = new ResponseHeaderBag();
$this->assertFalse($bag->has('set-cookie'));
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
$bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar'));
$this->assertTrue($bag->has('set-cookie'));
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertArrayHasKey('/path/foo', $cookies['foo.bar']);
$bag->removeCookie('foo', '/path/foo', 'foo.bar');
$this->assertTrue($bag->has('set-cookie'));
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertArrayNotHasKey('/path/foo', $cookies['foo.bar']);
$bag->removeCookie('bar', '/path/bar', 'foo.bar');
$this->assertFalse($bag->has('set-cookie'));
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertArrayNotHasKey('foo.bar', $cookies);
}
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->assertArrayHasKey('/', $cookies['']);
$bag->removeCookie('foo', null);
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertArrayNotHasKey('foo', $cookies['']['/']);
$bag->removeCookie('bar', null);
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertFalse(isset($cookies['']['/']['bar']));
}
public function testSetCookieHeader()
{
$bag = new ResponseHeaderBag();
$bag->set('set-cookie', 'foo=bar');
$this->assertEquals(array(new Cookie('foo', 'bar', 0, '/', null, false, false, true)), $bag->getCookies());
$bag->set('set-cookie', 'foo2=bar2', false);
$this->assertEquals(array(
new Cookie('foo', 'bar', 0, '/', null, false, false, true),
new Cookie('foo2', 'bar2', 0, '/', null, false, false, true),
), $bag->getCookies());
$bag->remove('set-cookie');
$this->assertEquals(array(), $bag->getCookies());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testGetCookiesWithInvalidArgument()
{
$bag = new ResponseHeaderBag();
$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'),
);
}
public function testDateHeaderAddedOnCreation()
{
$now = time();
$bag = new ResponseHeaderBag();
$this->assertTrue($bag->has('Date'));
$this->assertEquals($now, $bag->getDate('Date')->getTimestamp());
}
public function testDateHeaderCanBeSetOnCreation()
{
$someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
$bag = new ResponseHeaderBag(array('Date' => $someDate));
$this->assertEquals($someDate, $bag->get('Date'));
}
public function testDateHeaderWillBeRecreatedWhenRemoved()
{
$someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
$bag = new ResponseHeaderBag(array('Date' => $someDate));
$bag->remove('Date');
// a (new) Date header is still present
$this->assertTrue($bag->has('Date'));
$this->assertNotEquals($someDate, $bag->get('Date'));
}
public function testDateHeaderWillBeRecreatedWhenHeadersAreReplaced()
{
$bag = new ResponseHeaderBag();
$bag->replace(array());
$this->assertTrue($bag->has('Date'));
}
private function assertSetCookieHeader($expected, ResponseHeaderBag $actual)
{
$this->assertRegExp('#^Set-Cookie:\s+'.preg_quote($expected, '#').'$#m', str_replace("\r\n", "\n", (string) $actual));
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,89 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
abstract class ResponseTestCase extends 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

@ -1,170 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\ServerBag;
/**
* ServerBagTest.
*
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
*/
class ServerBagTest extends 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 testHttpBasicAuthWithPhpCgiBogus()
{
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic_'.base64_encode('foo:bar')));
// Username and passwords should not be set as the header is bogus
$headers = $bag->getHeaders();
$this->assertArrayNotHasKey('PHP_AUTH_USER', $headers);
$this->assertArrayNotHasKey('PHP_AUTH_PW', $headers);
}
public function testHttpBasicAuthWithPhpCgiRedirect()
{
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word')));
$this->assertEquals(array(
'AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'),
'PHP_AUTH_USER' => 'username',
'PHP_AUTH_PW' => 'pass:word',
), $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 testHttpDigestAuthWithPhpCgi()
{
$digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest));
$this->assertEquals(array(
'AUTHORIZATION' => $digest,
'PHP_AUTH_DIGEST' => $digest,
), $bag->getHeaders());
}
public function testHttpDigestAuthWithPhpCgiBogus()
{
$digest = 'Digest_username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest));
// Username and passwords should not be set as the header is bogus
$headers = $bag->getHeaders();
$this->assertArrayNotHasKey('PHP_AUTH_USER', $headers);
$this->assertArrayNotHasKey('PHP_AUTH_PW', $headers);
}
public function testHttpDigestAuthWithPhpCgiRedirect()
{
$digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $digest));
$this->assertEquals(array(
'AUTHORIZATION' => $digest,
'PHP_AUTH_DIGEST' => $digest,
), $bag->getHeaders());
}
public function testOAuthBearerAuth()
{
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => $headerContent));
$this->assertEquals(array(
'AUTHORIZATION' => $headerContent,
), $bag->getHeaders());
}
public function testOAuthBearerAuthWithRedirect()
{
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $headerContent));
$this->assertEquals(array(
'AUTHORIZATION' => $headerContent,
), $bag->getHeaders());
}
/**
* @see https://github.com/symfony/symfony/issues/17345
*/
public function testItDoesNotOverwriteTheAuthorizationHeaderIfItIsAlreadySet()
{
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
$bag = new ServerBag(array('PHP_AUTH_USER' => 'foo', 'HTTP_AUTHORIZATION' => $headerContent));
$this->assertEquals(array(
'AUTHORIZATION' => $headerContent,
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => '',
), $bag->getHeaders());
}
}

View file

@ -1,186 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
/**
* Tests AttributeBag.
*
* @author Drak <drak@zikula.org>
*/
class AttributeBagTest extends TestCase
{
private $array = 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),
);
}
public function testGetIterator()
{
$i = 0;
foreach ($this->bag as $key => $val) {
$this->assertEquals($this->array[$key], $val);
++$i;
}
$this->assertEquals(\count($this->array), $i);
}
public function testCount()
{
$this->assertCount(\count($this->array), $this->bag);
}
}

View file

@ -1,204 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
/**
* Tests NamespacedAttributeBag.
*
* @author Drak <drak@zikula.org>
*/
class NamespacedAttributeBagTest extends TestCase
{
private $array = 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 testHasNoSideEffect($key, $value, $expected)
{
$expected = json_encode($this->bag->all());
$this->bag->has($key);
$this->assertEquals($expected, json_encode($this->bag->all()));
}
/**
* @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 testGetNoSideEffect($key, $value, $expected)
{
$expected = json_encode($this->bag->all());
$this->bag->get($key);
$this->assertEquals($expected, json_encode($this->bag->all()));
}
/**
* @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

@ -1,161 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag as FlashBag;
/**
* AutoExpireFlashBagTest.
*
* @author Drak <drak@zikula.org>
*/
class AutoExpireFlashBagTest extends TestCase
{
/**
* @var \Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag
*/
private $bag;
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);
}
protected 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('_symfony_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());
}
public function testDoNotRemoveTheNewFlashesWhenDisplayingTheExistingOnes()
{
$this->bag->add('success', 'Something');
$this->bag->all();
$this->assertEquals(array('new' => array('success' => array('Something')), 'display' => array()), $this->array);
}
}

View file

@ -1,157 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
/**
* FlashBagTest.
*
* @author Drak <drak@zikula.org>
*/
class FlashBagTest extends TestCase
{
/**
* @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface
*/
private $bag;
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);
}
protected 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('_symfony_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 testAdd()
{
$tab = array('bar' => 'baz');
$this->bag->add('string_message', 'lorem');
$this->bag->add('object_message', new \stdClass());
$this->bag->add('array_message', $tab);
$this->assertEquals(array('lorem'), $this->bag->get('string_message'));
$this->assertEquals(array(new \stdClass()), $this->bag->get('object_message'));
$this->assertEquals(array($tab), $this->bag->get('array_message'));
}
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 testSetAll()
{
$this->bag->add('one_flash', 'Foo');
$this->bag->add('another_flash', 'Bar');
$this->assertTrue($this->bag->has('one_flash'));
$this->assertTrue($this->bag->has('another_flash'));
$this->bag->setAll(array('unique_flash' => 'FooBar'));
$this->assertFalse($this->bag->has('one_flash'));
$this->assertFalse($this->bag->has('another_flash'));
$this->assertSame(array('unique_flash' => 'FooBar'), $this->bag->all());
$this->assertSame(array(), $this->bag->all());
}
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()
);
}
}

View file

@ -1,242 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Session;
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 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', 'symfony is great', array('great' => 'symfony 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();
$this->assertFalse($this->session->isStarted());
}
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());
}
public function testGetIterator()
{
$attributes = array('hello' => 'world', 'symfony' => '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);
}
public function testGetCount()
{
$this->session->set('hello', 'world');
$this->session->set('symfony', 'rocks');
$this->assertCount(2, $this->session);
}
public function testGetMeta()
{
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag());
}
public function testIsEmpty()
{
$this->assertTrue($this->session->isEmpty());
$this->session->set('hello', 'world');
$this->assertFalse($this->session->isEmpty());
$this->session->remove('hello');
$this->assertTrue($this->session->isEmpty());
$flash = $this->session->getFlashBag();
$flash->set('hello', 'world');
$this->assertFalse($this->session->isEmpty());
$flash->get('hello');
$this->assertTrue($this->session->isEmpty());
}
}

View file

@ -1,135 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler;
/**
* @requires extension memcache
* @group time-sensitive
* @group legacy
*/
class MemcacheSessionHandlerTest extends TestCase
{
const PREFIX = 'prefix_';
const TTL = 1000;
/**
* @var MemcacheSessionHandler
*/
protected $storage;
protected $memcache;
protected function setUp()
{
if (\defined('HHVM_VERSION')) {
$this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcache class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
}
parent::setUp();
$this->memcache = $this->getMockBuilder('Memcache')->getMock();
$this->storage = new MemcacheSessionHandler(
$this->memcache,
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
);
}
protected function tearDown()
{
$this->memcache = null;
$this->storage = null;
parent::tearDown();
}
public function testOpenSession()
{
$this->assertTrue($this->storage->open('', ''));
}
public function testCloseSession()
{
$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),
);
}
public function testGetConnection()
{
$method = new \ReflectionMethod($this->storage, 'getMemcache');
$method->setAccessible(true);
$this->assertInstanceOf('\Memcache', $method->invoke($this->storage));
}
}

View file

@ -1,139 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler;
/**
* @requires extension memcached
* @group time-sensitive
*/
class MemcachedSessionHandlerTest extends TestCase
{
const PREFIX = 'prefix_';
const TTL = 1000;
/**
* @var MemcachedSessionHandler
*/
protected $storage;
protected $memcached;
protected function setUp()
{
if (\defined('HHVM_VERSION')) {
$this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcached class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
}
parent::setUp();
if (version_compare(phpversion('memcached'), '2.2.0', '>=') && version_compare(phpversion('memcached'), '3.0.0b1', '<')) {
$this->markTestSkipped('Tests can only be run with memcached extension 2.1.0 or lower, or 3.0.0b1 or higher');
}
$this->memcached = $this->getMockBuilder('Memcached')->getMock();
$this->storage = new MemcachedSessionHandler(
$this->memcached,
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
);
}
protected function tearDown()
{
$this->memcached = null;
$this->storage = null;
parent::tearDown();
}
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),
);
}
public function testGetConnection()
{
$method = new \ReflectionMethod($this->storage, 'getMemcached');
$method->setAccessible(true);
$this->assertInstanceOf('\Memcached', $method->invoke($this->storage));
}
}

View file

@ -1,333 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
/**
* @author Markus Bachmann <markus.bachmann@bachi.biz>
* @group time-sensitive
* @group legacy
*/
class MongoDbSessionHandlerTest extends TestCase
{
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $mongo;
private $storage;
public $options;
protected function setUp()
{
parent::setUp();
if (\extension_loaded('mongodb')) {
if (!class_exists('MongoDB\Client')) {
$this->markTestSkipped('The mongodb/mongodb package is required.');
}
} elseif (!\extension_loaded('mongo')) {
$this->markTestSkipped('The Mongo or MongoDB extension is required.');
}
if (phpversion('mongodb')) {
$mongoClass = 'MongoDB\Client';
} else {
$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',
'expiry_field' => 'expires_at',
'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 testRead()
{
$collection = $this->createMongoCollectionMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
// defining the timeout before the actual method call
// allows to test for "greater than" values in the $criteria
$testTimeout = time() + 1;
$collection->expects($this->once())
->method('findOne')
->will($this->returnCallback(function ($criteria) use ($testTimeout) {
$this->assertArrayHasKey($this->options['id_field'], $criteria);
$this->assertEquals($criteria[$this->options['id_field']], 'foo');
$this->assertArrayHasKey($this->options['expiry_field'], $criteria);
$this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
if (phpversion('mongodb')) {
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
$this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout);
} else {
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
$this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
}
$fields = array(
$this->options['id_field'] => 'foo',
);
if (phpversion('mongodb')) {
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
$fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
} else {
$fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
$fields[$this->options['id_field']] = new \MongoDate();
}
return $fields;
}));
$this->assertEquals('bar', $this->storage->read('foo'));
}
public function testWrite()
{
$collection = $this->createMongoCollectionMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$data = array();
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->once())
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
$this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
if (phpversion('mongodb')) {
$this->assertEquals(array('upsert' => true), $options);
} else {
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
}
$data = $updateData['$set'];
}));
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
$this->assertTrue($this->storage->write('foo', 'bar'));
if (phpversion('mongodb')) {
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
$this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000));
} else {
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
$this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
}
}
public function testWriteWhenUsingExpiresField()
{
$this->options = array(
'id_field' => '_id',
'data_field' => 'data',
'time_field' => 'time',
'database' => 'sf2-test',
'collection' => 'session-test',
'expiry_field' => 'expiresAt',
);
$this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
$collection = $this->createMongoCollectionMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$data = array();
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->once())
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
$this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
if (phpversion('mongodb')) {
$this->assertEquals(array('upsert' => true), $options);
} else {
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
}
$data = $updateData['$set'];
}));
$this->assertTrue($this->storage->write('foo', 'bar'));
if (phpversion('mongodb')) {
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
} else {
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
}
}
public function testReplaceSessionData()
{
$collection = $this->createMongoCollectionMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$data = array();
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->exactly(2))
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
$data = $updateData;
}));
$this->storage->write('foo', 'bar');
$this->storage->write('foo', 'foobar');
if (phpversion('mongodb')) {
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
} else {
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
}
}
public function testDestroy()
{
$collection = $this->createMongoCollectionMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
$collection->expects($this->once())
->method($methodName)
->with(array($this->options['id_field'] => 'foo'));
$this->assertTrue($this->storage->destroy('foo'));
}
public function testGc()
{
$collection = $this->createMongoCollectionMock();
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$methodName = phpversion('mongodb') ? 'deleteMany' : 'remove';
$collection->expects($this->once())
->method($methodName)
->will($this->returnCallback(function ($criteria) {
if (phpversion('mongodb')) {
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']);
$this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000));
} else {
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
$this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
}
}));
$this->assertTrue($this->storage->gc(1));
}
public function testGetConnection()
{
$method = new \ReflectionMethod($this->storage, 'getMongo');
$method->setAccessible(true);
if (phpversion('mongodb')) {
$mongoClass = 'MongoDB\Client';
} else {
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
}
$this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
}
private function createMongoCollectionMock()
{
$collectionClass = 'MongoCollection';
if (phpversion('mongodb')) {
$collectionClass = 'MongoDB\Collection';
}
$collection = $this->getMockBuilder($collectionClass)
->disableOriginalConstructor()
->getMock();
return $collection;
}
}

View file

@ -1,77 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
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
* @preserveGlobalState disabled
*/
class NativeFileSessionHandlerTest extends TestCase
{
public function testConstruct()
{
$storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir()));
$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

@ -1,38 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
/**
* Test class for NativeSessionHandler.
*
* @author Drak <drak@zikula.org>
*
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @group legacy
*/
class NativeSessionHandlerTest extends TestCase
{
/**
* @expectedDeprecation The Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.
*/
public function testConstruct()
{
$handler = new NativeSessionHandler();
$this->assertInstanceOf('SessionHandler', $handler);
$this->assertTrue($handler instanceof NativeSessionHandler);
}
}

View file

@ -1,59 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
/**
* Test class for NullSessionHandler.
*
* @author Drak <drak@zikula.org>
*
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
*/
class NullSessionHandlerTest extends 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

@ -1,411 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
/**
* @requires extension pdo_sqlite
* @group time-sensitive
*/
class PdoSessionHandlerTest extends TestCase
{
private $dbFile;
protected function tearDown()
{
// make sure the temporary database file is deleted when it has been created (even when a test fails)
if ($this->dbFile) {
@unlink($this->dbFile);
}
parent::tearDown();
}
protected function getPersistentSqliteDsn()
{
$this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions');
return 'sqlite:'.$this->dbFile;
}
protected function getMemorySqlitePdo()
{
$pdo = new \PDO('sqlite::memory:');
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$storage = new PdoSessionHandler($pdo);
$storage->createTable();
return $pdo;
}
/**
* @expectedException \InvalidArgumentException
*/
public function testWrongPdoErrMode()
{
$pdo = $this->getMemorySqlitePdo();
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
$storage = new PdoSessionHandler($pdo);
}
/**
* @expectedException \RuntimeException
*/
public function testInexistentTable()
{
$storage = new PdoSessionHandler($this->getMemorySqlitePdo(), array('db_table' => 'inexistent_table'));
$storage->open('', 'sid');
$storage->read('id');
$storage->write('id', 'data');
$storage->close();
}
/**
* @expectedException \RuntimeException
*/
public function testCreateTableTwice()
{
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
$storage->createTable();
}
public function testWithLazyDsnConnection()
{
$dsn = $this->getPersistentSqliteDsn();
$storage = new PdoSessionHandler($dsn);
$storage->createTable();
$storage->open('', 'sid');
$data = $storage->read('id');
$storage->write('id', 'data');
$storage->close();
$this->assertSame('', $data, 'New session returns empty string data');
$storage->open('', 'sid');
$data = $storage->read('id');
$storage->close();
$this->assertSame('data', $data, 'Written value can be read back correctly');
}
public function testWithLazySavePathConnection()
{
$dsn = $this->getPersistentSqliteDsn();
// Open is called with what ini_set('session.save_path', $dsn) would mean
$storage = new PdoSessionHandler(null);
$storage->open($dsn, 'sid');
$storage->createTable();
$data = $storage->read('id');
$storage->write('id', 'data');
$storage->close();
$this->assertSame('', $data, 'New session returns empty string data');
$storage->open($dsn, 'sid');
$data = $storage->read('id');
$storage->close();
$this->assertSame('data', $data, 'Written value can be read back correctly');
}
public function testReadWriteReadWithNullByte()
{
$sessionData = 'da'."\0".'ta';
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
$storage->open('', 'sid');
$readData = $storage->read('id');
$storage->write('id', $sessionData);
$storage->close();
$this->assertSame('', $readData, 'New session returns empty string data');
$storage->open('', 'sid');
$readData = $storage->read('id');
$storage->close();
$this->assertSame($sessionData, $readData, 'Written value can be read back correctly');
}
public function testReadConvertsStreamToString()
{
if (\defined('HHVM_VERSION')) {
$this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
}
$pdo = new MockPdo('pgsql');
$pdo->prepareResult = $this->getMockBuilder('PDOStatement')->getMock();
$content = 'foobar';
$stream = $this->createStream($content);
$pdo->prepareResult->expects($this->once())->method('fetchAll')
->will($this->returnValue(array(array($stream, 42, time()))));
$storage = new PdoSessionHandler($pdo);
$result = $storage->read('foo');
$this->assertSame($content, $result);
}
public function testReadLockedConvertsStreamToString()
{
if (\defined('HHVM_VERSION')) {
$this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
}
if (ini_get('session.use_strict_mode')) {
$this->markTestSkipped('Strict mode needs no locking for new sessions.');
}
$pdo = new MockPdo('pgsql');
$selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
$insertStmt = $this->getMockBuilder('PDOStatement')->getMock();
$pdo->prepareResult = function ($statement) use ($selectStmt, $insertStmt) {
return 0 === strpos($statement, 'INSERT') ? $insertStmt : $selectStmt;
};
$content = 'foobar';
$stream = $this->createStream($content);
$exception = null;
$selectStmt->expects($this->atLeast(2))->method('fetchAll')
->will($this->returnCallback(function () use (&$exception, $stream) {
return $exception ? array(array($stream, 42, time())) : array();
}));
$insertStmt->expects($this->once())->method('execute')
->will($this->returnCallback(function () use (&$exception) {
throw $exception = new \PDOException('', '23');
}));
$storage = new PdoSessionHandler($pdo);
$result = $storage->read('foo');
$this->assertSame($content, $result);
}
public function testReadingRequiresExactlySameId()
{
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
$storage->open('', 'sid');
$storage->write('id', 'data');
$storage->write('test', 'data');
$storage->write('space ', 'data');
$storage->close();
$storage->open('', 'sid');
$readDataCaseSensitive = $storage->read('ID');
$readDataNoCharFolding = $storage->read('tést');
$readDataKeepSpace = $storage->read('space ');
$readDataExtraSpace = $storage->read('space ');
$storage->close();
$this->assertSame('', $readDataCaseSensitive, 'Retrieval by ID should be case-sensitive (collation setting)');
$this->assertSame('', $readDataNoCharFolding, 'Retrieval by ID should not do character folding (collation setting)');
$this->assertSame('data', $readDataKeepSpace, 'Retrieval by ID requires spaces as-is');
$this->assertSame('', $readDataExtraSpace, 'Retrieval by ID requires spaces as-is');
}
/**
* Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace).
*/
public function testWriteDifferentSessionIdThanRead()
{
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
$storage->open('', 'sid');
$storage->read('id');
$storage->destroy('id');
$storage->write('new_id', 'data_of_new_session_id');
$storage->close();
$storage->open('', 'sid');
$data = $storage->read('new_id');
$storage->close();
$this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available');
}
public function testWrongUsageStillWorks()
{
// wrong method sequence that should no happen, but still works
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
$storage->write('id', 'data');
$storage->write('other_id', 'other_data');
$storage->destroy('inexistent');
$storage->open('', 'sid');
$data = $storage->read('id');
$otherData = $storage->read('other_id');
$storage->close();
$this->assertSame('data', $data);
$this->assertSame('other_data', $otherData);
}
public function testSessionDestroy()
{
$pdo = $this->getMemorySqlitePdo();
$storage = new PdoSessionHandler($pdo);
$storage->open('', 'sid');
$storage->read('id');
$storage->write('id', 'data');
$storage->close();
$this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
$storage->open('', 'sid');
$storage->read('id');
$storage->destroy('id');
$storage->close();
$this->assertEquals(0, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
$storage->open('', 'sid');
$data = $storage->read('id');
$storage->close();
$this->assertSame('', $data, 'Destroyed session returns empty string');
}
/**
* @runInSeparateProcess
*/
public function testSessionGC()
{
$previousLifeTime = ini_set('session.gc_maxlifetime', 1000);
$pdo = $this->getMemorySqlitePdo();
$storage = new PdoSessionHandler($pdo);
$storage->open('', 'sid');
$storage->read('id');
$storage->write('id', 'data');
$storage->close();
$storage->open('', 'sid');
$storage->read('gc_id');
ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read
$storage->write('gc_id', 'data');
$storage->close();
$this->assertEquals(2, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called');
$storage->open('', 'sid');
$data = $storage->read('gc_id');
$storage->gc(-1);
$storage->close();
ini_set('session.gc_maxlifetime', $previousLifeTime);
$this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet');
$this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned');
}
public function testGetConnection()
{
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
$method = new \ReflectionMethod($storage, 'getConnection');
$method->setAccessible(true);
$this->assertInstanceOf('\PDO', $method->invoke($storage));
}
public function testGetConnectionConnectsIfNeeded()
{
$storage = new PdoSessionHandler('sqlite::memory:');
$method = new \ReflectionMethod($storage, 'getConnection');
$method->setAccessible(true);
$this->assertInstanceOf('\PDO', $method->invoke($storage));
}
/**
* @dataProvider provideUrlDsnPairs
*/
public function testUrlDsn($url, $expectedDsn, $expectedUser = null, $expectedPassword = null)
{
$storage = new PdoSessionHandler($url);
$this->assertAttributeEquals($expectedDsn, 'dsn', $storage);
if (null !== $expectedUser) {
$this->assertAttributeEquals($expectedUser, 'username', $storage);
}
if (null !== $expectedPassword) {
$this->assertAttributeEquals($expectedPassword, 'password', $storage);
}
}
public function provideUrlDsnPairs()
{
yield array('mysql://localhost/test', 'mysql:host=localhost;dbname=test;');
yield array('mysql://localhost:56/test', 'mysql:host=localhost;port=56;dbname=test;');
yield array('mysql2://root:pwd@localhost/test', 'mysql:host=localhost;dbname=test;', 'root', 'pwd');
yield array('postgres://localhost/test', 'pgsql:host=localhost;dbname=test;');
yield array('postgresql://localhost:5634/test', 'pgsql:host=localhost;port=5634;dbname=test;');
yield array('postgres://root:pwd@localhost/test', 'pgsql:host=localhost;dbname=test;', 'root', 'pwd');
yield 'sqlite relative path' => array('sqlite://localhost/tmp/test', 'sqlite:tmp/test');
yield 'sqlite absolute path' => array('sqlite://localhost//tmp/test', 'sqlite:/tmp/test');
yield 'sqlite relative path without host' => array('sqlite:///tmp/test', 'sqlite:tmp/test');
yield 'sqlite absolute path without host' => array('sqlite3:////tmp/test', 'sqlite:/tmp/test');
yield array('sqlite://localhost/:memory:', 'sqlite::memory:');
yield array('mssql://localhost/test', 'sqlsrv:server=localhost;Database=test');
yield array('mssql://localhost:56/test', 'sqlsrv:server=localhost,56;Database=test');
}
private function createStream($content)
{
$stream = tmpfile();
fwrite($stream, $content);
fseek($stream, 0);
return $stream;
}
}
class MockPdo extends \PDO
{
public $prepareResult;
private $driverName;
private $errorMode;
public function __construct($driverName = null, $errorMode = null)
{
$this->driverName = $driverName;
$this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION;
}
public function getAttribute($attribute)
{
if (\PDO::ATTR_ERRMODE === $attribute) {
return $this->errorMode;
}
if (\PDO::ATTR_DRIVER_NAME === $attribute) {
return $this->driverName;
}
return parent::getAttribute($attribute);
}
public function prepare($statement, $driverOptions = array())
{
return \is_callable($this->prepareResult)
? \call_user_func($this->prepareResult, $statement, $driverOptions)
: $this->prepareResult;
}
public function beginTransaction()
{
}
public function rollBack()
{
}
}

View file

@ -1,97 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@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 PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler;
/**
* @author Adrien Brault <adrien.brault@gmail.com>
*
* @group legacy
*/
class WriteCheckSessionHandlerTest extends TestCase
{
public function test()
{
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
$wrappedSessionHandlerMock
->expects($this->once())
->method('close')
->with()
->will($this->returnValue(true))
;
$this->assertTrue($writeCheckSessionHandler->close());
}
public function testWrite()
{
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
$wrappedSessionHandlerMock
->expects($this->once())
->method('write')
->with('foo', 'bar')
->will($this->returnValue(true))
;
$this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
}
public function testSkippedWrite()
{
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
$wrappedSessionHandlerMock
->expects($this->once())
->method('read')
->with('foo')
->will($this->returnValue('bar'))
;
$wrappedSessionHandlerMock
->expects($this->never())
->method('write')
;
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
$this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
}
public function testNonSkippedWrite()
{
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
$wrappedSessionHandlerMock
->expects($this->once())
->method('read')
->with('foo')
->will($this->returnValue('bar'))
;
$wrappedSessionHandlerMock
->expects($this->once())
->method('write')
->with('foo', 'baZZZ')
->will($this->returnValue(true))
;
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
$this->assertTrue($writeCheckSessionHandler->write('foo', 'baZZZ'));
}
}

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