Upgrade 1-11.38

This commit is contained in:
xesmyd
2026-03-30 14:10:30 +02:00
parent f2a7e6d1fc
commit ac648ef29d
24665 changed files with 69682 additions and 2205004 deletions
+2 -2
View File
@@ -24,7 +24,7 @@ class AcceptHeader
/**
* @var AcceptHeaderItem[]
*/
private $items = array();
private $items = [];
/**
* @var bool
@@ -57,7 +57,7 @@ class AcceptHeader
$item->setIndex($index++);
return $item;
}, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
}, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE)));
}
/**
+4 -5
View File
@@ -21,13 +21,12 @@ 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($value, array $attributes = [])
{
$this->value = $value;
foreach ($attributes as $name => $value) {
@@ -44,9 +43,9 @@ class AcceptHeaderItem
*/
public static function fromString($itemValue)
{
$bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE);
$value = array_shift($bits);
$attributes = array();
$attributes = [];
$lastNullAttribute = null;
foreach ($bits as $bit) {
+47 -37
View File
@@ -44,7 +44,7 @@ class BinaryFileResponse extends Response
* @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, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
{
parent::__construct(null, $status, $headers);
@@ -66,7 +66,7 @@ class BinaryFileResponse extends Response
*
* @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);
}
@@ -139,7 +139,7 @@ class BinaryFileResponse extends Response
*/
public function setAutoEtag()
{
$this->setEtag(sha1_file($this->file->getPathname()));
$this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
return $this;
}
@@ -184,13 +184,6 @@ class BinaryFileResponse extends Response
*/
public function prepare(Request $request)
{
$this->headers->set('Content-Length', $this->file->getSize());
if (!$this->headers->has('Accept-Ranges')) {
// Only accept ranges on safe HTTP methods
$this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
}
if (!$this->headers->has('Content-Type')) {
$this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
}
@@ -204,6 +197,16 @@ class BinaryFileResponse extends Response
$this->offset = 0;
$this->maxlen = -1;
if (false === $fileSize = $this->file->getSize()) {
return $this;
}
$this->headers->set('Content-Length', $fileSize);
if (!$this->headers->has('Accept-Ranges')) {
// Only accept ranges on safe HTTP methods
$this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
}
if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
// Use X-Sendfile, do not send any content.
$type = $request->headers->get('X-Sendfile-Type');
@@ -214,7 +217,7 @@ 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
// @link https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect
foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
$mapping = explode('=', $mapping, 2);
@@ -224,41 +227,48 @@ class BinaryFileResponse extends Response
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')) {
} elseif ($request->headers->has('Range') && $request->isMethod('GET')) {
// Process the range headers.
if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
$range = $request->headers->get('Range');
$fileSize = $this->file->getSize();
list($start, $end) = explode('-', substr($range, 6), 2) + array(0);
if (0 === strpos($range, 'bytes=')) {
list($start, $end) = explode('-', substr($range, 6), 2) + [0];
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
if ('' === $start) {
$start = $fileSize - $end;
$end = $fileSize - 1;
} else {
$start = (int) $start;
}
if ('' === $start) {
$start = $fileSize - $end;
$end = $fileSize - 1;
} else {
$start = (int) $start;
}
if ($start <= $end) {
if ($start < 0 || $end > $fileSize - 1) {
$this->setStatusCode(416);
$this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
} elseif (0 !== $start || $end !== $fileSize - 1) {
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
$this->offset = $start;
if ($start <= $end) {
$end = min($end, $fileSize - 1);
if ($start < 0 || $start > $end) {
$this->setStatusCode(416);
$this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
} elseif ($end - $start < $fileSize - 1) {
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
$this->offset = $start;
$this->setStatusCode(206);
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
$this->headers->set('Content-Length', $end - $start + 1);
$this->setStatusCode(206);
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
$this->headers->set('Content-Length', $end - $start + 1);
}
}
}
}
@@ -303,7 +313,7 @@ class BinaryFileResponse extends Response
fclose($out);
fclose($file);
if ($this->deleteFileAfterSend) {
if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) {
unlink($this->file->getPathname());
}
@@ -320,12 +330,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()
{
@@ -341,7 +351,7 @@ 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
+34 -3
View File
@@ -1,12 +1,43 @@
CHANGELOG
=========
2.8.44
3.4.14
------
* [BC BREAK] Support for the IIS-only `X_ORIGINAL_URL` and `X_REWRITE_URL`
HTTP headers has been dropped for security reasons.
3.4.0
-----
* implemented PHP 7.0's `SessionUpdateTimestampHandlerInterface` with a new
`AbstractSessionHandler` base class and a new `StrictSessionHandler` wrapper
* deprecated the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes
* deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()`
* deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead
* deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead
3.3.0
-----
* the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument,
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
* added the `Cookie::fromString()` method that allows to create a cookie from a
raw header string
3.1.0
-----
* Added support for creating `JsonResponse` with a string of JSON data
3.0.0
-----
* The precedence of parameters returned from `Request::get()` changed from "GET, PATH, BODY" to "PATH, GET, BODY"
2.8.0
-----
@@ -113,10 +144,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
+133 -18
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;
@@ -26,21 +30,79 @@ class Cookie
protected $secure;
protected $httpOnly;
private $raw;
private $sameSite;
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'];
/**
* @param string $name The name of the cookie
* @param string $value The value of the cookie
* @param int|string|\DateTime|\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 $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 $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
* Creates cookie from raw header string.
*
* @param string $cookie
* @param bool $decode
*
* @return static
*/
public static function fromString($cookie, $decode = false)
{
$data = [
'expires' => 0,
'path' => '/',
'domain' => null,
'secure' => false,
'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;
}
}
return new static($data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
}
/**
* @param string $name The name of the cookie
* @param string|null $value The value of the 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 $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)
public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null)
{
// 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));
}
@@ -49,7 +111,7 @@ class Cookie
}
// convert expiration time to a Unix timestamp
if ($expire instanceof \DateTime || $expire instanceof \DateTimeInterface) {
if ($expire instanceof \DateTimeInterface) {
$expire = $expire->format('U');
} elseif (!is_numeric($expire)) {
$expire = strtotime($expire);
@@ -66,6 +128,17 @@ class Cookie
$this->path = empty($path) ? '/' : $path;
$this->secure = (bool) $secure;
$this->httpOnly = (bool) $httpOnly;
$this->raw = (bool) $raw;
if (null !== $sameSite) {
$sameSite = strtolower($sameSite);
}
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.');
}
$this->sameSite = $sameSite;
}
/**
@@ -75,20 +148,26 @@ class Cookie
*/
public function __toString()
{
$str = 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);
$str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0';
} else {
$str .= rawurlencode($this->getValue());
$str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue());
if (0 !== $this->getExpiresTime()) {
$str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime());
$str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; Max-Age='.$this->getMaxAge();
}
}
if ($this->path) {
$str .= '; path='.$this->path;
if ($this->getPath()) {
$str .= '; path='.$this->getPath();
}
if ($this->getDomain()) {
@@ -103,6 +182,10 @@ class Cookie
$str .= '; httponly';
}
if (null !== $this->getSameSite()) {
$str .= '; samesite='.$this->getSameSite();
}
return $str;
}
@@ -119,7 +202,7 @@ class Cookie
/**
* Gets the value of the cookie.
*
* @return string
* @return string|null
*/
public function getValue()
{
@@ -129,7 +212,7 @@ class Cookie
/**
* Gets the domain that the cookie is available to.
*
* @return string
* @return string|null
*/
public function getDomain()
{
@@ -146,6 +229,18 @@ class Cookie
return $this->expire;
}
/**
* Gets the max-age attribute.
*
* @return int
*/
public function getMaxAge()
{
$maxAge = $this->expire - time();
return 0 >= $maxAge ? 0 : $maxAge;
}
/**
* Gets the path on the server in which the cookie will be available on.
*
@@ -185,4 +280,24 @@ class Cookie
{
return 0 !== $this->expire && $this->expire < time();
}
/**
* Checks if the cookie value should be sent with no url encoding.
*
* @return bool
*/
public function isRaw()
{
return $this->raw;
}
/**
* Gets the SameSite attribute.
*
* @return string|null
*/
public function getSameSite()
{
return $this->sameSite;
}
}
@@ -14,10 +14,8 @@ namespace Symfony\Component\HttpFoundation\Exception;
/**
* The HTTP request contains headers with conflicting information.
*
* This exception should trigger an HTTP 400 response in your application code.
*
* @author Magnus Nordlander <magnus@fervo.se>
*/
class ConflictingHeadersException extends \RuntimeException
class ConflictingHeadersException extends \UnexpectedValueException implements RequestExceptionInterface
{
}
@@ -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);
}
}
+3 -3
View File
@@ -97,7 +97,7 @@ class File extends \SplFileInfo
$renamed = rename($this->getPathname(), $target);
restore_error_handler();
if (!$renamed) {
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s).', $this->getPathname(), $target, strip_tags($error)));
}
@chmod($target, 0666 & ~umask());
@@ -109,10 +109,10 @@ class File extends \SplFileInfo
{
if (!is_dir($directory)) {
if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {
throw new FileException(sprintf('Unable to create the "%s" directory', $directory));
throw new FileException(sprintf('Unable to create the "%s" directory.', $directory));
}
} elseif (!is_writable($directory)) {
throw new FileException(sprintf('Unable to write in the "%s" directory', $directory));
throw new FileException(sprintf('Unable to write in the "%s" directory.', $directory));
}
$target = rtrim($directory, '/\\').\DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : $this->getName($name));
@@ -37,7 +37,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface
*
* @var array
*/
protected $guessers = array();
protected $guessers = [];
/**
* Returns the singleton instance.
@@ -90,5 +90,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface
return $extension;
}
}
return null;
}
}
@@ -74,7 +74,7 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
}
if (!self::isSupported()) {
return;
return null;
}
ob_start();
@@ -84,14 +84,14 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
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];
@@ -26,7 +26,7 @@ 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)
{
@@ -57,13 +57,19 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
}
if (!self::isSupported()) {
return;
return null;
}
if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
return;
if (!$finfo = new \finfo(\FILEINFO_MIME_TYPE, $this->magicFile)) {
return null;
}
$mimeType = $finfo->file($path);
if ($mimeType && 0 === (\strlen($mimeType) % 2)) {
$mimeStart = substr($mimeType, 0, \strlen($mimeType) >> 1);
$mimeType = $mimeStart.$mimeStart === $mimeType ? $mimeStart : $mimeType;
}
return $finfo->file($path);
return $mimeType;
}
}
@@ -20,11 +20,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,8 +618,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'audio/adpcm' => 'adp',
'audio/basic' => 'au',
'audio/midi' => 'mid',
'audio/mp4' => 'mp4a',
'audio/mpeg' => 'mpga',
'audio/mp4' => 'm4a',
'audio/mpeg' => 'mp3',
'audio/ogg' => 'oga',
'audio/s3m' => 's3m',
'audio/silk' => 'sil',
@@ -653,6 +653,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 +674,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 +737,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',
@@ -743,13 +748,14 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
'text/vnd.sun.j2me.app-descriptor' => 'jad',
'text/vnd.wap.wml' => 'wml',
'text/vnd.wap.wmlscript' => 'wmls',
'text/vtt' => 'vtt',
'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',
@@ -795,13 +801,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;
}
}
@@ -51,7 +51,7 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface
*
* @var array
*/
protected $guessers = array();
protected $guessers = [];
/**
* Returns the singleton instance.
@@ -127,7 +127,9 @@ 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?)');
throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?).');
}
return null;
}
}
@@ -26,7 +26,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
+36 -23
View File
@@ -24,7 +24,7 @@ use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
*/
class UploadedFile extends File
{
private $test = false;
private $test;
private $originalName;
private $mimeType;
private $size;
@@ -60,10 +60,10 @@ class UploadedFile extends File
$this->originalName = $this->getName($originalName);
$this->mimeType = $mimeType ?: 'application/octet-stream';
$this->size = $size;
$this->error = $error ?: UPLOAD_ERR_OK;
$this->error = $error ?: \UPLOAD_ERR_OK;
$this->test = (bool) $test;
parent::__construct($path, UPLOAD_ERR_OK === $this->error);
parent::__construct($path, \UPLOAD_ERR_OK === $this->error);
}
/**
@@ -72,7 +72,7 @@ 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 string|null The original name
* @return string The original name
*/
public function getClientOriginalName()
{
@@ -89,7 +89,7 @@ class UploadedFile extends File
*/
public function getClientOriginalExtension()
{
return pathinfo($this->originalName, PATHINFO_EXTENSION);
return pathinfo($this->originalName, \PATHINFO_EXTENSION);
}
/**
@@ -101,7 +101,7 @@ class UploadedFile extends File
* For a trusted mime type, use getMimeType() instead (which guesses the mime
* type based on the file content).
*
* @return string|null The mime type
* @return string The mime type
*
* @see getMimeType()
*/
@@ -168,7 +168,7 @@ class UploadedFile extends File
*/
public function isValid()
{
$isOk = UPLOAD_ERR_OK === $this->error;
$isOk = \UPLOAD_ERR_OK === $this->error;
return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
}
@@ -196,7 +196,7 @@ class UploadedFile extends File
$moved = move_uploaded_file($this->getPathname(), $target);
restore_error_handler();
if (!$moved) {
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s).', $this->getPathname(), $target, strip_tags($error)));
}
@chmod($target, 0666 & ~umask());
@@ -214,13 +214,26 @@ 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.
*
* @return int The given size in bytes
*/
private static function parseFilesize($size)
{
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 +242,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,18 +262,18 @@ class UploadedFile extends File
*/
public function getErrorMessage()
{
static $errors = array(
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.',
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
);
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.',
\UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
\UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
\UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
\UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
];
$errorCode = $this->error;
$maxFilesize = UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
$maxFilesize = \UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
$message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.';
return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
+12 -14
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
*/
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,19 +75,19 @@ class FileBag extends ParameterBag
return $file;
}
$file = $this->fixPhpFilesArray($file);
if (\is_array($file)) {
$file = $this->fixPhpFilesArray($file);
$keys = array_keys($file);
sort($keys);
if ($keys == self::$fileKeys) {
if (UPLOAD_ERR_NO_FILE == $file['error']) {
if (\UPLOAD_ERR_NO_FILE == $file['error']) {
$file = null;
} else {
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']);
}
} else {
$file = array_map(array($this, 'convertFileInformation'), $file);
$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;
+48 -31
View File
@@ -18,13 +18,13 @@ namespace Symfony\Component\HttpFoundation;
*/
class HeaderBag implements \IteratorAggregate, \Countable
{
protected $headers = array();
protected $cacheControl = array();
protected $headers = [];
protected $cacheControl = [];
/**
* @param array $headers An array of HTTP headers
*/
public function __construct(array $headers = array())
public function __construct(array $headers = [])
{
foreach ($headers as $key => $values) {
$this->set($key, $values);
@@ -38,14 +38,14 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function __toString()
{
if (!$this->headers) {
if (!$headers = $this->all()) {
return '';
}
$max = max(array_map('strlen', array_keys($this->headers))) + 1;
ksort($headers);
$max = max(array_map('strlen', array_keys($headers))) + 1;
$content = '';
ksort($this->headers);
foreach ($this->headers as $name => $values) {
foreach ($headers as $name => $values) {
$name = implode('-', array_map('ucfirst', explode('-', $name)));
foreach ($values as $value) {
$content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
@@ -72,7 +72,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function keys()
{
return array_keys($this->headers);
return array_keys($this->all());
}
/**
@@ -80,9 +80,9 @@ class HeaderBag implements \IteratorAggregate, \Countable
*
* @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);
}
@@ -101,29 +101,38 @@ 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
* @param bool $first Whether to return the first value or all header values
*
* @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise
*/
public function get($key, $default = null, $first = true)
{
$key = str_replace('_', '-', strtolower($key));
$headers = $this->all();
if (!array_key_exists($key, $this->headers)) {
if (!\array_key_exists($key, $headers)) {
if (null === $default) {
return $first ? null : array();
return $first ? null : [];
}
return $first ? $default : array($default);
return $first ? $default : [$default];
}
if ($first) {
return \count($this->headers[$key]) ? $this->headers[$key][0] : $default;
if (!$headers[$key]) {
return $default;
}
if (null === $headers[$key][0]) {
return null;
}
return (string) $headers[$key][0];
}
return $this->headers[$key];
return $headers[$key];
}
/**
@@ -137,12 +146,20 @@ class HeaderBag implements \IteratorAggregate, \Countable
{
$key = str_replace('_', '-', strtolower($key));
$values = array_values((array) $values);
if (\is_array($values)) {
$values = array_values($values);
if (true === $replace || !isset($this->headers[$key])) {
$this->headers[$key] = $values;
if (true === $replace || !isset($this->headers[$key])) {
$this->headers[$key] = $values;
} else {
$this->headers[$key] = array_merge($this->headers[$key], $values);
}
} else {
$this->headers[$key] = array_merge($this->headers[$key], $values);
if (true === $replace || !isset($this->headers[$key])) {
$this->headers[$key] = [$values];
} else {
$this->headers[$key][] = $values;
}
}
if ('cache-control' === $key) {
@@ -159,7 +176,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function has($key)
{
return array_key_exists(str_replace('_', '-', strtolower($key)), $this->headers);
return \array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
}
/**
@@ -187,7 +204,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
unset($this->headers[$key]);
if ('cache-control' === $key) {
$this->cacheControl = array();
$this->cacheControl = [];
}
}
@@ -207,8 +224,8 @@ class HeaderBag implements \IteratorAggregate, \Countable
return $default;
}
if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) {
throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
if (false === $date = \DateTime::createFromFormat(\DATE_RFC2822, $value)) {
throw new \RuntimeException(sprintf('The "%s" HTTP header is not parseable (%s).', $key, $value));
}
return $date;
@@ -236,7 +253,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
*/
public function hasCacheControlDirective($key)
{
return array_key_exists($key, $this->cacheControl);
return \array_key_exists($key, $this->cacheControl);
}
/**
@@ -248,7 +265,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;
}
/**
@@ -285,7 +302,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
protected function getCacheControlHeader()
{
$parts = array();
$parts = [];
ksort($this->cacheControl);
foreach ($this->cacheControl as $key => $value) {
if (true === $value) {
@@ -311,8 +328,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);
$cacheControl = [];
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);
}
+4 -4
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';
@@ -68,7 +68,7 @@ class IpUtils
return self::$checkedIps[$cacheKey];
}
if (!filter_var($requestIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
return self::$checkedIps[$cacheKey] = false;
}
@@ -76,7 +76,7 @@ class IpUtils
list($address, $netmask) = explode('/', $ip, 2);
if ('0' === $netmask) {
return self::$checkedIps[$cacheKey] = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
return self::$checkedIps[$cacheKey] = filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4);
}
if ($netmask < 0 || $netmask > 32) {
+67 -50
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>
*/
@@ -29,14 +29,17 @@ class JsonResponse extends Response
// Encode <, >, ', &, and " characters in the JSON, making it also safe to be embedded into HTML.
// 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
protected $encodingOptions = 15;
const DEFAULT_ENCODING_OPTIONS = 15;
protected $encodingOptions = self::DEFAULT_ENCODING_OPTIONS;
/**
* @param mixed $data The response data
* @param int $status The response status code
* @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())
public function __construct($data = null, $status = 200, $headers = [], $json = false)
{
parent::__construct('', $status, $headers);
@@ -44,7 +47,7 @@ class JsonResponse extends Response
$data = new \ArrayObject();
}
$this->setData($data);
$json ? $this->setJson($data) : $this->setData($data);
}
/**
@@ -52,20 +55,39 @@ 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);
}
/**
* 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 = [])
{
return new static($data, $status, $headers, true);
}
/**
* Sets the JSONP callback.
*
@@ -78,16 +100,16 @@ class JsonResponse extends Response
public function setCallback($callback = null)
{
if (null !== $callback) {
// partially token from http://www.geekality.net/2011/08/03/valid-javascript-identifier/
// partially token from https://github.com/willdurand/JsonpCallbackValidator
// 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)) {
@@ -101,6 +123,22 @@ class JsonResponse extends Response
return $this->update();
}
/**
* Sets a raw string containing a JSON document to be sent.
*
* @param string $json
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setJson($json)
{
$this->data = $json;
return $this->update();
}
/**
* Sets the data to be sent as JSON.
*
@@ -110,7 +148,7 @@ 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
@@ -118,55 +156,34 @@ class JsonResponse extends Response
// If only PHP did the same...
$data = json_encode($data, $this->encodingOptions);
} else {
try {
if (!interface_exists('JsonSerializable', false)) {
// PHP 5.3 triggers annoying warnings for some
// types that can't be serialized as JSON (INF, resources, etc.)
// but doesn't provide the JsonSerializable interface.
set_error_handler(function () { return false; });
if (!interface_exists('JsonSerializable', false)) {
set_error_handler(function () { return false; });
try {
$data = @json_encode($data, $this->encodingOptions);
} finally {
restore_error_handler();
} elseif (\PHP_VERSION_ID < 50500) {
// PHP 5.4 and up wrap exceptions thrown by JsonSerializable
// objects in a new exception that needs to be removed.
// Fortunately, PHP 5.5 and up do not trigger any warning anymore.
// Clear json_last_error()
json_encode(null);
$errorHandler = set_error_handler('var_dump');
restore_error_handler();
set_error_handler(function () use ($errorHandler) {
if (JSON_ERROR_NONE === json_last_error()) {
return $errorHandler && false !== \call_user_func_array($errorHandler, \func_get_args());
}
});
$data = json_encode($data, $this->encodingOptions);
restore_error_handler();
} else {
}
} 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 (\Error $e) {
if (\PHP_VERSION_ID < 50500 || !interface_exists('JsonSerializable', false)) {
restore_error_handler();
if (\PHP_VERSION_ID >= 70300 && (\JSON_THROW_ON_ERROR & $this->encodingOptions)) {
return $this->setJson($data);
}
throw $e;
} catch (\Exception $e) {
if (\PHP_VERSION_ID < 50500 || !interface_exists('JsonSerializable', false)) {
restore_error_handler();
}
if (interface_exists('JsonSerializable', false) && 'Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
throw $e->getPrevious() ?: $e;
}
throw $e;
}
}
if (JSON_ERROR_NONE !== json_last_error()) {
if (\JSON_ERROR_NONE !== json_last_error()) {
throw new \InvalidArgumentException(json_last_error_msg());
}
$this->data = $data;
return $this->update();
return $this->setJson($data);
}
/**
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2004-2018 Fabien Potencier
Copyright (c) 2004-2020 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
+21 -95
View File
@@ -26,7 +26,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
/**
* @param array $parameters An array of parameters
*/
public function __construct(array $parameters = array())
public function __construct(array $parameters = [])
{
$this->parameters = $parameters;
}
@@ -56,7 +56,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
*
* @param array $parameters An array of parameters
*/
public function replace(array $parameters = array())
public function replace(array $parameters = [])
{
$this->parameters = $parameters;
}
@@ -66,7 +66,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
*
* @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);
}
@@ -74,67 +74,14 @@ class ParameterBag implements \IteratorAggregate, \Countable
/**
* Returns a parameter by name.
*
* Note: Finding deep items is deprecated since version 2.8, to be removed in 3.0.
*
* @param string $key The key
* @param mixed $default The default value if the parameter key does not exist
* @param bool $deep If true, a path like foo[bar] will find deeper items
*
* @return mixed
*
* @throws \InvalidArgumentException
*/
public function get($key, $default = null, $deep = false)
public function get($key, $default = null)
{
if ($deep) {
@trigger_error('Using paths to find deeper items in '.__METHOD__.' is deprecated since Symfony 2.8 and will be removed in 3.0. Filter the returned value in your own code instead.', E_USER_DEPRECATED);
}
if (!$deep || false === $pos = strpos($key, '[')) {
return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
}
$root = substr($key, 0, $pos);
if (!array_key_exists($root, $this->parameters)) {
return $default;
}
$value = $this->parameters[$root];
$currentKey = null;
for ($i = $pos, $c = \strlen($key); $i < $c; ++$i) {
$char = $key[$i];
if ('[' === $char) {
if (null !== $currentKey) {
throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "[" at position %d.', $i));
}
$currentKey = '';
} elseif (']' === $char) {
if (null === $currentKey) {
throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "]" at position %d.', $i));
}
if (!\is_array($value) || !array_key_exists($currentKey, $value)) {
return $default;
}
$value = $value[$currentKey];
$currentKey = null;
} else {
if (null === $currentKey) {
throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "%s" at position %d.', $char, $i));
}
$currentKey .= $char;
}
}
if (null !== $currentKey) {
throw new \InvalidArgumentException('Malformed path. Path must end with "]".');
}
return $value;
return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
}
/**
@@ -157,7 +104,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
*/
public function has($key)
{
return array_key_exists($key, $this->parameters);
return \array_key_exists($key, $this->parameters);
}
/**
@@ -175,13 +122,12 @@ class ParameterBag implements \IteratorAggregate, \Countable
*
* @param string $key The parameter key
* @param string $default The default value if the parameter key does not exist
* @param bool $deep If true, a path like foo[bar] will find deeper items
*
* @return string The filtered value
*/
public function getAlpha($key, $default = '', $deep = false)
public function getAlpha($key, $default = '')
{
return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default, $deep));
return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default));
}
/**
@@ -189,13 +135,12 @@ class ParameterBag implements \IteratorAggregate, \Countable
*
* @param string $key The parameter key
* @param string $default The default value if the parameter key does not exist
* @param bool $deep If true, a path like foo[bar] will find deeper items
*
* @return string The filtered value
*/
public function getAlnum($key, $default = '', $deep = false)
public function getAlnum($key, $default = '')
{
return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default, $deep));
return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default));
}
/**
@@ -203,14 +148,13 @@ class ParameterBag implements \IteratorAggregate, \Countable
*
* @param string $key The parameter key
* @param string $default The default value if the parameter key does not exist
* @param bool $deep If true, a path like foo[bar] will find deeper items
*
* @return string The filtered value
*/
public function getDigits($key, $default = '', $deep = false)
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, array(), $deep));
return str_replace(['-', '+'], '', $this->filter($key, $default, \FILTER_SANITIZE_NUMBER_INT));
}
/**
@@ -218,13 +162,12 @@ class ParameterBag implements \IteratorAggregate, \Countable
*
* @param string $key The parameter key
* @param int $default The default value if the parameter key does not exist
* @param bool $deep If true, a path like foo[bar] will find deeper items
*
* @return int The filtered value
*/
public function getInt($key, $default = 0, $deep = false)
public function getInt($key, $default = 0)
{
return (int) $this->get($key, $default, $deep);
return (int) $this->get($key, $default);
}
/**
@@ -232,13 +175,12 @@ class ParameterBag implements \IteratorAggregate, \Countable
*
* @param string $key The parameter key
* @param bool $default The default value if the parameter key does not exist
* @param bool $deep If true, a path like foo[bar] will find deeper items
*
* @return bool The filtered value
*/
public function getBoolean($key, $default = false, $deep = false)
public function getBoolean($key, $default = false)
{
return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN, array(), $deep);
return $this->filter($key, $default, \FILTER_VALIDATE_BOOLEAN);
}
/**
@@ -248,39 +190,23 @@ class ParameterBag implements \IteratorAggregate, \Countable
* @param mixed $default Default = null
* @param int $filter FILTER_* constant
* @param mixed $options Filter options
* @param bool $deep Default = false
*
* @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(), $deep = false)
public function filter($key, $default = null, $filter = \FILTER_DEFAULT, $options = [])
{
static $filters = null;
if (null === $filters) {
foreach (filter_list() as $tmp) {
$filters[filter_id($tmp)] = 1;
}
}
if (\is_bool($filter) || !isset($filters[$filter]) || \is_array($deep)) {
@trigger_error('Passing the $deep boolean as 3rd argument to the '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0. Remove it altogether as the $deep argument will be removed in 3.0.', E_USER_DEPRECATED);
$tmp = $deep;
$deep = $filter;
$filter = $options;
$options = $tmp;
}
$value = $this->get($key, $default, $deep);
$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.
if (\is_array($value) && !isset($options['flags'])) {
$options['flags'] = FILTER_REQUIRE_ARRAY;
$options['flags'] = \FILTER_REQUIRE_ARRAY;
}
return filter_var($value, $filter, $options);
+1 -1
View File
@@ -7,7 +7,7 @@ specification.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/http_foundation/index.html)
* [Documentation](https://symfony.com/doc/current/components/http_foundation.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
+9 -5
View File
@@ -30,9 +30,9 @@ 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($url, $status = 302, $headers = [])
{
parent::__construct('', $status, $headers);
@@ -41,6 +41,10 @@ class RedirectResponse extends Response
if (!$this->isRedirect()) {
throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
}
if (301 == $status && !\array_key_exists('cache-control', array_change_key_case($headers, \CASE_LOWER))) {
$this->headers->remove('cache-control');
}
}
/**
@@ -52,7 +56,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);
}
@@ -89,14 +93,14 @@ 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>
<body>
Redirecting to <a href="%1$s">%1$s</a>.
</body>
</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
</html>', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8')));
$this->headers->set('Location', $url);
+299 -136
View File
@@ -12,6 +12,7 @@
namespace Symfony\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
/**
@@ -29,11 +30,22 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface;
*/
class Request
{
const HEADER_FORWARDED = 'forwarded';
const HEADER_CLIENT_IP = 'client_ip';
const HEADER_CLIENT_HOST = 'client_host';
const HEADER_CLIENT_PROTO = 'client_proto';
const HEADER_CLIENT_PORT = 'client_port';
const HEADER_FORWARDED = 0b00001; // When using RFC 7239
const HEADER_X_FORWARDED_FOR = 0b00010;
const HEADER_X_FORWARDED_HOST = 0b00100;
const HEADER_X_FORWARDED_PROTO = 0b01000;
const HEADER_X_FORWARDED_PORT = 0b10000;
const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers
const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host
/** @deprecated since version 3.3, to be removed in 4.0 */
const HEADER_CLIENT_IP = self::HEADER_X_FORWARDED_FOR;
/** @deprecated since version 3.3, to be removed in 4.0 */
const HEADER_CLIENT_HOST = self::HEADER_X_FORWARDED_HOST;
/** @deprecated since version 3.3, to be removed in 4.0 */
const HEADER_CLIENT_PROTO = self::HEADER_X_FORWARDED_PROTO;
/** @deprecated since version 3.3, to be removed in 4.0 */
const HEADER_CLIENT_PORT = self::HEADER_X_FORWARDED_PORT;
const METHOD_HEAD = 'HEAD';
const METHOD_GET = 'GET';
@@ -49,17 +61,17 @@ class Request
/**
* @var string[]
*/
protected static $trustedProxies = array();
protected static $trustedProxies = [];
/**
* @var string[]
*/
protected static $trustedHostPatterns = array();
protected static $trustedHostPatterns = [];
/**
* @var string[]
*/
protected static $trustedHosts = array();
protected static $trustedHosts = [];
/**
* Names for headers that can be trusted when
@@ -69,63 +81,65 @@ class Request
*
* The other headers are non-standard, but widely used
* by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
*
* @deprecated since version 3.3, to be removed in 4.0
*/
protected static $trustedHeaders = array(
protected static $trustedHeaders = [
self::HEADER_FORWARDED => 'FORWARDED',
self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
);
];
protected static $httpMethodParameterOverride = false;
/**
* Custom parameters.
*
* @var \Symfony\Component\HttpFoundation\ParameterBag
* @var ParameterBag
*/
public $attributes;
/**
* Request body parameters ($_POST).
*
* @var \Symfony\Component\HttpFoundation\ParameterBag
* @var ParameterBag
*/
public $request;
/**
* Query string parameters ($_GET).
*
* @var \Symfony\Component\HttpFoundation\ParameterBag
* @var ParameterBag
*/
public $query;
/**
* Server and execution environment parameters ($_SERVER).
*
* @var \Symfony\Component\HttpFoundation\ServerBag
* @var ServerBag
*/
public $server;
/**
* Uploaded files ($_FILES).
*
* @var \Symfony\Component\HttpFoundation\FileBag
* @var FileBag
*/
public $files;
/**
* Cookies ($_COOKIE).
*
* @var \Symfony\Component\HttpFoundation\ParameterBag
* @var ParameterBag
*/
public $cookies;
/**
* Headers (taken from the $_SERVER).
*
* @var \Symfony\Component\HttpFoundation\HeaderBag
* @var HeaderBag
*/
public $headers;
@@ -185,7 +199,7 @@ class Request
protected $format;
/**
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
* @var SessionInterface
*/
protected $session;
@@ -206,14 +220,26 @@ class Request
protected static $requestFactory;
private $isHostValid = true;
private $isForwardedValid = true;
private static $forwardedParams = array(
self::HEADER_CLIENT_IP => 'for',
self::HEADER_CLIENT_HOST => 'host',
self::HEADER_CLIENT_PROTO => 'proto',
self::HEADER_CLIENT_PORT => 'host',
);
private static $trustedHeaderSet = -1;
/** @deprecated since version 3.3, to be removed in 4.0 */
private static $trustedHeaderNames = [
self::HEADER_FORWARDED => 'FORWARDED',
self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
];
private static $forwardedParams = [
self::HEADER_X_FORWARDED_FOR => 'for',
self::HEADER_X_FORWARDED_HOST => 'host',
self::HEADER_X_FORWARDED_PROTO => 'proto',
self::HEADER_X_FORWARDED_PORT => 'host',
];
/**
* @param array $query The GET parameters
@@ -224,7 +250,7 @@ class Request
* @param array $server The SERVER parameters
* @param string|resource|null $content The raw body data
*/
public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
{
$this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
}
@@ -242,7 +268,7 @@ class Request
* @param array $server The SERVER parameters
* @param string|resource|null $content The raw body data
*/
public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
{
$this->request = new ParameterBag($request);
$this->query = new ParameterBag($query);
@@ -277,18 +303,18 @@ class Request
// HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields.
$server = $_SERVER;
if ('cli-server' === \PHP_SAPI) {
if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
if (\array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
$server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH'];
}
if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
if (\array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
$server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE'];
}
}
$request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server);
$request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $server);
if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
) {
parse_str($request->getContent(), $data);
$request->request = new ParameterBag($data);
@@ -313,13 +339,13 @@ class Request
*
* @return static
*/
public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
public static function create($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
{
$server = array_replace(array(
$server = array_replace([
'SERVER_NAME' => 'localhost',
'SERVER_PORT' => 80,
'HTTP_HOST' => 'localhost',
'HTTP_USER_AGENT' => 'Symfony/2.X',
'HTTP_USER_AGENT' => 'Symfony/3.X',
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
@@ -328,7 +354,7 @@ class Request
'SCRIPT_FILENAME' => '',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'REQUEST_TIME' => time(),
), $server);
], $server);
$server['PATH_INFO'] = '';
$server['REQUEST_METHOD'] = strtoupper($method);
@@ -376,10 +402,10 @@ class Request
// no break
case 'PATCH':
$request = $parameters;
$query = array();
$query = [];
break;
default:
$request = array();
$request = [];
$query = $parameters;
break;
}
@@ -402,7 +428,7 @@ class Request
$server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
$server['QUERY_STRING'] = $queryString;
return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content);
return self::createRequestFromFactory($query, $request, [], $cookies, $files, $server, $content);
}
/**
@@ -502,11 +528,15 @@ class Request
try {
$content = $this->getContent();
} catch (\LogicException $e) {
return trigger_error($e, E_USER_ERROR);
if (\PHP_VERSION_ID >= 70400) {
throw $e;
}
return trigger_error($e, \E_USER_ERROR);
}
$cookieHeader = '';
$cookies = array();
$cookies = [];
foreach ($this->cookies as $k => $v) {
$cookies[] = $k.'='.$v;
@@ -540,19 +570,19 @@ class Request
foreach ($this->headers->all() as $key => $value) {
$key = strtoupper(str_replace('-', '_', $key));
if (\in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
if (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH'])) {
$_SERVER[$key] = implode(', ', $value);
} else {
$_SERVER['HTTP_'.$key] = implode(', ', $value);
}
}
$request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE);
$request = ['g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE];
$requestOrder = ini_get('request_order') ?: ini_get('variables_order');
$requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
$_REQUEST = array();
$_REQUEST = [];
foreach (str_split($requestOrder) as $order) {
$_REQUEST = array_merge($_REQUEST, $request[$order]);
}
@@ -563,11 +593,26 @@ class Request
*
* You should only list the reverse proxies that you manage directly.
*
* @param array $proxies A list of trusted proxies
* @param array $proxies A list of trusted proxies
* @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies
*
* @throws \InvalidArgumentException When $trustedHeaderSet is invalid
*/
public static function setTrustedProxies(array $proxies)
public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/)
{
self::$trustedProxies = $proxies;
if (2 > \func_num_args()) {
@trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since Symfony 3.3. Defining it will be required in 4.0. ', __METHOD__), \E_USER_DEPRECATED);
return;
}
$trustedHeaderSet = (int) func_get_arg(1);
foreach (self::$trustedHeaderNames as $header => $name) {
self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null;
}
self::$trustedHeaderSet = $trustedHeaderSet;
}
/**
@@ -580,6 +625,16 @@ class Request
return self::$trustedProxies;
}
/**
* Gets the set of trusted headers from trusted proxies.
*
* @return int A bit field of Request::HEADER_* that defines which headers are trusted from your proxies
*/
public static function getTrustedHeaderSet()
{
return self::$trustedHeaderSet;
}
/**
* Sets a list of trusted host patterns.
*
@@ -593,7 +648,7 @@ class Request
return sprintf('{%s}i', $hostPattern);
}, $hostPatterns);
// we need to reset trusted hosts on trusted host patterns change
self::$trustedHosts = array();
self::$trustedHosts = [];
}
/**
@@ -623,14 +678,35 @@ class Request
* @param string $value The header name
*
* @throws \InvalidArgumentException
*
* @deprecated since version 3.3, to be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.
*/
public static function setTrustedHeaderName($key, $value)
{
if (!array_key_exists($key, self::$trustedHeaders)) {
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), \E_USER_DEPRECATED);
if ('forwarded' === $key) {
$key = self::HEADER_FORWARDED;
} elseif ('client_ip' === $key) {
$key = self::HEADER_CLIENT_IP;
} elseif ('client_host' === $key) {
$key = self::HEADER_CLIENT_HOST;
} elseif ('client_proto' === $key) {
$key = self::HEADER_CLIENT_PROTO;
} elseif ('client_port' === $key) {
$key = self::HEADER_CLIENT_PORT;
} elseif (!\array_key_exists($key, self::$trustedHeaders)) {
throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
}
self::$trustedHeaders[$key] = $value;
if (null !== $value) {
self::$trustedHeaderNames[$key] = $value;
self::$trustedHeaderSet |= $key;
} else {
self::$trustedHeaderSet &= ~$key;
}
}
/**
@@ -641,10 +717,16 @@ class Request
* @return string The header name
*
* @throws \InvalidArgumentException
*
* @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.
*/
public static function getTrustedHeaderName($key)
{
if (!array_key_exists($key, self::$trustedHeaders)) {
if (2 > \func_num_args() || func_get_arg(1)) {
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), \E_USER_DEPRECATED);
}
if (!\array_key_exists($key, self::$trustedHeaders)) {
throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key));
}
@@ -667,8 +749,8 @@ class Request
return '';
}
$parts = array();
$order = array();
$parts = [];
$order = [];
foreach (explode('&', $qs) as $param) {
if ('' === $param || '=' === $param[0]) {
@@ -689,7 +771,7 @@ class Request
$order[] = urldecode($keyValuePair[0]);
}
array_multisort($order, SORT_ASC, $parts);
array_multisort($order, \SORT_ASC, $parts);
return implode('&', $parts);
}
@@ -721,43 +803,30 @@ class Request
}
/**
* Gets a "parameter" value.
* Gets a "parameter" value from any bag.
*
* This method is mainly useful for libraries that want to provide some flexibility.
* This method is mainly useful for libraries that want to provide some flexibility. If you don't need the
* flexibility in controllers, it is better to explicitly get request parameters from the appropriate
* public property instead (attributes, query, request).
*
* Order of precedence: GET, PATH, POST
*
* Avoid using this method in controllers:
*
* * slow
* * prefer to get from a "named" source
*
* It is better to explicitly get request parameters from the appropriate
* public property instead (query, attributes, request).
*
* Note: Finding deep items is deprecated since version 2.8, to be removed in 3.0.
* Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
*
* @param string $key The key
* @param mixed $default The default value if the parameter key does not exist
* @param bool $deep Is parameter deep in multidimensional array
*
* @return mixed
*/
public function get($key, $default = null, $deep = false)
public function get($key, $default = null)
{
if ($deep) {
@trigger_error('Using paths to find deeper items in '.__METHOD__.' is deprecated since Symfony 2.8 and will be removed in 3.0. Filter the returned value in your own code instead.', E_USER_DEPRECATED);
}
if ($this !== $result = $this->query->get($key, $this, $deep)) {
if ($this !== $result = $this->attributes->get($key, $this)) {
return $result;
}
if ($this !== $result = $this->attributes->get($key, $this, $deep)) {
if ($this !== $result = $this->query->get($key, $this)) {
return $result;
}
if ($this !== $result = $this->request->get($key, $this, $deep)) {
if ($this !== $result = $this->request->get($key, $this)) {
return $result;
}
@@ -828,10 +897,10 @@ class Request
$ip = $this->server->get('REMOTE_ADDR');
if (!$this->isFromTrustedProxy()) {
return array($ip);
return [$ip];
}
return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip);
return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: [$ip];
}
/**
@@ -844,13 +913,13 @@ class Request
* adding the IP address where it received the request from.
*
* If your reverse proxy uses a different header name than "X-Forwarded-For",
* ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with
* the "client-ip" key.
* ("Client-Ip" for instance), configure it via the $trustedHeaderSet
* argument of the Request::setTrustedProxies() method instead.
*
* @return string|null The client IP address
*
* @see getClientIps()
* @see http://en.wikipedia.org/wiki/X-Forwarded-For
* @see https://wikipedia.org/wiki/X-Forwarded-For
*/
public function getClientIp()
{
@@ -951,7 +1020,8 @@ class Request
* The "X-Forwarded-Port" header must contain the client port.
*
* If your reverse proxy uses a different header name than "X-Forwarded-Port",
* configure it via "setTrustedHeaderName()" with the "client-port" key.
* configure it via via the $trustedHeaderSet argument of the
* Request::setTrustedProxies() method instead.
*
* @return int|string can be a string if fetched from the server bag
*/
@@ -971,8 +1041,8 @@ class Request
$pos = strrpos($host, ':');
}
if (false !== $pos) {
return (int) substr($host, $pos + 1);
if (false !== $pos && $port = substr($host, $pos + 1)) {
return (int) $port;
}
return 'https' === $this->getScheme() ? 443 : 80;
@@ -1138,7 +1208,7 @@ class Request
// A reference to the same base directory or an empty subdirectory must be prefixed with "./".
// This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
// as the first segment of a relative-path reference, as it would be mistaken for a scheme name
// (see http://tools.ietf.org/html/rfc3986#section-4.2).
// (see https://tools.ietf.org/html/rfc3986#section-4.2).
return !isset($path[0]) || '/' === $path[0]
|| false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
? "./$path" : $path;
@@ -1168,15 +1238,15 @@ class Request
* The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
*
* If your reverse proxy uses a different header name than "X-Forwarded-Proto"
* ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with
* the "client-proto" key.
* ("SSL_HTTPS" for instance), configure it via the $trustedHeaderSet
* argument of the Request::setTrustedProxies() method instead.
*
* @return bool
*/
public function isSecure()
{
if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) {
return \in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true);
return \in_array(strtolower($proto[0]), ['https', 'on', 'ssl', '1'], true);
}
$https = $this->server->get('HTTPS');
@@ -1193,11 +1263,12 @@ class Request
* The "X-Forwarded-Host" header must contain the client host name.
*
* If your reverse proxy uses a different header name than "X-Forwarded-Host",
* configure it via "setTrustedHeaderName()" with the "client-host" key.
* configure it via the $trustedHeaderSet argument of the
* Request::setTrustedProxies() method instead.
*
* @return string
*
* @throws \UnexpectedValueException when the host name is invalid
* @throws SuspiciousOperationException when the host name is invalid or not trusted
*/
public function getHost()
{
@@ -1217,7 +1288,12 @@ class Request
// check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
// use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
throw new \UnexpectedValueException(sprintf('Invalid Host "%s"', $host));
if (!$this->isHostValid) {
return '';
}
$this->isHostValid = false;
throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host));
}
if (\count(self::$trustedHostPatterns) > 0) {
@@ -1235,7 +1311,12 @@ class Request
}
}
throw new \UnexpectedValueException(sprintf('Untrusted Host "%s"', $host));
if (!$this->isHostValid) {
return '';
}
$this->isHostValid = false;
throw new SuspiciousOperationException(sprintf('Untrusted Host "%s".', $host));
}
return $host;
@@ -1291,12 +1372,12 @@ class Request
$method = strtoupper($method);
if (\in_array($method, array('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'PATCH', 'PURGE', 'TRACE'), true)) {
if (\in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'PATCH', 'PURGE', 'TRACE'], true)) {
return $this->method = $method;
}
if (!preg_match('/^[A-Z]++$/D', $method)) {
throw new \UnexpectedValueException(sprintf('Invalid method override "%s".', $method));
throw new SuspiciousOperationException(sprintf('Invalid method override "%s".', $method));
}
return $this->method = $method;
@@ -1330,6 +1411,22 @@ class Request
return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
}
/**
* Gets the mime types associated with the format.
*
* @param string $format The format
*
* @return array The associated mime types
*/
public static function getMimeTypes($format)
{
if (null === static::$formats) {
static::initializeFormats();
}
return isset(static::$formats[$format]) ? static::$formats[$format] : [];
}
/**
* Gets the format associated with the mime type.
*
@@ -1356,6 +1453,8 @@ class Request
return $format;
}
}
return null;
}
/**
@@ -1370,7 +1469,7 @@ class Request
static::initializeFormats();
}
static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : [$mimeTypes];
}
/**
@@ -1379,17 +1478,17 @@ class Request
* Here is the process to determine the format:
*
* * format defined by the user (with setRequestFormat())
* * _format request parameter
* * _format request attribute
* * $default
*
* @param string|null $default The default format
*
* @return string The request format
* @return string|null The request format
*/
public function getRequestFormat($default = 'html')
{
if (null === $this->format) {
$this->format = $this->get('_format');
$this->format = $this->attributes->get('_format');
}
return null === $this->format ? $default : $this->format;
@@ -1472,7 +1571,7 @@ class Request
}
/**
* Checks whether the method is safe or not.
* Checks whether or not the method is safe.
*
* @see https://tools.ietf.org/html/rfc7231#section-4.2.1
*
@@ -1482,7 +1581,25 @@ class Request
*/
public function isMethodSafe(/* $andCacheable = true */)
{
return \in_array($this->getMethod(), 0 < \func_num_args() && !func_get_arg(0) ? array('GET', 'HEAD', 'OPTIONS', 'TRACE') : array('GET', 'HEAD'));
if (!\func_num_args() || func_get_arg(0)) {
// This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature)
// then setting $andCacheable to false should be deprecated in 4.1
@trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', \E_USER_DEPRECATED);
return \in_array($this->getMethod(), ['GET', 'HEAD']);
}
return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']);
}
/**
* Checks whether or not the method is idempotent.
*
* @return bool
*/
public function isMethodIdempotent()
{
return \in_array($this->getMethod(), ['HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE']);
}
/**
@@ -1494,7 +1611,31 @@ class Request
*/
public function isMethodCacheable()
{
return \in_array($this->getMethod(), array('GET', 'HEAD'));
return \in_array($this->getMethod(), ['GET', 'HEAD']);
}
/**
* Returns the protocol version.
*
* If the application is behind a proxy, the protocol version used in the
* requests between the client and the proxy and between the proxy and the
* server might be different. This returns the former (from the "Via" header)
* if the proxy is trusted (see "setTrustedProxies()"), otherwise it returns
* the latter (from the "SERVER_PROTOCOL" server parameter).
*
* @return string
*/
public function getProtocolVersion()
{
if ($this->isFromTrustedProxy()) {
preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches);
if ($matches) {
return 'HTTP/'.$matches[2];
}
}
return $this->server->get('SERVER_PROTOCOL');
}
/**
@@ -1554,7 +1695,7 @@ class Request
*/
public function getETags()
{
return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, \PREG_SPLIT_NO_EMPTY);
}
/**
@@ -1584,7 +1725,7 @@ class Request
return $locales[0];
}
$extendedPreferredLanguages = array();
$extendedPreferredLanguages = [];
foreach ($preferredLanguages as $language) {
$extendedPreferredLanguages[] = $language;
if (false !== $position = strpos($language, '_')) {
@@ -1612,7 +1753,7 @@ class Request
}
$languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
$this->languages = array();
$this->languages = [];
foreach ($languages as $lang => $acceptHeaderItem) {
if (false !== strpos($lang, '-')) {
$codes = explode('-', $lang);
@@ -1688,7 +1829,7 @@ class Request
* It works if your JavaScript library sets an X-Requested-With HTTP header.
* It is known to work with common JavaScript frameworks:
*
* @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
* @see https://wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
*
* @return bool true if the request is an XMLHttpRequest, false otherwise
*/
@@ -1700,9 +1841,9 @@ class Request
/*
* The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
*
* Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
* Code subject to the new BSD license (https://framework.zend.com/license).
*
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (https://www.zend.com/)
*/
protected function prepareRequestUri()
@@ -1716,10 +1857,24 @@ class Request
$this->server->remove('IIS_WasUrlRewritten');
} elseif ($this->server->has('REQUEST_URI')) {
$requestUri = $this->server->get('REQUEST_URI');
// HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path
$schemeAndHttpHost = $this->getSchemeAndHttpHost();
if (0 === strpos($requestUri, $schemeAndHttpHost)) {
$requestUri = substr($requestUri, \strlen($schemeAndHttpHost));
if ('' !== $requestUri && '/' === $requestUri[0]) {
// To only use path and query remove the fragment.
if (false !== $pos = strpos($requestUri, '#')) {
$requestUri = substr($requestUri, 0, $pos);
}
} else {
// HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path,
// only use URL path.
$uriComponents = parse_url($requestUri);
if (isset($uriComponents['path'])) {
$requestUri = $uriComponents['path'];
}
if (isset($uriComponents['query'])) {
$requestUri .= '?'.$uriComponents['query'];
}
}
} elseif ($this->server->has('ORIG_PATH_INFO')) {
// IIS 5.0, PHP as CGI
@@ -1812,12 +1967,12 @@ class Request
*/
protected function prepareBasePath()
{
$filename = basename($this->server->get('SCRIPT_FILENAME'));
$baseUrl = $this->getBaseUrl();
if (empty($baseUrl)) {
return '';
}
$filename = basename($this->server->get('SCRIPT_FILENAME'));
if (basename($baseUrl) === $filename) {
$basePath = \dirname($baseUrl);
} else {
@@ -1838,8 +1993,6 @@ class Request
*/
protected function preparePathInfo()
{
$baseUrl = $this->getBaseUrl();
if (null === ($requestUri = $this->getRequestUri())) {
return '/';
}
@@ -1852,12 +2005,14 @@ class Request
$requestUri = '/'.$requestUri;
}
if (null === ($baseUrl = $this->getBaseUrl())) {
return $requestUri;
}
$pathInfo = substr($requestUri, \strlen($baseUrl));
if (null !== $baseUrl && (false === $pathInfo || '' === $pathInfo)) {
if (false === $pathInfo || '' === $pathInfo) {
// If substr() returns false then PATH_INFO is set to an empty string
return '/';
} elseif (null === $baseUrl) {
return $requestUri;
}
return (string) $pathInfo;
@@ -1868,19 +2023,19 @@ class Request
*/
protected static function initializeFormats()
{
static::$formats = array(
'html' => array('text/html', 'application/xhtml+xml'),
'txt' => array('text/plain'),
'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'),
'css' => array('text/css'),
'json' => array('application/json', 'application/x-json'),
'jsonld' => array('application/ld+json'),
'xml' => array('text/xml', 'application/xml', 'application/x-xml'),
'rdf' => array('application/rdf+xml'),
'atom' => array('application/atom+xml'),
'rss' => array('application/rss+xml'),
'form' => array('application/x-www-form-urlencoded'),
);
static::$formats = [
'html' => ['text/html', 'application/xhtml+xml'],
'txt' => ['text/plain'],
'js' => ['application/javascript', 'application/x-javascript', 'text/javascript'],
'css' => ['text/css'],
'json' => ['application/json', 'application/x-json'],
'jsonld' => ['application/ld+json'],
'xml' => ['text/xml', 'application/xml', 'application/x-xml'],
'rdf' => ['application/rdf+xml'],
'atom' => ['application/atom+xml'],
'rss' => ['application/rss+xml'],
'form' => ['application/x-www-form-urlencoded'],
];
}
/**
@@ -1901,7 +2056,7 @@ class Request
}
}
/*
/**
* Returns the prefix as encoded in the string when the string starts with
* the given prefix, false otherwise.
*
@@ -1925,7 +2080,7 @@ class Request
return false;
}
private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
{
if (self::$requestFactory) {
$request = \call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);
@@ -1940,15 +2095,23 @@ class Request
return new static($query, $request, $attributes, $cookies, $files, $server, $content);
}
private function isFromTrustedProxy()
/**
* Indicates whether this request originated from a trusted proxy.
*
* This can be useful to determine whether or not to trust the
* contents of a proxy-specific header.
*
* @return bool true if the request came from a trusted proxy, false otherwise
*/
public function isFromTrustedProxy()
{
return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
}
private function getTrustedValues($type, $ip = null)
{
$clientValues = array();
$forwardedValues = array();
$clientValues = [];
$forwardedValues = [];
if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) {
foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) {
@@ -1958,7 +2121,7 @@ class Request
if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
$forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
$forwardedValues = preg_match_all(sprintf('{(?:%s)="?([a-zA-Z0-9\.:_\-/\[\]]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array();
$forwardedValues = preg_match_all(sprintf('{(?:%s)="?([a-zA-Z0-9\.:_\-/\[\]]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : [];
if (self::HEADER_CLIENT_PORT === $type) {
foreach ($forwardedValues as $k => $v) {
if (']' === substr($v, -1) || false === $v = strrchr($v, ':')) {
@@ -1983,7 +2146,7 @@ class Request
}
if (!$this->isForwardedValid) {
return null !== $ip ? array('0.0.0.0', $ip) : array();
return null !== $ip ? ['0.0.0.0', $ip] : [];
}
$this->isForwardedValid = false;
@@ -1993,7 +2156,7 @@ class Request
private function normalizeAndFilterClientIps(array $clientIps, $ip)
{
if (!$clientIps) {
return array();
return [];
}
$clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
$firstTrustedIp = null;
@@ -2012,7 +2175,7 @@ class Request
$clientIps[$key] = $clientIp = substr($clientIp, 1, $i - 1);
}
if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
if (!filter_var($clientIp, \FILTER_VALIDATE_IP)) {
unset($clientIps[$key]);
continue;
@@ -2029,6 +2192,6 @@ class Request
}
// Now the IP chain contains only untrusted proxies and the client IP
return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp];
}
}
+8 -9
View File
@@ -31,32 +31,31 @@ class RequestMatcher implements RequestMatcherInterface
/**
* @var string[]
*/
private $methods = array();
private $methods = [];
/**
* @var string[]
*/
private $ips = array();
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($path = null, $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null)
{
$this->matchPath($path);
$this->matchHost($host);
@@ -76,7 +75,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) : [];
}
/**
@@ -116,7 +115,7 @@ class RequestMatcher implements RequestMatcherInterface
*/
public function matchIps($ips)
{
$this->ips = null !== $ips ? (array) $ips : array();
$this->ips = null !== $ips ? (array) $ips : [];
}
/**
@@ -126,7 +125,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) : [];
}
/**
+4 -4
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];
+166 -52
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,7 +191,7 @@ class Response
508 => 'Loop Detected', // RFC5842
510 => 'Not Extended', // RFC2774
511 => 'Network Authentication Required', // RFC6585
);
];
/**
* @param mixed $content The response content, see setContent()
@@ -200,17 +200,12 @@ class Response
*
* @throws \InvalidArgumentException When the HTTP status code is not valid
*/
public function __construct($content = '', $status = 200, $headers = array())
public function __construct($content = '', $status = 200, $headers = [])
{
$this->headers = new ResponseHeaderBag($headers);
$this->setContent($content);
$this->setStatusCode($status);
$this->setProtocolVersion('1.0');
/* RFC2616 - 14.18 says all Responses need to have a Date */
if (!$this->headers->has('Date')) {
$this->setDate(\DateTime::createFromFormat('U', time()));
}
}
/**
@@ -227,7 +222,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);
}
@@ -315,9 +310,9 @@ class Response
}
// Check if we need to send extra expire info headers
if ('1.0' == $this->getProtocolVersion() && 'no-cache' == $this->headers->get('Cache-Control')) {
$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);
@@ -337,27 +332,22 @@ class Response
return $this;
}
/* RFC2616 - 14.18 says all Responses need to have a Date */
if (!$this->headers->has('Date')) {
$this->setDate(\DateTime::createFromFormat('U', time()));
}
// headers
foreach ($this->headers->allPreserveCase() as $name => $values) {
foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
$replace = 0 === strcasecmp($name, 'Content-Type');
foreach ($values as $value) {
header($name.': '.$value, $replace, $this->statusCode);
}
}
// status
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
// cookies
foreach ($this->headers->getCookies() as $cookie) {
setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
header('Set-Cookie: '.$cookie, false, $this->statusCode);
}
// status
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
return $this;
}
@@ -385,7 +375,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);
}
@@ -405,7 +395,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)));
}
@@ -417,7 +407,7 @@ class Response
/**
* Gets the current response content.
*
* @return string Content
* @return string|false
*/
public function getContent()
{
@@ -430,6 +420,8 @@ class Response
* @param string $version The HTTP protocol version
*
* @return $this
*
* @final since version 3.2
*/
public function setProtocolVersion($version)
{
@@ -442,6 +434,8 @@ class Response
* Gets the HTTP protocol version.
*
* @return string The HTTP protocol version
*
* @final since version 3.2
*/
public function getProtocolVersion()
{
@@ -460,6 +454,8 @@ class Response
* @return $this
*
* @throws \InvalidArgumentException When the HTTP status code is not valid
*
* @final since version 3.2
*/
public function setStatusCode($code, $text = null)
{
@@ -489,6 +485,8 @@ class Response
* Retrieves the status code for the current web response.
*
* @return int Status code
*
* @final since version 3.2
*/
public function getStatusCode()
{
@@ -501,6 +499,8 @@ class Response
* @param string $charset Character set
*
* @return $this
*
* @final since version 3.2
*/
public function setCharset($charset)
{
@@ -513,6 +513,8 @@ class Response
* Retrieves the response charset.
*
* @return string Character set
*
* @final since version 3.2
*/
public function getCharset()
{
@@ -535,10 +537,12 @@ class Response
* (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
*/
public function isCacheable()
{
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;
}
@@ -557,6 +561,8 @@ class Response
* 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
*/
public function isFresh()
{
@@ -568,6 +574,8 @@ class Response
* 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
*/
public function isValidateable()
{
@@ -580,6 +588,8 @@ class Response
* It makes the response ineligible for serving other clients.
*
* @return $this
*
* @final since version 3.2
*/
public function setPrivate()
{
@@ -595,6 +605,8 @@ class Response
* It makes the response eligible for serving other clients.
*
* @return $this
*
* @final since version 3.2
*/
public function setPublic()
{
@@ -605,7 +617,39 @@ class Response
}
/**
* Returns true if the response must be revalidated by caches.
* Marks the response as "immutable".
*
* @param bool $immutable enables or disables the immutable directive
*
* @return $this
*
* @final
*/
public function setImmutable($immutable = true)
{
if ($immutable) {
$this->headers->addCacheControlDirective('immutable');
} else {
$this->headers->removeCacheControlDirective('immutable');
}
return $this;
}
/**
* 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()
{
return $this->headers->hasCacheControlDirective('immutable');
}
/**
* Returns true if the response must be revalidated by shared caches once it has become stale.
*
* This method indicates that the response must not be served stale by a
* cache in any circumstance without first revalidating with the origin.
@@ -613,6 +657,8 @@ class Response
* 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
*/
public function mustRevalidate()
{
@@ -625,18 +671,11 @@ class Response
* @return \DateTime A \DateTime instance
*
* @throws \RuntimeException When the header is not parseable
*
* @final since version 3.2
*/
public function getDate()
{
/*
RFC2616 - 14.18 says all Responses need to have a Date.
Make sure we provide one even if it the header
has been removed in the meantime.
*/
if (!$this->headers->has('Date')) {
$this->setDate(\DateTime::createFromFormat('U', time()));
}
return $this->headers->getDate('Date');
}
@@ -644,6 +683,8 @@ class Response
* Sets the Date header.
*
* @return $this
*
* @final since version 3.2
*/
public function setDate(\DateTime $date)
{
@@ -657,6 +698,8 @@ class Response
* Returns the age of the response.
*
* @return int The age of the response in seconds
*
* @final since version 3.2
*/
public function getAge()
{
@@ -664,7 +707,7 @@ class Response
return (int) $age;
}
return max(time() - $this->getDate()->format('U'), 0);
return max(time() - (int) $this->getDate()->format('U'), 0);
}
/**
@@ -686,6 +729,8 @@ 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
*/
public function getExpires()
{
@@ -693,7 +738,7 @@ class Response
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(\DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
}
}
@@ -705,6 +750,8 @@ class Response
* @param \DateTime|null $date A \DateTime instance or null to remove the header
*
* @return $this
*
* @final since version 3.2
*/
public function setExpires(\DateTime $date = null)
{
@@ -727,6 +774,8 @@ class Response
* 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
*/
public function getMaxAge()
{
@@ -739,8 +788,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;
}
/**
@@ -751,6 +802,8 @@ class Response
* @param int $value Number of seconds
*
* @return $this
*
* @final since version 3.2
*/
public function setMaxAge($value)
{
@@ -767,6 +820,8 @@ class Response
* @param int $value Number of seconds
*
* @return $this
*
* @final since version 3.2
*/
public function setSharedMaxAge($value)
{
@@ -785,12 +840,16 @@ class Response
* revalidating with the origin.
*
* @return int|null The TTL in seconds
*
* @final since version 3.2
*/
public function getTtl()
{
if (null !== $maxAge = $this->getMaxAge()) {
return $maxAge - $this->getAge();
}
return null;
}
/**
@@ -801,6 +860,8 @@ class Response
* @param int $seconds Number of seconds
*
* @return $this
*
* @final since version 3.2
*/
public function setTtl($seconds)
{
@@ -817,6 +878,8 @@ class Response
* @param int $seconds Number of seconds
*
* @return $this
*
* @final since version 3.2
*/
public function setClientTtl($seconds)
{
@@ -831,6 +894,8 @@ class Response
* @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
*/
public function getLastModified()
{
@@ -845,6 +910,8 @@ class Response
* @param \DateTime|null $date A \DateTime instance or null to remove the header
*
* @return $this
*
* @final since version 3.2
*/
public function setLastModified(\DateTime $date = null)
{
@@ -863,6 +930,8 @@ class Response
* 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
*/
public function getEtag()
{
@@ -876,6 +945,8 @@ class Response
* @param bool $weak Whether you want a weak ETag or not
*
* @return $this
*
* @final since version 3.2
*/
public function setEtag($etag = null, $weak = false)
{
@@ -895,18 +966,20 @@ class Response
/**
* Sets the response's cache headers (validation and/or expiration).
*
* Available options are: etag, last_modified, max_age, s_maxage, private, and public.
* 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
*/
public function setCache(array $options)
{
if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) {
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_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'])) {
@@ -941,6 +1014,10 @@ class Response
}
}
if (isset($options['immutable'])) {
$this->setImmutable((bool) $options['immutable']);
}
return $this;
}
@@ -952,7 +1029,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
*/
public function setNotModified()
{
@@ -960,7 +1039,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);
}
@@ -971,6 +1050,8 @@ 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
*/
public function hasVary()
{
@@ -981,14 +1062,16 @@ 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
*/
public function getVary()
{
if (!$vary = $this->headers->get('Vary', null, false)) {
return array();
return [];
}
$ret = array();
$ret = [];
foreach ($vary as $item) {
$ret = array_merge($ret, preg_split('/[\s,]+/', $item));
}
@@ -1003,6 +1086,8 @@ class Response
* @param bool $replace Whether to replace the actual value or not (true by default)
*
* @return $this
*
* @final since version 3.2
*/
public function setVary($headers, $replace = true)
{
@@ -1019,6 +1104,8 @@ class Response
* removes the actual content by calling the setNotModified() method.
*
* @return bool true if the Response validators match the Request, false otherwise
*
* @final since version 3.3
*/
public function isNotModified(Request $request)
{
@@ -1050,7 +1137,9 @@ class Response
*
* @return bool
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
* @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
*
* @final since version 3.2
*/
public function isInvalid()
{
@@ -1061,6 +1150,8 @@ class Response
* Is response informative?
*
* @return bool
*
* @final since version 3.3
*/
public function isInformational()
{
@@ -1071,6 +1162,8 @@ class Response
* Is response successful?
*
* @return bool
*
* @final since version 3.2
*/
public function isSuccessful()
{
@@ -1081,6 +1174,8 @@ class Response
* Is the response a redirect?
*
* @return bool
*
* @final since version 3.2
*/
public function isRedirection()
{
@@ -1091,6 +1186,8 @@ class Response
* Is there a client error?
*
* @return bool
*
* @final since version 3.2
*/
public function isClientError()
{
@@ -1101,6 +1198,8 @@ class Response
* Was there a server side error?
*
* @return bool
*
* @final since version 3.3
*/
public function isServerError()
{
@@ -1111,6 +1210,8 @@ class Response
* Is the response OK?
*
* @return bool
*
* @final since version 3.2
*/
public function isOk()
{
@@ -1121,6 +1222,8 @@ class Response
* Is the response forbidden?
*
* @return bool
*
* @final since version 3.2
*/
public function isForbidden()
{
@@ -1131,6 +1234,8 @@ class Response
* Is the response a not found error?
*
* @return bool
*
* @final since version 3.2
*/
public function isNotFound()
{
@@ -1143,20 +1248,24 @@ class Response
* @param string $location
*
* @return bool
*
* @final since version 3.2
*/
public function isRedirect($location = null)
{
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
*/
public function isEmpty()
{
return \in_array($this->statusCode, array(204, 304));
return \in_array($this->statusCode, [204, 304]);
}
/**
@@ -1166,12 +1275,15 @@ class Response
*
* @param int $targetLevel The target output buffering level
* @param bool $flush Whether to flush or clean the buffers
*
* @final since version 3.3
*/
public static function closeOutputBuffers($targetLevel, $flush)
{
$status = ob_get_status(true);
$level = \count($status);
$flags = \defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
// 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;
while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) {
if ($flush) {
@@ -1186,6 +1298,8 @@ class Response
* Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9.
*
* @see http://support.microsoft.com/kb/323308
*
* @final since version 3.3
*/
protected function ensureIEOverSSLCompatibility(Request $request)
{
+100 -40
View File
@@ -24,32 +24,22 @@ 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);
if (!isset($this->headers['cache-control'])) {
$this->set('Cache-Control', '');
}
}
/**
* {@inheritdoc}
*/
public function __toString()
{
$cookies = '';
foreach ($this->getCookies() as $cookie) {
$cookies .= 'Set-Cookie: '.$cookie."\r\n";
/* RFC2616 - 14.18 says all Responses need to have a Date */
if (!isset($this->headers['date'])) {
$this->initDate();
}
ksort($this->headerNames);
return parent::__toString().$cookies;
}
/**
@@ -59,21 +49,53 @@ class ResponseHeaderBag extends HeaderBag
*/
public function allPreserveCase()
{
return array_combine($this->headerNames, $this->headers);
$headers = [];
foreach ($this->all() as $name => $value) {
$headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value;
}
return $headers;
}
public function allPreserveCaseWithoutCookies()
{
$headers = $this->allPreserveCase();
if (isset($this->headerNames['set-cookie'])) {
unset($headers[$this->headerNames['set-cookie']]);
}
return $headers;
}
/**
* {@inheritdoc}
*/
public function replace(array $headers = array())
public function replace(array $headers = [])
{
$this->headerNames = array();
$this->headerNames = [];
parent::replace($headers);
if (!isset($this->headers['cache-control'])) {
$this->set('Cache-Control', '');
}
if (!isset($this->headers['date'])) {
$this->initDate();
}
}
/**
* {@inheritdoc}
*/
public function all()
{
$headers = parent::all();
foreach ($this->getCookies() as $cookie) {
$headers['set-cookie'][] = (string) $cookie;
}
return $headers;
}
/**
@@ -81,15 +103,28 @@ class ResponseHeaderBag extends HeaderBag
*/
public function set($key, $values, $replace = true)
{
parent::set($key, $values, $replace);
$uniqueKey = str_replace('_', '-', strtolower($key));
if ('set-cookie' === $uniqueKey) {
if ($replace) {
$this->cookies = [];
}
foreach ((array) $values as $cookie) {
$this->setCookie(Cookie::fromString($cookie));
}
$this->headerNames[$uniqueKey] = $key;
return;
}
$this->headerNames[$uniqueKey] = $key;
parent::set($key, $values, $replace);
// ensure the cache-control header has sensible defaults
if (\in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'))) {
if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true)) {
$computed = $this->computeCacheControlValue();
$this->headers['cache-control'] = array($computed);
$this->headers['cache-control'] = [$computed];
$this->headerNames['cache-control'] = 'Cache-Control';
$this->computedCacheControl = $this->parseCacheControl($computed);
}
@@ -100,13 +135,23 @@ class ResponseHeaderBag extends HeaderBag
*/
public function remove($key)
{
parent::remove($key);
$uniqueKey = str_replace('_', '-', strtolower($key));
unset($this->headerNames[$uniqueKey]);
if ('set-cookie' === $uniqueKey) {
$this->cookies = [];
return;
}
parent::remove($key);
if ('cache-control' === $uniqueKey) {
$this->computedCacheControl = array();
$this->computedCacheControl = [];
}
if ('date' === $uniqueKey) {
$this->initDate();
}
}
@@ -115,7 +160,7 @@ class ResponseHeaderBag extends HeaderBag
*/
public function hasCacheControlDirective($key)
{
return array_key_exists($key, $this->computedCacheControl);
return \array_key_exists($key, $this->computedCacheControl);
}
/**
@@ -123,12 +168,13 @@ 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)
{
$this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
$this->headerNames['set-cookie'] = 'Set-Cookie';
}
/**
@@ -153,6 +199,10 @@ class ResponseHeaderBag extends HeaderBag
unset($this->cookies[$domain]);
}
}
if (empty($this->cookies)) {
unset($this->headerNames['set-cookie']);
}
}
/**
@@ -166,15 +216,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) {
@@ -194,10 +244,13 @@ class ResponseHeaderBag extends HeaderBag
* @param string $domain
* @param bool $secure
* @param bool $httpOnly
* @param string $sameSite
*/
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true/*, $sameSite = null*/)
{
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
$sameSite = \func_num_args() > 5 ? func_get_arg(5) : null;
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, $sameSite));
}
/**
@@ -217,7 +270,7 @@ class ResponseHeaderBag extends HeaderBag
*/
public function makeDisposition($disposition, $filename, $filenameFallback = '')
{
if (!\in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE])) {
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
}
@@ -259,13 +312,13 @@ class ResponseHeaderBag extends HeaderBag
*/
protected function computeCacheControlValue()
{
if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
return 'no-cache';
}
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();
@@ -280,4 +333,11 @@ class ResponseHeaderBag extends HeaderBag
return $header;
}
private function initDate()
{
$now = \DateTime::createFromFormat('U', time());
$now->setTimezone(new \DateTimeZone('UTC'));
$this->set('Date', $now->format('D, d M Y H:i:s').' GMT');
}
}
+7 -7
View File
@@ -27,8 +27,8 @@ class ServerBag extends ParameterBag
*/
public function getHeaders()
{
$headers = array();
$contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
$headers = [];
$contentHeaders = ['CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true];
foreach ($this->parameters as $key => $value) {
if (0 === strpos($key, 'HTTP_')) {
$headers[substr($key, 5)] = $value;
@@ -46,13 +46,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 +79,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;
}
@@ -19,7 +19,7 @@ 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
@@ -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;
}
@@ -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];
@@ -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 = '_sf2_flashes')
public function __construct($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];
}
/**
+7 -23
View File
@@ -14,20 +14,18 @@ namespace Symfony\Component\HttpFoundation\Session\Flash;
/**
* FlashBag flash message container.
*
* \IteratorAggregate implementation is deprecated and will be removed in 3.0.
*
* @author Drak <drak@zikula.org>
*/
class FlashBag implements FlashBagInterface, \IteratorAggregate
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 = '_sf2_flashes')
public function __construct($storageKey = '_symfony_flashes')
{
$this->storageKey = $storageKey;
}
@@ -64,7 +62,7 @@ class FlashBag implements FlashBagInterface, \IteratorAggregate
/**
* {@inheritdoc}
*/
public function peek($type, array $default = array())
public function peek($type, array $default = [])
{
return $this->has($type) ? $this->flashes[$type] : $default;
}
@@ -80,7 +78,7 @@ class FlashBag implements FlashBagInterface, \IteratorAggregate
/**
* {@inheritdoc}
*/
public function get($type, array $default = array())
public function get($type, array $default = [])
{
if (!$this->has($type)) {
return $default;
@@ -99,7 +97,7 @@ class FlashBag implements FlashBagInterface, \IteratorAggregate
public function all()
{
$return = $this->peekAll();
$this->flashes = array();
$this->flashes = [];
return $return;
}
@@ -125,7 +123,7 @@ class FlashBag implements FlashBagInterface, \IteratorAggregate
*/
public function has($type)
{
return array_key_exists($type, $this->flashes) && $this->flashes[$type];
return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
}
/**
@@ -151,18 +149,4 @@ class FlashBag implements FlashBagInterface, \IteratorAggregate
{
return $this->all();
}
/**
* Returns an iterator for flashes.
*
* @deprecated since version 2.4, to be removed in 3.0.
*
* @return \ArrayIterator An \ArrayIterator instance
*/
public function getIterator()
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.4 and will be removed in 3.0.', E_USER_DEPRECATED);
return new \ArrayIterator($this->all());
}
}
@@ -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.
+58 -11
View File
@@ -28,6 +28,8 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
private $flashName;
private $attributeName;
private $data = [];
private $usageIndex = 0;
/**
* @param SessionStorageInterface $storage A SessionStorageInterface instance
@@ -60,7 +62,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function has($name)
{
return $this->storage->getBag($this->attributeName)->has($name);
return $this->getAttributeBag()->has($name);
}
/**
@@ -68,7 +70,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function get($name, $default = null)
{
return $this->storage->getBag($this->attributeName)->get($name, $default);
return $this->getAttributeBag()->get($name, $default);
}
/**
@@ -76,7 +78,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function set($name, $value)
{
$this->storage->getBag($this->attributeName)->set($name, $value);
$this->getAttributeBag()->set($name, $value);
}
/**
@@ -84,7 +86,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function all()
{
return $this->storage->getBag($this->attributeName)->all();
return $this->getAttributeBag()->all();
}
/**
@@ -92,7 +94,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function replace(array $attributes)
{
$this->storage->getBag($this->attributeName)->replace($attributes);
$this->getAttributeBag()->replace($attributes);
}
/**
@@ -100,7 +102,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function remove($name)
{
return $this->storage->getBag($this->attributeName)->remove($name);
return $this->getAttributeBag()->remove($name);
}
/**
@@ -108,7 +110,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function clear()
{
$this->storage->getBag($this->attributeName)->clear();
$this->getAttributeBag()->clear();
}
/**
@@ -126,7 +128,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function getIterator()
{
return new \ArrayIterator($this->storage->getBag($this->attributeName)->all());
return new \ArrayIterator($this->getAttributeBag()->all());
}
/**
@@ -136,7 +138,36 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function count()
{
return \count($this->storage->getBag($this->attributeName)->all());
return \count($this->getAttributeBag()->all());
}
/**
* @return int
*
* @internal
*/
public function getUsageIndex()
{
return $this->usageIndex;
}
/**
* @return bool
*
* @internal
*/
public function isEmpty()
{
if ($this->isStarted()) {
++$this->usageIndex;
}
foreach ($this->data as &$data) {
if (!empty($data)) {
return false;
}
}
return true;
}
/**
@@ -204,6 +235,8 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function getMetadataBag()
{
++$this->usageIndex;
return $this->storage->getMetadataBag();
}
@@ -212,7 +245,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function registerBag(SessionBagInterface $bag)
{
$this->storage->registerBag($bag);
$this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->usageIndex));
}
/**
@@ -220,7 +253,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function getBag($name)
{
return $this->storage->getBag($name);
$bag = $this->storage->getBag($name);
return method_exists($bag, 'getBag') ? $bag->getBag() : $bag;
}
/**
@@ -232,4 +267,16 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
{
return $this->getBag($this->flashName);
}
/**
* Gets the attributebag interface.
*
* Note that this method was added to help with IDE autocompletion.
*
* @return AttributeBagInterface
*/
private function getAttributeBag()
{
return $this->getBag($this->attributeName);
}
}
@@ -1,268 +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('The '.__NAMESPACE__.'\LegacyPdoSessionHandler class is deprecated since Symfony 2.6 and will be removed in 3.0. Use the Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler class instead.', E_USER_DEPRECATED);
/**
* Session handler using a PDO connection to read and write data.
*
* Session data is a binary string that can contain non-printable characters like the null byte.
* For this reason this handler base64 encodes the data to be able to save it in a character column.
*
* This version of the PdoSessionHandler does NOT implement locking. So concurrent requests to the
* same session can result in data loss due to race conditions.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Michael Williams <michael.williams@funsational.com>
* @author Tobias Schultze <http://tobion.de>
*
* @deprecated since version 2.6, to be removed in 3.0. Use
* {@link PdoSessionHandler} instead.
*/
class LegacyPdoSessionHandler implements \SessionHandlerInterface
{
private $pdo;
/**
* @var string Table name
*/
private $table;
/**
* @var string Column for session id
*/
private $idCol;
/**
* @var string Column for session data
*/
private $dataCol;
/**
* @var string Column for timestamp
*/
private $timeCol;
/**
* Constructor.
*
* List of available options:
* * db_table: The name of the table [required]
* * db_id_col: The column where to store the session id [default: sess_id]
* * db_data_col: The column where to store the session data [default: sess_data]
* * db_time_col: The column where to store the timestamp [default: sess_time]
*
* @param \PDO $pdo A \PDO instance
* @param array $dbOptions An associative array of DB options
*
* @throws \InvalidArgumentException When "db_table" option is not provided
*/
public function __construct(\PDO $pdo, array $dbOptions = array())
{
if (!array_key_exists('db_table', $dbOptions)) {
throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.');
}
if (\PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) {
throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
}
$this->pdo = $pdo;
$dbOptions = array_merge(array(
'db_id_col' => 'sess_id',
'db_data_col' => 'sess_data',
'db_time_col' => 'sess_time',
), $dbOptions);
$this->table = $dbOptions['db_table'];
$this->idCol = $dbOptions['db_id_col'];
$this->dataCol = $dbOptions['db_data_col'];
$this->timeCol = $dbOptions['db_time_col'];
}
/**
* {@inheritdoc}
*/
public function open($savePath, $sessionName)
{
return true;
}
/**
* {@inheritdoc}
*/
public function close()
{
return true;
}
/**
* {@inheritdoc}
*/
public function destroy($sessionId)
{
// delete the record associated with this id
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->execute();
} catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e);
}
return true;
}
/**
* {@inheritdoc}
*/
public function gc($maxlifetime)
{
// delete the session records that have expired
$sql = "DELETE FROM $this->table WHERE $this->timeCol < :time";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT);
$stmt->execute();
} catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e);
}
return true;
}
/**
* {@inheritdoc}
*/
public function read($sessionId)
{
$sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->execute();
// We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed
$sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
if ($sessionRows) {
return base64_decode($sessionRows[0][0]);
}
return '';
} catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
}
}
/**
* {@inheritdoc}
*/
public function write($sessionId, $data)
{
$encoded = base64_encode($data);
try {
// We use a single MERGE SQL query when supported by the database.
$mergeSql = $this->getMergeSql();
if (null !== $mergeSql) {
$mergeStmt = $this->pdo->prepare($mergeSql);
$mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$mergeStmt->execute();
return true;
}
$updateStmt = $this->pdo->prepare(
"UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id"
);
$updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$updateStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$updateStmt->execute();
// When MERGE is not supported, like in Postgres, we have to use this approach that can result in
// duplicate key errors when the same session is written simultaneously. We can just catch such an
// error and re-execute the update. This is similar to a serializable transaction with retry logic
// on serialization failures but without the overhead and without possible false positives due to
// longer gap locking.
if (!$updateStmt->rowCount()) {
try {
$insertStmt = $this->pdo->prepare(
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"
);
$insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$insertStmt->execute();
} catch (\PDOException $e) {
// Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
if (0 === strpos($e->getCode(), '23')) {
$updateStmt->execute();
} else {
throw $e;
}
}
}
} catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
}
return true;
}
/**
* Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database.
*
* @return string|null The SQL string or null when not supported
*/
private function getMergeSql()
{
$driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
switch ($driver) {
case 'mysql':
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)";
case 'oci':
// DUAL is Oracle specific dummy table
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time";
case 'sqlsrv' === $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
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;";
case 'sqlite':
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)";
}
}
/**
* Return a PDO instance.
*
* @return \PDO
*/
protected function getConnection()
{
return $this->pdo;
}
}
@@ -11,8 +11,12 @@
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
{
@@ -40,10 +44,10 @@ class MemcacheSessionHandler implements \SessionHandlerInterface
*
* @throws \InvalidArgumentException When unsupported options are passed
*/
public function __construct(\Memcache $memcache, array $options = array())
public function __construct(\Memcache $memcache, array $options = [])
{
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->memcache = $memcache;
@@ -15,11 +15,11 @@ 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>
*/
class MemcachedSessionHandler implements \SessionHandlerInterface
class MemcachedSessionHandler extends AbstractSessionHandler
{
private $memcached;
@@ -38,19 +38,16 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
*
* List of available options:
* * prefix: The prefix to use for the memcached keys in order to avoid collision
* * expiretime: The time to live in seconds
*
* @param \Memcached $memcached A \Memcached instance
* @param array $options An associative array of Memcached options
* * expiretime: The time to live in seconds.
*
* @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;
@@ -58,33 +55,35 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
}
/**
* {@inheritdoc}
*/
public function open($savePath, $sessionName)
{
return true;
}
/**
* {@inheritdoc}
* @return bool
*/
public function close()
{
return true;
return $this->memcached->quit();
}
/**
* {@inheritdoc}
*/
public function read($sessionId)
protected function doRead($sessionId)
{
return $this->memcached->get($this->prefix.$sessionId) ?: '';
}
/**
* @return bool
*/
public function updateTimestamp($sessionId, $data)
{
$this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl);
return true;
}
/**
* {@inheritdoc}
*/
public function write($sessionId, $data)
protected function doWrite($sessionId, $data)
{
return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
}
@@ -92,7 +91,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function destroy($sessionId)
protected function doDestroy($sessionId)
{
$result = $this->memcached->delete($this->prefix.$sessionId);
@@ -100,7 +99,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
}
/**
* {@inheritdoc}
* @return bool
*/
public function gc($maxlifetime)
{
@@ -12,9 +12,14 @@
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
/**
* Session handler using the mongodb/mongodb package and MongoDB driver extension.
*
* @author Markus Bachmann <markus.bachmann@bachi.biz>
*
* @see https://packagist.org/packages/mongodb/mongodb
* @see https://php.net/mongodb
*/
class MongoDbSessionHandler implements \SessionHandlerInterface
class MongoDbSessionHandler extends AbstractSessionHandler
{
private $mongo;
@@ -37,7 +42,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
* * id_field: The field name for storing the session id [default: _id]
* * data_field: The field name for storing the session data [default: data]
* * time_field: The field name for storing the timestamp [default: time]
* * expiry_field: The field name for storing the expiry-timestamp [default: expires_at]
* * expiry_field: The field name for storing the expiry-timestamp [default: expires_at].
*
* It is strongly recommended to put an index on the `expiry_field` for
* garbage-collection. Alternatively it's possible to automatically expire
@@ -51,43 +56,39 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
* { "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 \Mongo|\MongoClient|\MongoDB\Client $mongo A MongoDB\Client, MongoClient or Mongo instance
* @param array $options An associative array of field options
* @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)
{
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');
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');
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);
}
/**
* {@inheritdoc}
*/
public function open($savePath, $sessionName)
{
return true;
], $options);
}
/**
@@ -101,13 +102,13 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function destroy($sessionId)
protected function doDestroy($sessionId)
{
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
$this->getCollection()->$methodName(array(
$this->getCollection()->$methodName([
$this->options['id_field'] => $sessionId,
));
]);
return true;
}
@@ -119,9 +120,9 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
{
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove';
$this->getCollection()->$methodName(array(
$this->options['expiry_field'] => array('$lt' => $this->createDateTime()),
));
$this->getCollection()->$methodName([
$this->options['expiry_field'] => ['$lt' => $this->createDateTime()],
]);
return true;
}
@@ -129,16 +130,16 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function write($sessionId, $data)
protected function doWrite($sessionId, $data)
{
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
$fields = array(
$fields = [
$this->options['time_field'] => $this->createDateTime(),
$this->options['expiry_field'] => $expiry,
);
];
$options = array('upsert' => true);
$options = ['upsert' => true];
if ($this->mongo instanceof \MongoDB\Client) {
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
@@ -150,8 +151,8 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
$methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update';
$this->getCollection()->$methodName(
array($this->options['id_field'] => $sessionId),
array('$set' => $fields),
[$this->options['id_field'] => $sessionId],
['$set' => $fields],
$options
);
@@ -161,12 +162,39 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function read($sessionId)
public function updateTimestamp($sessionId, $data)
{
$dbData = $this->getCollection()->findOne(array(
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
if ($this->mongo instanceof \MongoDB\Client) {
$methodName = 'updateOne';
$options = [];
} else {
$methodName = 'update';
$options = ['multiple' => false];
}
$this->getCollection()->$methodName(
[$this->options['id_field'] => $sessionId],
['$set' => [
$this->options['time_field'] => $this->createDateTime(),
$this->options['expiry_field'] => $expiry,
]],
$options
);
return true;
}
/**
* {@inheritdoc}
*/
protected function doRead($sessionId)
{
$dbData = $this->getCollection()->findOne([
$this->options['id_field'] => $sessionId,
$this->options['expiry_field'] => array('$gte' => $this->createDateTime()),
));
$this->options['expiry_field'] => ['$gte' => $this->createDateTime()],
]);
if (null === $dbData) {
return '';
@@ -23,9 +23,10 @@ class NativeFileSessionHandler extends NativeSessionHandler
* 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)
{
@@ -37,7 +38,7 @@ class NativeFileSessionHandler extends NativeSessionHandler
if ($count = substr_count($savePath, ';')) {
if ($count > 2) {
throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath));
throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'.', $savePath));
}
// characters after last ';' are the path
@@ -45,7 +46,7 @@ class NativeFileSessionHandler extends NativeSessionHandler
}
if ($baseDir && !is_dir($baseDir) && !@mkdir($baseDir, 0777, true) && !is_dir($baseDir)) {
throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $baseDir));
throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s".', $baseDir));
}
ini_set('session.save_path', $savePath);
@@ -11,14 +11,14 @@
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
// Adds SessionHandler functionality if available.
// @see http://php.net/sessionhandler
if (\PHP_VERSION_ID >= 50400) {
class NativeSessionHandler extends \SessionHandler
{
}
} else {
class NativeSessionHandler
/**
* @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead.
* @see https://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);
}
}
@@ -12,22 +12,12 @@
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
/**
* NullSessionHandler.
*
* Can be used in unit testing or in a situations where persisted sessions are not desired.
*
* @author Drak <drak@zikula.org>
*/
class NullSessionHandler implements \SessionHandlerInterface
class NullSessionHandler extends AbstractSessionHandler
{
/**
* {@inheritdoc}
*/
public function open($savePath, $sessionName)
{
return true;
}
/**
* {@inheritdoc}
*/
@@ -39,7 +29,15 @@ class NullSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function read($sessionId)
public function validateId($sessionId)
{
return true;
}
/**
* {@inheritdoc}
*/
protected function doRead($sessionId)
{
return '';
}
@@ -47,7 +45,7 @@ class NullSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function write($sessionId, $data)
public function updateTimestamp($sessionId, $data)
{
return true;
}
@@ -55,7 +53,7 @@ class NullSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function destroy($sessionId)
protected function doWrite($sessionId, $data)
{
return true;
}
@@ -63,6 +61,14 @@ class NullSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
protected function doDestroy($sessionId)
{
return true;
}
/**
* @return bool
*/
public function gc($maxlifetime)
{
return true;
@@ -32,13 +32,13 @@ 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>
* @author Tobias Schultze <http://tobion.de>
*/
class PdoSessionHandler implements \SessionHandlerInterface
class PdoSessionHandler extends AbstractSessionHandler
{
/**
* No locking is done. This means sessions are prone to loss of data due to
@@ -118,7 +118,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
/**
* @var array Connection options when lazy-connect
*/
private $connectionOptions = array();
private $connectionOptions = [];
/**
* @var int The strategy for locking, see constants
@@ -130,7 +130,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
*
* @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,23 +161,25 @@ class PdoSessionHandler implements \SessionHandlerInterface
* * 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 null
* @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)) {
throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __CLASS__));
}
$this->pdo = $pdoOrDsn;
$this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
} elseif (\is_string($pdoOrDsn) && false !== strpos($pdoOrDsn, '://')) {
$this->dsn = $this->buildDsnFromUrl($pdoOrDsn);
} else {
$this->dsn = $pdoOrDsn;
}
@@ -216,7 +218,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
// - 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)";
@@ -260,11 +262,13 @@ class PdoSessionHandler implements \SessionHandlerInterface
*/
public function open($savePath, $sessionName)
{
$this->sessionExpired = false;
if (null === $this->pdo) {
$this->connect($this->dsn ?: $savePath);
}
return true;
return parent::open($savePath, $sessionName);
}
/**
@@ -273,7 +277,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
public function read($sessionId)
{
try {
return $this->doRead($sessionId);
return parent::read($sessionId);
} catch (\PDOException $e) {
$this->rollback();
@@ -282,7 +286,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
}
/**
* {@inheritdoc}
* @return bool
*/
public function gc($maxlifetime)
{
@@ -296,7 +300,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function destroy($sessionId)
protected function doDestroy($sessionId)
{
// delete the record associated with this id
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
@@ -317,7 +321,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
public function write($sessionId, $data)
protected function doWrite($sessionId, $data)
{
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
@@ -360,6 +364,30 @@ class PdoSessionHandler implements \SessionHandlerInterface
return true;
}
/**
* {@inheritdoc}
*/
public function updateTimestamp($sessionId, $data)
{
$maxlifetime = (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"
);
$updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$updateStmt->execute();
} catch (\PDOException $e) {
$this->rollback();
throw $e;
}
return true;
}
/**
* {@inheritdoc}
*/
@@ -405,6 +433,102 @@ class PdoSessionHandler implements \SessionHandlerInterface
$this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
}
/**
* 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)
{
// (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
$url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl);
$params = parse_url($url);
if (false === $params) {
return $dsnOrUrl; // If the URL is not valid, let's assume it might be a DSN already.
}
$params = array_map('rawurldecode', $params);
// Override the default username and password. Values passed through options will still win over these in the constructor.
if (isset($params['user'])) {
$this->username = $params['user'];
}
if (isset($params['pass'])) {
$this->password = $params['pass'];
}
if (!isset($params['scheme'])) {
throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler.');
}
$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'];
// Doctrine DBAL supports passing its internal pdo_* driver names directly too (allowing both dashes and underscores). This allows supporting the same here.
if (0 === strpos($driver, 'pdo_') || 0 === strpos($driver, 'pdo-')) {
$driver = substr($driver, 4);
}
switch ($driver) {
case 'mysql':
case 'pgsql':
$dsn = $driver.':';
if (isset($params['host']) && '' !== $params['host']) {
$dsn .= 'host='.$params['host'].';';
}
if (isset($params['port']) && '' !== $params['port']) {
$dsn .= 'port='.$params['port'].';';
}
if (isset($params['path'])) {
$dbName = substr($params['path'], 1); // Remove the leading slash
$dsn .= 'dbname='.$dbName.';';
}
return $dsn;
case 'sqlite':
return 'sqlite:'.substr($params['path'], 1);
case 'sqlsrv':
$dsn = 'sqlsrv:server=';
if (isset($params['host'])) {
$dsn .= $params['host'];
}
if (isset($params['port']) && '' !== $params['port']) {
$dsn .= ','.$params['port'];
}
if (isset($params['path'])) {
$dbName = substr($params['path'], 1); // Remove the leading slash
$dsn .= ';Database='.$dbName;
}
return $dsn;
default:
throw new \InvalidArgumentException(sprintf('The scheme "%s" is not supported by the PdoSessionHandler URL configuration. Pass a PDO DSN directly.', $params['scheme']));
}
}
/**
* Helper method to begin a transaction.
*
@@ -414,7 +538,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
* 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()
@@ -483,10 +607,8 @@ class PdoSessionHandler implements \SessionHandlerInterface
*
* @return string The session data
*/
private function doRead($sessionId)
protected function doRead($sessionId)
{
$this->sessionExpired = false;
if (self::LOCK_ADVISORY === $this->lockMode) {
$this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
}
@@ -515,7 +637,9 @@ class PdoSessionHandler implements \SessionHandlerInterface
throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.');
}
if (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
// until other connections to the session are committed.
try {
@@ -559,7 +683,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
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)');
@@ -740,7 +864,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
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 = ?;";
@@ -753,7 +877,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
"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;
}
@@ -15,6 +15,8 @@ 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
{
@@ -27,6 +29,8 @@ class WriteCheckSessionHandler implements \SessionHandlerInterface
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;
}
@@ -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.
@@ -50,7 +50,7 @@ class MockArraySessionStorage implements SessionStorageInterface
/**
* @var array
*/
protected $data = array();
protected $data = [];
/**
* @var MetadataBag
@@ -60,7 +60,7 @@ class MockArraySessionStorage implements SessionStorageInterface
/**
* @var array|SessionBagInterface[]
*/
protected $bags = array();
protected $bags = [];
/**
* @param string $name Session name
@@ -152,7 +152,7 @@ class MockArraySessionStorage implements SessionStorageInterface
public function save()
{
if (!$this->started || $this->closed) {
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed.');
}
// nothing to do since we don't persist the session data
$this->closed = false;
@@ -170,7 +170,7 @@ class MockArraySessionStorage implements SessionStorageInterface
}
// clear out the session
$this->data = array();
$this->data = [];
// reconnect the bags to the session
$this->loadSession();
@@ -190,7 +190,7 @@ class MockArraySessionStorage implements SessionStorageInterface
public function getBag($name)
{
if (!isset($this->bags[$name])) {
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
throw new \InvalidArgumentException(sprintf('The SessionBagInterface "%s" is not registered.', $name));
}
if (!$this->started) {
@@ -242,11 +242,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]);
}
@@ -38,7 +38,7 @@ class MockFileSessionStorage extends MockArraySessionStorage
}
if (!is_dir($savePath) && !@mkdir($savePath, 0777, true) && !is_dir($savePath)) {
throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $savePath));
throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s".', $savePath));
}
$this->savePath = $savePath;
@@ -88,10 +88,29 @@ class MockFileSessionStorage extends MockArraySessionStorage
public function save()
{
if (!$this->started) {
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed.');
}
file_put_contents($this->getFilePath(), serialize($this->data));
$data = $this->data;
foreach ($this->bags as $bag) {
if (empty($data[$key = $bag->getStorageKey()])) {
unset($data[$key]);
}
}
if ([$key = $this->metadataBag->getStorageKey()] === array_keys($data)) {
unset($data[$key]);
}
try {
if ($data) {
file_put_contents($this->getFilePath(), serialize($data));
} else {
$this->destroy();
}
} finally {
$this->data = $data;
}
// this is needed for Silex, where the session object is re-used across requests
// in functional tests. In Symfony, the container is rebooted, so we don't have
@@ -126,7 +145,7 @@ class MockFileSessionStorage extends MockArraySessionStorage
private function read()
{
$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();
}
@@ -12,9 +12,9 @@
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\SessionUtils;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
/**
@@ -25,11 +25,9 @@ use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
class NativeSessionStorage implements SessionStorageInterface
{
/**
* Array of SessionBagInterface.
*
* @var SessionBagInterface[]
*/
protected $bags;
protected $bags = [];
/**
* @var bool
@@ -42,7 +40,7 @@ class NativeSessionStorage implements SessionStorageInterface
protected $closed = false;
/**
* @var AbstractProxy
* @var AbstractProxy|\SessionHandlerInterface
*/
protected $saveHandler;
@@ -51,24 +49,31 @@ 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
* PHP starts to execute user-land code. Setting during runtime has no effect).
*
* cache_limiter, "" (use "0" to prevent headers from being sent entirely).
* cache_expire, "0"
* cookie_domain, ""
* cookie_httponly, ""
* cookie_lifetime, "0"
* cookie_path, "/"
* cookie_secure, ""
* cookie_samesite, null
* entropy_file, ""
* entropy_length, "0"
* gc_divisor, "100"
@@ -96,24 +101,23 @@ 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 AbstractProxy|NativeSessionHandler|\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(
// disable by default because it's managed by HeaderBag (if used)
'cache_limiter' => '',
'use_cookies' => 1,
);
if (\PHP_VERSION_ID >= 50400) {
session_register_shutdown();
} else {
register_shutdown_function('session_write_close');
if (!\extension_loaded('session')) {
throw new \LogicException('PHP extension "session" is required.');
}
$options += [
'cache_limiter' => '',
'cache_expire' => 0,
'use_cookies' => 1,
'lazy_write' => 1,
];
session_register_shutdown();
$this->setMetadataBag($metaBag);
$this->setOptions($options);
$this->setSaveHandler($handler);
@@ -122,7 +126,7 @@ class NativeSessionStorage implements SessionStorageInterface
/**
* Gets the save handler instance.
*
* @return AbstractProxy
* @return AbstractProxy|\SessionHandlerInterface
*/
public function getSaveHandler()
{
@@ -138,29 +142,27 @@ class NativeSessionStorage implements SessionStorageInterface
return true;
}
if (\PHP_VERSION_ID >= 50400 && \PHP_SESSION_ACTIVE === session_status()) {
if (\PHP_SESSION_ACTIVE === session_status()) {
throw new \RuntimeException('Failed to start the session: already started by PHP.');
}
if (\PHP_VERSION_ID < 50400 && !$this->closed && isset($_SESSION) && session_id()) {
// not 100% fool-proof, but is the most reliable way to determine if a session is active in PHP 5.3
throw new \RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).');
}
if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
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));
}
// ok to try and start the session
if (!session_start()) {
throw new \RuntimeException('Failed to start the session');
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();
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
// This condition matches only PHP 5.3 with internal save handlers
$this->saveHandler->setActive(true);
}
return true;
}
@@ -203,12 +205,7 @@ class NativeSessionStorage implements SessionStorageInterface
public function regenerate($destroy = false, $lifetime = null)
{
// Cannot regenerate the session ID for non-active sessions.
if (\PHP_VERSION_ID >= 50400 && \PHP_SESSION_ACTIVE !== session_status()) {
return false;
}
// Check if session ID exists in PHP 5.3
if (\PHP_VERSION_ID < 50400 && '' === session_id()) {
if (\PHP_SESSION_ACTIVE !== session_status()) {
return false;
}
@@ -216,8 +213,10 @@ class NativeSessionStorage implements SessionStorageInterface
return false;
}
if (null !== $lifetime) {
if (null !== $lifetime && $lifetime != ini_get('session.cookie_lifetime')) {
$this->save();
ini_set('session.cookie_lifetime', $lifetime);
$this->start();
}
if ($destroy) {
@@ -226,9 +225,12 @@ 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
$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;
}
@@ -238,11 +240,37 @@ class NativeSessionStorage implements SessionStorageInterface
*/
public function save()
{
session_write_close();
// Store a copy so we can restore the bags in case the session was not left empty
$session = $_SESSION;
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
// This condition matches only PHP 5.3 with internal save handlers
$this->saveHandler->setActive(false);
foreach ($this->bags as $bag) {
if (empty($_SESSION[$key = $bag->getStorageKey()])) {
unset($_SESSION[$key]);
}
}
if ([$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) {
unset($_SESSION[$key]);
}
// 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));
}
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;
@@ -260,7 +288,7 @@ class NativeSessionStorage implements SessionStorageInterface
}
// clear out the session
$_SESSION = array();
$_SESSION = [];
// reconnect the bags to the session
$this->loadSession();
@@ -284,7 +312,7 @@ class NativeSessionStorage implements SessionStorageInterface
public function getBag($name)
{
if (!isset($this->bags[$name])) {
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
throw new \InvalidArgumentException(sprintf('The SessionBagInterface "%s" is not registered.', $name));
}
if (!$this->started && $this->saveHandler->isActive()) {
@@ -329,19 +357,19 @@ 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)
{
if (headers_sent() || (\PHP_VERSION_ID >= 50400 && \PHP_SESSION_ACTIVE === session_status())) {
if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
return;
}
$validOptions = array_flip(array(
$validOptions = array_flip([
'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
'cookie_lifetime', 'cookie_path', 'cookie_secure',
'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite',
'entropy_file', 'entropy_length', 'gc_divisor',
'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
'hash_function', 'lazy_write', 'name', 'referer_check',
@@ -350,10 +378,16 @@ class NativeSessionStorage implements SessionStorageInterface
'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);
}
}
@@ -368,54 +402,39 @@ class NativeSessionStorage implements SessionStorageInterface
* ini_set('session.save_handler', 'files');
* ini_set('session.save_path', '/tmp');
*
* or pass in a NativeSessionHandler instance which configures session.save_handler in the
* constructor, for a template see NativeFileSessionHandler or use handlers in
* composer package drak/native-session
* or pass in a \SessionHandler instance which configures session.save_handler in the
* 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 AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $saveHandler
* @param AbstractProxy|\SessionHandlerInterface|null $saveHandler
*
* @throws \InvalidArgumentException
*/
public function setSaveHandler($saveHandler = null)
{
if (!$saveHandler instanceof AbstractProxy &&
!$saveHandler instanceof NativeSessionHandler &&
!$saveHandler instanceof \SessionHandlerInterface &&
null !== $saveHandler) {
throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \SessionHandlerInterface; or be null.');
throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
}
// Wrap $saveHandler in proxy and prevent double wrapping of proxy
if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
$saveHandler = new SessionHandlerProxy($saveHandler);
} elseif (!$saveHandler instanceof AbstractProxy) {
$saveHandler = \PHP_VERSION_ID >= 50400 ?
new SessionHandlerProxy(new \SessionHandler()) : new NativeProxy();
$saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()));
}
$this->saveHandler = $saveHandler;
if (headers_sent() || (\PHP_VERSION_ID >= 50400 && \PHP_SESSION_ACTIVE === session_status())) {
if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
return;
}
if ($this->saveHandler instanceof \SessionHandlerInterface) {
if (\PHP_VERSION_ID >= 50400) {
session_set_save_handler($this->saveHandler, false);
} else {
session_set_save_handler(
array($this->saveHandler, 'open'),
array($this->saveHandler, 'close'),
array($this->saveHandler, 'read'),
array($this->saveHandler, 'write'),
array($this->saveHandler, 'destroy'),
array($this->saveHandler, 'gc')
);
}
if ($this->saveHandler instanceof SessionHandlerProxy) {
session_set_save_handler($this->saveHandler, false);
}
}
@@ -433,11 +452,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]);
}
@@ -11,9 +11,6 @@
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
/**
* Allows session to be started by PHP and managed by Symfony.
*
@@ -22,11 +19,15 @@ use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
class PhpBridgeSessionStorage extends NativeSessionStorage
{
/**
* @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler
* @param MetadataBag $metaBag MetadataBag
* @param \SessionHandlerInterface|null $handler
* @param MetadataBag $metaBag MetadataBag
*/
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);
}
@@ -41,10 +42,6 @@ class PhpBridgeSessionStorage extends NativeSessionStorage
}
$this->loadSession();
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
// This condition matches only PHP 5.3 + internal save handlers
$this->saveHandler->setActive(true);
}
return true;
}
@@ -12,8 +12,6 @@
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
/**
* AbstractProxy.
*
* @author Drak <drak@zikula.org>
*/
abstract class AbstractProxy
@@ -25,11 +23,6 @@ abstract class AbstractProxy
*/
protected $wrapper = false;
/**
* @var bool
*/
protected $active = false;
/**
* @var string
*/
@@ -38,7 +31,7 @@ abstract class AbstractProxy
/**
* Gets the session.save_handler name.
*
* @return string
* @return string|null
*/
public function getSaveHandlerName()
{
@@ -72,32 +65,7 @@ abstract class AbstractProxy
*/
public function isActive()
{
if (\PHP_VERSION_ID >= 50400) {
return $this->active = \PHP_SESSION_ACTIVE === session_status();
}
return $this->active;
}
/**
* Sets the active flag.
*
* Has no effect under PHP 5.4+ as status is detected
* automatically in isActive()
*
* @internal
*
* @param bool $flag
*
* @throws \LogicException
*/
public function setActive($flag)
{
if (\PHP_VERSION_ID >= 50400) {
throw new \LogicException('This method is disabled in PHP 5.4.0+');
}
$this->active = (bool) $flag;
return \PHP_SESSION_ACTIVE === session_status();
}
/**
@@ -120,7 +88,7 @@ abstract class AbstractProxy
public function setId($id)
{
if ($this->isActive()) {
throw new \LogicException('Cannot change the ID of an active session');
throw new \LogicException('Cannot change the ID of an active session.');
}
session_id($id);
@@ -146,7 +114,7 @@ abstract class AbstractProxy
public function setName($name)
{
if ($this->isActive()) {
throw new \LogicException('Cannot change the name of an active session');
throw new \LogicException('Cannot change the name of an active session.');
}
session_name($name);
@@ -11,9 +11,13 @@
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
@@ -14,13 +14,10 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
/**
* @author Drak <drak@zikula.org>
*/
class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface
class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
{
protected $handler;
/**
* @param \SessionHandlerInterface $handler
*/
public function __construct(\SessionHandlerInterface $handler)
{
$this->handler = $handler;
@@ -28,6 +25,14 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
$this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
}
/**
* @return \SessionHandlerInterface
*/
public function getHandler()
{
return $this->handler;
}
// \SessionHandlerInterface
/**
@@ -35,13 +40,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
*/
public function open($savePath, $sessionName)
{
$return = (bool) $this->handler->open($savePath, $sessionName);
if (true === $return) {
$this->active = true;
}
return $return;
return (bool) $this->handler->open($savePath, $sessionName);
}
/**
@@ -49,8 +48,6 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
*/
public function close()
{
$this->active = false;
return (bool) $this->handler->close();
}
@@ -79,10 +76,26 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
}
/**
* {@inheritdoc}
* @return bool
*/
public function gc($maxlifetime)
{
return (bool) $this->handler->gc($maxlifetime);
}
/**
* {@inheritdoc}
*/
public function validateId($sessionId)
{
return !$this->handler instanceof \SessionUpdateTimestampHandlerInterface || $this->handler->validateId($sessionId);
}
/**
* {@inheritdoc}
*/
public function updateTimestamp($sessionId, $data)
{
return $this->handler instanceof \SessionUpdateTimestampHandlerInterface ? $this->handler->updateTimestamp($sessionId, $data) : $this->write($sessionId, $data);
}
}
@@ -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.
+14 -9
View File
@@ -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($callback = null, $status = 200, $headers = array())
public function __construct(callable $callback = null, $status = 200, $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);
}
@@ -65,20 +65,21 @@ class StreamedResponse extends Response
*
* @param callable $callback A valid PHP callback
*
* @throws \LogicException
* @return $this
*/
public function setCallback($callback)
public function setCallback(callable $callback)
{
if (!\is_callable($callback)) {
throw new \LogicException('The Response callback must be a valid PHP callable.');
}
$this->callback = $callback;
return $this;
}
/**
* {@inheritdoc}
*
* This method only sends the headers once.
*
* @return $this
*/
public function sendHeaders()
{
@@ -95,6 +96,8 @@ class StreamedResponse extends Response
* {@inheritdoc}
*
* This method only sends the content once.
*
* @return $this
*/
public function sendContent()
{
@@ -117,6 +120,8 @@ class StreamedResponse extends Response
* {@inheritdoc}
*
* @throws \LogicException when the content is not null
*
* @return $this
*/
public function setContent($content)
{
@@ -125,12 +130,12 @@ class StreamedResponse extends Response
}
$this->streamed = true;
return $this;
}
/**
* {@inheritdoc}
*
* @return false
*/
public function getContent()
{
+27 -27
View File
@@ -28,24 +28,24 @@ class AcceptHeaderItemTest extends TestCase
public function provideFromStringData()
{
return array(
array(
return [
[
'text/html',
'text/html', array(),
),
array(
'text/html', [],
],
[
'"this;should,not=matter"',
'this;should,not=matter', array(),
),
array(
'this;should,not=matter', [],
],
[
"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(
'text/plain', ['charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'],
],
[
'"this;should,not=matter";charset=utf-8',
'this;should,not=matter', array('charset' => 'utf-8'),
),
);
'this;should,not=matter', ['charset' => 'utf-8'],
],
];
}
/**
@@ -59,21 +59,21 @@ class AcceptHeaderItemTest extends TestCase
public function provideToStringData()
{
return array(
array(
'text/html', array(),
return [
[
'text/html', [],
'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'],
'text/plain;charset=utf-8;param="this;should,not=matter";footnotes=true',
),
);
],
];
}
public function testValue()
{
$item = new AcceptHeaderItem('value', array());
$item = new AcceptHeaderItem('value', []);
$this->assertEquals('value', $item->getValue());
$item->setValue('new value');
@@ -85,7 +85,7 @@ class AcceptHeaderItemTest extends TestCase
public function testQuality()
{
$item = new AcceptHeaderItem('value', array());
$item = new AcceptHeaderItem('value', []);
$this->assertEquals(1.0, $item->getQuality());
$item->setQuality(0.5);
@@ -98,14 +98,14 @@ class AcceptHeaderItemTest extends TestCase
public function testAttribute()
{
$item = new AcceptHeaderItem('value', array());
$this->assertEquals(array(), $item->getAttributes());
$item = new AcceptHeaderItem('value', []);
$this->assertEquals([], $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->assertEquals(['test' => 'value'], $item->getAttributes());
$this->assertTrue($item->hasAttribute('test'));
$this->assertEquals('value', $item->getAttribute('test'));
$this->assertEquals('value', $item->getAttribute('test', 'default'));
+21 -21
View File
@@ -39,13 +39,13 @@ class AcceptHeaderTest extends TestCase
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'))),
);
return [
['', []],
['gzip', [new AcceptHeaderItem('gzip')]],
['gzip,deflate,sdch', [new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')]],
["gzip, deflate\t,sdch", [new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')]],
['"this;should,not=matter"', [new AcceptHeaderItem('this;should,not=matter')]],
];
}
/**
@@ -59,12 +59,12 @@ class AcceptHeaderTest extends TestCase
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'),
);
return [
[[], ''],
[[new AcceptHeaderItem('gzip')], 'gzip'],
[[new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')], 'gzip,deflate,sdch'],
[[new AcceptHeaderItem('this;should,not=matter')], 'this;should,not=matter'],
];
}
/**
@@ -78,9 +78,9 @@ class AcceptHeaderTest extends TestCase
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')),
);
return [
['fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', ['fr-FR', 'fr']],
];
}
/**
@@ -94,10 +94,10 @@ class AcceptHeaderTest extends TestCase
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', '*')),
);
return [
'quality has priority' => ['*;q=0.3,ISO-8859-1,utf-8;q=0.7', ['ISO-8859-1', 'utf-8', '*']],
'order matters when q is equal' => ['*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', ['ISO-8859-1', 'utf-8', '*']],
'order matters when q is equal2' => ['*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', ['utf-8', 'ISO-8859-1', '*']],
];
}
}
+26 -26
View File
@@ -31,63 +31,63 @@ class ApacheRequestTest extends TestCase
public function provideServerVars()
{
return array(
array(
array(
return [
[
[
'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',
),
],
'/',
'',
'/',
),
);
],
];
}
}
@@ -12,6 +12,7 @@
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;
@@ -21,14 +22,14 @@ class BinaryFileResponseTest extends ResponseTestCase
public function testConstruction()
{
$file = __DIR__.'/../README.md';
$response = new BinaryFileResponse($file, 404, array('X-Header' => 'Foo'), true, null, true, true);
$response = new BinaryFileResponse($file, 404, ['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);
$response = BinaryFileResponse::create($file, 404, [], 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'));
@@ -38,18 +39,16 @@ class BinaryFileResponseTest extends ResponseTestCase
{
touch(sys_get_temp_dir().'/fööö.html');
$response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, array(), true, 'attachment');
$response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, [], true, 'attachment');
@unlink(sys_get_temp_dir().'/fööö.html');
$this->assertSame('fööö.html', $response->getFile()->getFilename());
}
/**
* @expectedException \LogicException
*/
public function testSetContent()
{
$this->expectException('LogicException');
$response = new BinaryFileResponse(__FILE__);
$response->setContent('foo');
}
@@ -84,7 +83,7 @@ class BinaryFileResponseTest extends ResponseTestCase
*/
public function testRequests($requestRange, $offset, $length, $responseRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'])->setAutoEtag();
// do a request to get the ETag
$request = Request::create('/');
@@ -108,6 +107,7 @@ class BinaryFileResponseTest extends ResponseTestCase
$this->assertEquals(206, $response->getStatusCode());
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
$this->assertSame((string) $length, $response->headers->get('Content-Length'));
}
/**
@@ -115,7 +115,7 @@ class BinaryFileResponseTest extends ResponseTestCase
*/
public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
// do a request to get the LastModified
$request = Request::create('/');
@@ -143,19 +143,20 @@ class BinaryFileResponseTest extends ResponseTestCase
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'),
);
return [
['bytes=1-4', 1, 4, 'bytes 1-4/35'],
['bytes=-5', 30, 5, 'bytes 30-34/35'],
['bytes=30-', 30, 5, 'bytes 30-34/35'],
['bytes=30-30', 30, 1, 'bytes 30-30/35'],
['bytes=30-34', 30, 5, 'bytes 30-34/35'],
['bytes=30-40', 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);
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'], true, null, false, false);
// prepare a request for a range of the testing file
$request = Request::create('/');
@@ -176,7 +177,7 @@ class BinaryFileResponseTest extends ResponseTestCase
*/
public function testFullFileRequests($requestRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'])->setAutoEtag();
// prepare a request for a range of the testing file
$request = Request::create('/');
@@ -196,14 +197,36 @@ class BinaryFileResponseTest extends ResponseTestCase
public function provideFullFileRanges()
{
return array(
array('bytes=0-'),
array('bytes=0-34'),
array('bytes=-35'),
return [
['bytes=0-'],
['bytes=0-34'],
['bytes=-35'],
// Syntactical invalid range-request should also return the full resource
array('bytes=20-10'),
array('bytes=50-40'),
);
['bytes=20-10'],
['bytes=50-40'],
// range units other than bytes must be ignored
['unknown=10-20'],
];
}
public function testRangeOnPostMethod()
{
$request = Request::create('/', 'POST');
$request->headers->set('Range', 'bytes=10-20');
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
$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->assertSame(200, $response->getStatusCode());
$this->assertSame('35', $response->headers->get('Content-Length'));
$this->assertNull($response->headers->get('Content-Range'));
}
public function testUnpreparedResponseSendsFullFile()
@@ -224,7 +247,7 @@ class BinaryFileResponseTest extends ResponseTestCase
*/
public function testInvalidRequests($requestRange)
{
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'])->setAutoEtag();
// prepare a request for a range of the testing file
$request = Request::create('/');
@@ -240,10 +263,10 @@ class BinaryFileResponseTest extends ResponseTestCase
public function provideInvalidRanges()
{
return array(
array('bytes=-40'),
array('bytes=30-40'),
);
return [
['bytes=-40'],
['bytes=40-50'],
];
}
/**
@@ -255,21 +278,21 @@ class BinaryFileResponseTest extends ResponseTestCase
$request->headers->set('X-Sendfile-Type', 'X-Sendfile');
BinaryFileResponse::trustXSendfileTypeHeader();
$response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream'));
$response = BinaryFileResponse::create($file, 200, ['Content-Type' => 'application/octet-stream']);
$response->prepare($request);
$this->expectOutputString('');
$response->sendContent();
$this->assertContains('README.md', $response->headers->get('X-Sendfile'));
$this->assertStringContainsString('README.md', $response->headers->get('X-Sendfile'));
}
public function provideXSendfileFiles()
{
return array(
array(__DIR__.'/../README.md'),
array('file://'.__DIR__.'/../README.md'),
);
return [
[__DIR__.'/../README.md'],
['file://'.__DIR__.'/../README.md'],
];
}
/**
@@ -284,7 +307,7 @@ class BinaryFileResponseTest extends ResponseTestCase
$file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test');
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream'));
$response = new BinaryFileResponse($file, 200, ['Content-Type' => 'application/octet-stream']);
$reflection = new \ReflectionObject($response);
$property = $reflection->getProperty('file');
$property->setAccessible(true);
@@ -303,19 +326,19 @@ class BinaryFileResponseTest extends ResponseTestCase
$realPath = realpath($path);
$this->assertFileExists($realPath);
$response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream'));
$response = new BinaryFileResponse($realPath, 200, ['Content-Type' => 'application/octet-stream']);
$response->deleteFileAfterSend(true);
$response->prepare($request);
$response->sendContent();
$this->assertFileNotExists($path);
$this->assertFileDoesNotExist($path);
}
public function testAcceptRangeOnUnsafeMethods()
{
$request = Request::create('/', 'POST');
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
$response->prepare($request);
$this->assertEquals('none', $response->headers->get('Accept-Ranges'));
@@ -324,7 +347,7 @@ class BinaryFileResponseTest extends ResponseTestCase
public function testAcceptRangeNotOverriden()
{
$request = Request::create('/', 'POST');
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream']);
$response->headers->set('Accept-Ranges', 'foo');
$response->prepare($request);
@@ -333,15 +356,25 @@ class BinaryFileResponseTest extends ResponseTestCase
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'),
);
return [
['/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'],
['/home/foo/bar.txt', '/var/www/=/files/,/home/foo/=/baz/', '/baz/bar.txt'],
['/tmp/bar.txt', '"/var/www/"="/files/", "/home/Foo/"="/baz/"', null],
];
}
public function testStream()
{
$request = Request::create('/');
$response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, ['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'));
return new BinaryFileResponse(__DIR__.'/../README.md', 200, ['Content-Type' => 'application/octet-stream']);
}
public static function tearDownAfterClass()
+89 -30
View File
@@ -24,35 +24,46 @@ use Symfony\Component\HttpFoundation\Cookie;
*/
class CookieTest extends TestCase
{
public function invalidNames()
public function namesWithSpecialCharacters()
{
return array(
array(''),
array(',MyName'),
array(';MyName'),
array(' MyName'),
array("\tMyName"),
array("\rMyName"),
array("\nMyName"),
array("\013MyName"),
array("\014MyName"),
);
return [
[',MyName'],
[';MyName'],
[' MyName'],
["\tMyName"],
["\rMyName"],
["\nMyName"],
["\013MyName"],
["\014MyName"],
];
}
/**
* @dataProvider invalidNames
* @expectedException \InvalidArgumentException
* @dataProvider namesWithSpecialCharacters
*/
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
public function testInstantiationThrowsExceptionIfRawCookieNameContainsSpecialCharacters($name)
{
new Cookie($name);
$this->expectException('InvalidArgumentException');
new Cookie($name, null, 0, null, null, null, false, true);
}
/**
* @expectedException \InvalidArgumentException
* @dataProvider namesWithSpecialCharacters
*/
public function testInstantiationSucceedNonRawCookieNameContainsSpecialCharacters($name)
{
$this->assertInstanceOf(Cookie::class, new Cookie($name));
}
public function testInstantiationThrowsExceptionIfCookieNameIsEmpty()
{
$this->expectException('InvalidArgumentException');
new Cookie('');
}
public function testInvalidExpiration()
{
$this->expectException('InvalidArgumentException');
new Cookie('MyCookie', 'foo', 'bar');
}
@@ -80,9 +91,13 @@ class CookieTest extends TestCase
public function testGetExpiresTime()
{
$cookie = new Cookie('foo', 'bar', 3600);
$cookie = new Cookie('foo', 'bar');
$this->assertEquals(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
$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()
@@ -100,9 +115,6 @@ class CookieTest extends TestCase
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
/**
* @requires PHP 5.5
*/
public function testConstructorWithDateTimeImmutable()
{
$expire = new \DateTimeImmutable();
@@ -117,26 +129,26 @@ class CookieTest extends TestCase
$cookie = new Cookie('foo', 'bar', $value);
$expire = strtotime($value);
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1);
$this->assertEqualsWithDelta($expire, $cookie->getExpiresTime(), 1, '->getExpiresTime() returns the expire date');
}
public function testGetDomain()
{
$cookie = new Cookie('foo', 'bar', 3600, '/', '.myfoodomain.com');
$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', 3600, '/', '.myfoodomain.com', true);
$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', 3600, '/', '.myfoodomain.com', false, true);
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
}
@@ -169,16 +181,63 @@ class CookieTest extends TestCase
public function testToString()
{
$cookie = new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
$this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
$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; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
$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', time() - 31536001).'; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
$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());
}
}
@@ -18,11 +18,9 @@ use Symfony\Component\HttpFoundation\Request;
class ExpressionRequestMatcherTest extends TestCase
{
/**
* @expectedException \LogicException
*/
public function testWhenNoExpressionIsSet()
{
$this->expectException('LogicException');
$expressionRequestMatcher = new ExpressionRequestMatcher();
$expressionRequestMatcher->matches(new Request());
}
@@ -55,15 +53,15 @@ class ExpressionRequestMatcherTest extends TestCase
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),
);
return [
['request.getMethod() == method', true],
['request.getPathInfo() == path', true],
['request.getHost() == host', true],
['request.getClientIp() == ip', true],
['request.attributes.all() == attributes', true],
['request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', true],
['request.getMethod() != method', false],
['request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', false],
];
}
}
+14 -14
View File
@@ -64,7 +64,7 @@ class FileTest extends TestCase
public function testConstructWhenFileNotExists()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
$this->expectException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
new File(__DIR__.'/Fixtures/not_here');
}
@@ -83,7 +83,7 @@ class FileTest extends TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
$this->assertFileExists($targetPath);
$this->assertFileNotExists($path);
$this->assertFileDoesNotExist($path);
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($targetPath);
@@ -102,7 +102,7 @@ class FileTest extends TestCase
$movedFile = $file->move($targetDir, 'test.newname.gif');
$this->assertFileExists($targetPath);
$this->assertFileNotExists($path);
$this->assertFileDoesNotExist($path);
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($targetPath);
@@ -110,14 +110,14 @@ class FileTest extends TestCase
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'),
);
return [
['original.gif', 'original.gif'],
['..\\..\\original.gif', 'original.gif'],
['../../original.gif', 'original.gif'],
['файлfile.gif', 'файлfile.gif'],
['..\\..\\файлfile.gif', 'файлfile.gif'],
['../../файлfile.gif', 'файлfile.gif'],
];
}
/**
@@ -137,7 +137,7 @@ class FileTest extends TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
$this->assertFileExists($targetPath);
$this->assertFileNotExists($path);
$this->assertFileDoesNotExist($path);
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($targetPath);
@@ -157,7 +157,7 @@ class FileTest extends TestCase
$movedFile = $file->move($targetDir);
$this->assertFileExists($targetPath);
$this->assertFileNotExists($sourcePath);
$this->assertFileDoesNotExist($sourcePath);
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($sourcePath);
@@ -172,7 +172,7 @@ class FileTest extends TestCase
->expects($this->once())
->method('guess')
->with($this->equalTo($path))
->will($this->returnValue($mimeType))
->willReturn($mimeType)
;
return $guesser;
@@ -26,10 +26,8 @@ class MimeTypeTest extends TestCase
chdir(__DIR__.'/../Fixtures');
try {
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess('-test'));
} finally {
chdir($cwd);
} catch(\Exception $e) {
chdir($cwd);
throw $e;
}
}
@@ -40,7 +38,7 @@ class MimeTypeTest extends TestCase
public function testGuessImageWithDirectory()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
$this->expectException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory');
}
@@ -62,9 +60,23 @@ class MimeTypeTest extends TestCase
$this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
}
/**
* @requires PHP 7.0
*/
public function testGuessWithDuplicatedFileType()
{
if ('application/zip' === MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.docx')) {
$this->addToAssertionCount(1);
return;
}
$this->assertSame('application/vnd.openxmlformats-officedocument.wordprocessingml.document', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.docx'));
}
public function testGuessWithIncorrectPath()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
$this->expectException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here');
}
@@ -83,7 +95,7 @@ class MimeTypeTest extends TestCase
@chmod($path, 0333);
if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) {
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException');
$this->expectException('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException');
MimeTypeGuesser::getInstance()->guess($path);
} else {
$this->markTestSkipped('Can not verify chmod operations, change of file permissions failed');
@@ -25,7 +25,7 @@ class UploadedFileTest extends TestCase
public function testConstructWhenFileNotExists()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
$this->expectException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
new UploadedFile(
__DIR__.'/Fixtures/not_here',
@@ -41,7 +41,7 @@ class UploadedFileTest extends TestCase
'original.gif',
null,
filesize(__DIR__.'/Fixtures/test.gif'),
UPLOAD_ERR_OK
\UPLOAD_ERR_OK
);
$this->assertEquals('application/octet-stream', $file->getClientMimeType());
@@ -58,7 +58,7 @@ class UploadedFileTest extends TestCase
'original.gif',
null,
filesize(__DIR__.'/Fixtures/.unknownextension'),
UPLOAD_ERR_OK
\UPLOAD_ERR_OK
);
$this->assertEquals('application/octet-stream', $file->getClientMimeType());
@@ -82,12 +82,25 @@ class UploadedFileTest extends TestCase
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/jpeg',
'image/png',
filesize(__DIR__.'/Fixtures/test.gif'),
null
);
$this->assertEquals('jpeg', $file->guessClientExtension());
$this->assertEquals('png', $file->guessClientExtension());
}
public function testCaseSensitiveMimeType()
{
$file = new UploadedFile(
__DIR__.'/Fixtures/case-sensitive-mime-type.xlsm',
'test.xlsm',
'application/vnd.ms-excel.sheet.macroEnabled.12',
filesize(__DIR__.'/Fixtures/case-sensitive-mime-type.xlsm'),
null
);
$this->assertEquals('xlsm', $file->guessClientExtension());
}
public function testErrorIsOkByDefault()
@@ -100,7 +113,7 @@ class UploadedFileTest extends TestCase
null
);
$this->assertEquals(UPLOAD_ERR_OK, $file->getError());
$this->assertEquals(\UPLOAD_ERR_OK, $file->getError());
}
public function testGetClientOriginalName()
@@ -129,20 +142,18 @@ class UploadedFileTest extends TestCase
$this->assertEquals('gif', $file->getClientOriginalExtension());
}
/**
* @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException
*/
public function testMoveLocalFileIsNotAllowed()
{
$this->expectException('Symfony\Component\HttpFoundation\File\Exception\FileException');
$file = new UploadedFile(
__DIR__.'/Fixtures/test.gif',
'original.gif',
'image/gif',
filesize(__DIR__.'/Fixtures/test.gif'),
UPLOAD_ERR_OK
\UPLOAD_ERR_OK
);
$movedFile = $file->move(__DIR__.'/Fixtures/directory');
$file->move(__DIR__.'/Fixtures/directory');
}
public function testMoveLocalFileIsAllowedInTestMode()
@@ -159,14 +170,14 @@ class UploadedFileTest extends TestCase
'original.gif',
'image/gif',
filesize($path),
UPLOAD_ERR_OK,
\UPLOAD_ERR_OK,
true
);
$movedFile = $file->move(__DIR__.'/Fixtures/directory');
$this->assertFileExists($targetPath);
$this->assertFileNotExists($path);
$this->assertFileDoesNotExist($path);
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
@unlink($targetPath);
@@ -224,7 +235,7 @@ class UploadedFileTest extends TestCase
'original.gif',
null,
filesize(__DIR__.'/Fixtures/test.gif'),
UPLOAD_ERR_OK,
\UPLOAD_ERR_OK,
true
);
@@ -249,13 +260,13 @@ class UploadedFileTest extends TestCase
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),
);
return [
[\UPLOAD_ERR_INI_SIZE],
[\UPLOAD_ERR_FORM_SIZE],
[\UPLOAD_ERR_PARTIAL],
[\UPLOAD_ERR_NO_TMP_DIR],
[\UPLOAD_ERR_EXTENSION],
];
}
public function testIsInvalidIfNotHttpUpload()
@@ -265,9 +276,23 @@ class UploadedFileTest extends TestCase
'original.gif',
null,
filesize(__DIR__.'/Fixtures/test.gif'),
UPLOAD_ERR_OK
\UPLOAD_ERR_OK
);
$this->assertFalse($file->isValid());
}
public function testGetMaxFilesize()
{
$size = UploadedFile::getMaxFilesize();
$this->assertIsInt($size);
$this->assertGreaterThan(0, $size);
if (0 === (int) ini_get('post_max_size') && 0 === (int) ini_get('upload_max_filesize')) {
$this->assertSame(\PHP_INT_MAX, $size);
} else {
$this->assertLessThan(\PHP_INT_MAX, $size);
}
}
}
+59 -61
View File
@@ -23,12 +23,10 @@ use Symfony\Component\HttpFoundation\FileBag;
*/
class FileBagTest extends TestCase
{
/**
* @expectedException \InvalidArgumentException
*/
public function testFileMustBeAnArrayOrUploadedFile()
{
new FileBag(array('file' => 'foo'));
$this->expectException('InvalidArgumentException');
new FileBag(['file' => 'foo']);
}
public function testShouldConvertsUploadedFiles()
@@ -36,54 +34,54 @@ class FileBagTest extends TestCase
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array('file' => array(
$bag = new FileBag(['file' => [
'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(
$bag = new FileBag(['file' => [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => UPLOAD_ERR_NO_FILE,
'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),
)));
$bag = new FileBag(['files' => [
'name' => [''],
'type' => [''],
'tmp_name' => [''],
'error' => [\UPLOAD_ERR_NO_FILE],
'size' => [0],
]]);
$this->assertSame(array(), $bag->get('files'));
$this->assertSame([], $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),
)));
$bag = new FileBag(['files' => [
'name' => ['file1' => ''],
'type' => ['file1' => ''],
'tmp_name' => ['file1' => ''],
'error' => ['file1' => \UPLOAD_ERR_NO_FILE],
'size' => ['file1' => 0],
]]);
$this->assertSame(array('file1' => null), $bag->get('files'));
$this->assertSame(['file1' => null], $bag->get('files'));
}
public function testShouldConvertUploadedFilesWithPhpBug()
@@ -91,25 +89,25 @@ class FileBagTest extends TestCase
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array(
'child' => array(
'name' => array(
$bag = new FileBag([
'child' => [
'name' => [
'file' => basename($tmpFile),
),
'type' => array(
],
'type' => [
'file' => 'text/plain',
),
'tmp_name' => array(
],
'tmp_name' => [
'file' => $tmpFile,
),
'error' => array(
],
'error' => [
'file' => 0,
),
'size' => array(
],
'size' => [
'file' => 100,
),
),
));
],
],
]);
$files = $bag->all();
$this->assertEquals($file, $files['child']['file']);
@@ -120,25 +118,25 @@ class FileBagTest extends TestCase
$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),
),
),
));
$bag = new FileBag([
'child' => [
'name' => [
'sub' => ['file' => basename($tmpFile)],
],
'type' => [
'sub' => ['file' => 'text/plain'],
],
'tmp_name' => [
'sub' => ['file' => $tmpFile],
],
'error' => [
'sub' => ['file' => 0],
],
'size' => [
'sub' => ['file' => 100],
],
],
]);
$files = $bag->all();
$this->assertEquals($file, $files['child']['sub']['file']);
@@ -148,7 +146,7 @@ class FileBagTest extends TestCase
{
$tmpFile = $this->createTempFile();
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
$bag = new FileBag(array('image' => array('file' => $file)));
$bag = new FileBag(['image' => ['file' => $file]]);
$files = $bag->all();
$this->assertEquals($file, $files['image']['file']);
@@ -167,9 +165,9 @@ class FileBagTest extends TestCase
protected function tearDown()
{
foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) {
unlink($file);
@unlink($file);
}
rmdir(sys_get_temp_dir().'/form_test');
@rmdir(sys_get_temp_dir().'/form_test');
}
}
+47 -30
View File
@@ -18,7 +18,7 @@ class HeaderBagTest extends TestCase
{
public function testConstructor()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$bag = new HeaderBag(['foo' => 'bar']);
$this->assertTrue($bag->has('foo'));
}
@@ -30,31 +30,36 @@ class HeaderBagTest extends TestCase
public function testToStringNotNull()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$bag = new HeaderBag(['foo' => 'bar']);
$this->assertEquals("Foo: bar\r\n", $bag->__toString());
}
public function testKeys()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$bag = new HeaderBag(['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'));
$bag = new HeaderBag(['foo' => 'Tue, 4 Sep 2012 20:00:00 +0200']);
$headerDate = $bag->getDate('foo');
$this->assertInstanceOf('DateTime', $headerDate);
}
/**
* @expectedException \RuntimeException
*/
public function testGetDateNull()
{
$bag = new HeaderBag(['foo' => null]);
$headerDate = $bag->getDate('foo');
$this->assertNull($headerDate);
}
public function testGetDateException()
{
$bag = new HeaderBag(array('foo' => 'Tue'));
$headerDate = $bag->getDate('foo');
$this->expectException('RuntimeException');
$bag = new HeaderBag(['foo' => 'Tue']);
$bag->getDate('foo');
}
public function testGetCacheControlHeader()
@@ -67,50 +72,53 @@ class HeaderBagTest extends TestCase
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(['foo' => 'bar']);
$this->assertEquals(['foo' => ['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');
$bag = new HeaderBag(['FOO' => 'BAR']);
$this->assertEquals(['foo' => ['BAR']], $bag->all(), '->all() gets all the input key are lower case');
}
public function testReplace()
{
$bag = new HeaderBag(array('foo' => 'bar'));
$bag = new HeaderBag(['foo' => 'bar']);
$bag->replace(array('NOPE' => 'BAR'));
$this->assertEquals(array('nope' => array('BAR')), $bag->all(), '->replace() replaces the input with the argument');
$bag->replace(['NOPE' => 'BAR']);
$this->assertEquals(['nope' => ['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'));
$bag = new HeaderBag(['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');
$this->assertEquals(['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');
$this->assertEquals(['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');
$this->assertEquals(['bar', 'bor'], $bag->get('foo', 'nope', false), '->get return all values as array');
$bag->set('baz', null);
$this->assertNull($bag->get('baz', 'nope'), '->get return null although different default value is given');
}
public function testSetAssociativeArray()
{
$bag = new HeaderBag();
$bag->set('foo', array('bad-assoc-index' => 'value'));
$bag->set('foo', ['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');
$this->assertEquals(['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'));
$bag = new HeaderBag(['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');
@@ -143,7 +151,7 @@ class HeaderBagTest extends TestCase
public function testCacheControlDirectiveParsing()
{
$bag = new HeaderBag(array('cache-control' => 'public, max-age=10'));
$bag = new HeaderBag(['cache-control' => 'public, max-age=10']);
$this->assertTrue($bag->hasCacheControlDirective('public'));
$this->assertTrue($bag->getCacheControlDirective('public'));
@@ -156,15 +164,15 @@ class HeaderBagTest extends TestCase
public function testCacheControlDirectiveParsingQuotedZero()
{
$bag = new HeaderBag(array('cache-control' => 'max-age="0"'));
$bag = new HeaderBag(['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'));
$bag = new HeaderBag(['cache-control' => 'private, max-age=100']);
$bag->replace(['cache-control' => 'public, max-age=10']);
$this->assertTrue($bag->hasCacheControlDirective('public'));
$this->assertTrue($bag->getCacheControlDirective('public'));
@@ -172,15 +180,24 @@ class HeaderBagTest extends TestCase
$this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
}
public function testCacheControlClone()
{
$headers = ['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');
$headers = ['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([$headers[$key]], $val);
}
$this->assertEquals(\count($headers), $i);
@@ -188,7 +205,7 @@ class HeaderBagTest extends TestCase
public function testCount()
{
$headers = array('foo' => 'bar', 'HELLO' => 'WORLD');
$headers = ['foo' => 'bar', 'HELLO' => 'WORLD'];
$headerBag = new HeaderBag($headers);
$this->assertCount(\count($headers), $headerBag);
+34 -34
View File
@@ -26,20 +26,20 @@ class IpUtilsTest extends TestCase
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'),
);
return [
[true, '192.168.1.1', '192.168.1.1'],
[true, '192.168.1.1', '192.168.1.1/1'],
[true, '192.168.1.1', '192.168.1.0/24'],
[false, '192.168.1.1', '1.2.3.4/1'],
[false, '192.168.1.1', '192.168.1.1/33'], // invalid subnet
[true, '192.168.1.1', ['1.2.3.4/1', '192.168.1.0/24']],
[true, '192.168.1.1', ['192.168.1.0/24', '1.2.3.4/1']],
[false, '192.168.1.1', ['1.2.3.4/1', '4.3.2.1/1']],
[true, '1.2.3.4', '0.0.0.0/0'],
[true, '1.2.3.4', '192.168.1.0/0'],
[false, '1.2.3.4', '256.256.256/0'], // invalid CIDR notation
[false, 'an_invalid_ip', '192.168.1.0/24'],
];
}
/**
@@ -56,28 +56,28 @@ class IpUtilsTest extends TestCase
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'),
);
return [
[true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'],
[false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'],
[false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'],
[true, '0:0:0:0:0:0:0:1', '::1'],
[false, '0:0:603:0:396e:4789:8e99:0001', '::1'],
[true, '0:0:603:0:396e:4789:8e99:0001', '::/0'],
[true, '0:0:603:0:396e:4789:8e99:0001', '2a01:198:603:0::/0'],
[true, '2a01:198:603:0:396e:4789:8e99:890f', ['::1', '2a01:198:603:0::/65']],
[true, '2a01:198:603:0:396e:4789:8e99:890f', ['2a01:198:603:0::/65', '::1']],
[false, '2a01:198:603:0:396e:4789:8e99:890f', ['::1', '1a01:198:603:0::/65']],
[false, '}__test|O:21:&quot;JDatabaseDriverMysqli&quot;:3:{s:2', '::1'],
[false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'],
];
}
/**
* @expectedException \RuntimeException
* @requires extension sockets
*/
public function testAnIpv6WithOptionDisabledIpv6()
{
$this->expectException('RuntimeException');
if (\defined('AF_INET6')) {
$this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
}
@@ -95,10 +95,10 @@ class IpUtilsTest extends TestCase
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', '*'),
);
return [
'invalid proxy wildcard' => ['192.168.20.13', '*'],
'invalid proxy missing netmask' => ['192.168.20.13', '0.0.0.0'],
'invalid request IP with invalid proxy wildcard' => ['0.0.0.0', '*'],
];
}
}
+45 -30
View File
@@ -33,13 +33,13 @@ class JsonResponseTest extends TestCase
public function testConstructorWithArrayCreatesJsonArray()
{
$response = new JsonResponse(array(0, 1, 2, 3));
$response = new JsonResponse([0, 1, 2, 3]);
$this->assertSame('[0,1,2,3]', $response->getContent());
}
public function testConstructorWithAssocArrayCreatesJsonObject()
{
$response = new JsonResponse(array('foo' => 'bar'));
$response = new JsonResponse(['foo' => 'bar']);
$this->assertSame('{"foo":"bar"}', $response->getContent());
}
@@ -52,7 +52,8 @@ class JsonResponseTest extends TestCase
$this->assertSame('0', $response->getContent());
$response = new JsonResponse(0.1);
$this->assertSame('0.1', $response->getContent());
$this->assertEquals(0.1, $response->getContent());
$this->assertIsString($response->getContent());
$response = new JsonResponse(true);
$this->assertSame('true', $response->getContent());
@@ -60,7 +61,7 @@ class JsonResponseTest extends TestCase
public function testConstructorWithCustomStatus()
{
$response = new JsonResponse(array(), 202);
$response = new JsonResponse([], 202);
$this->assertSame(202, $response->getStatusCode());
}
@@ -72,22 +73,35 @@ class JsonResponseTest extends TestCase
public function testConstructorWithCustomHeaders()
{
$response = new JsonResponse(array(), 200, array('ETag' => 'foo'));
$response = new JsonResponse([], 200, ['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');
$headers = ['Content-Type' => 'application/vnd.acme.blog-v1+json'];
$response = new JsonResponse(array(), 200, $headers);
$response = new JsonResponse([], 200, $headers);
$this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
}
public function testSetJson()
{
$response = new JsonResponse('1', 200, [], true);
$this->assertEquals('1', $response->getContent());
$response = new JsonResponse('[1]', 200, [], true);
$this->assertEquals('[1]', $response->getContent());
$response = new JsonResponse(null, 200, []);
$response->setJson('true');
$this->assertEquals('true', $response->getContent());
}
public function testCreate()
{
$response = JsonResponse::create(array('foo' => 'bar'), 204);
$response = JsonResponse::create(['foo' => 'bar'], 204);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertEquals('{"foo":"bar"}', $response->getContent());
@@ -103,14 +117,14 @@ class JsonResponseTest extends TestCase
public function testStaticCreateJsonArray()
{
$response = JsonResponse::create(array(0, 1, 2, 3));
$response = JsonResponse::create([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'));
$response = JsonResponse::create(['foo' => 'bar']);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('{"foo":"bar"}', $response->getContent());
}
@@ -127,7 +141,8 @@ class JsonResponseTest extends TestCase
$response = JsonResponse::create(0.1);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
$this->assertSame('0.1', $response->getContent());
$this->assertEquals(0.1, $response->getContent());
$this->assertIsString($response->getContent());
$response = JsonResponse::create(true);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
@@ -136,7 +151,7 @@ class JsonResponseTest extends TestCase
public function testStaticCreateWithCustomStatus()
{
$response = JsonResponse::create(array(), 202);
$response = JsonResponse::create([], 202);
$this->assertSame(202, $response->getStatusCode());
}
@@ -148,22 +163,22 @@ class JsonResponseTest extends TestCase
public function testStaticCreateWithCustomHeaders()
{
$response = JsonResponse::create(array(), 200, array('ETag' => 'foo'));
$response = JsonResponse::create([], 200, ['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');
$headers = ['Content-Type' => 'application/vnd.acme.blog-v1+json'];
$response = JsonResponse::create(array(), 200, $headers);
$response = JsonResponse::create([], 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');
$response = JsonResponse::create(['foo' => 'bar'])->setCallback('callback');
$this->assertEquals('/**/callback({"foo":"bar"});', $response->getContent());
$this->assertEquals('text/javascript', $response->headers->get('Content-Type'));
@@ -180,44 +195,44 @@ class JsonResponseTest extends TestCase
{
$response = new JsonResponse();
$this->assertEquals(JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT, $response->getEncodingOptions());
$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)));
$response->setData([[1, 2, 3]]);
$this->assertEquals('[[1,2,3]]', $response->getContent());
$response->setEncodingOptions(JSON_FORCE_OBJECT);
$response->setEncodingOptions(\JSON_FORCE_OBJECT);
$this->assertEquals('{"0":{"0":1,"1":2,"2":3}}', $response->getContent());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testItAcceptsJsonAsString()
{
$response = JsonResponse::fromJsonString('{"foo":"bar"}');
$this->assertSame('{"foo":"bar"}', $response->getContent());
}
public function testSetCallbackInvalidIdentifier()
{
$this->expectException('InvalidArgumentException');
$response = new JsonResponse('foo');
$response->setCallback('+invalid');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testSetContent()
{
$this->expectException('InvalidArgumentException');
JsonResponse::create("\xB1\x31");
}
/**
* @expectedException \Exception
* @expectedExceptionMessage This error is expected
*/
public function testSetContentJsonSerializeError()
{
$this->expectException('Exception');
$this->expectExceptionMessage('This error is expected');
if (!interface_exists('JsonSerializable', false)) {
$this->markTestSkipped('JsonSerializable is required.');
}
@@ -229,7 +244,7 @@ class JsonResponseTest extends TestCase
public function testSetComplexCallback()
{
$response = JsonResponse::create(array('foo' => 'bar'));
$response = JsonResponse::create(['foo' => 'bar']);
$response->setCallback('ಠ_ಠ["foo"].bar[0]');
$this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent());
+41 -76
View File
@@ -23,44 +23,44 @@ class ParameterBagTest extends TestCase
public function testAll()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$this->assertEquals(array('foo' => 'bar'), $bag->all(), '->all() gets all the input');
$bag = new ParameterBag(['foo' => 'bar']);
$this->assertEquals(['foo' => 'bar'], $bag->all(), '->all() gets all the input');
}
public function testKeys()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$this->assertEquals(array('foo'), $bag->keys());
$bag = new ParameterBag(['foo' => 'bar']);
$this->assertEquals(['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());
$bag = new ParameterBag(['foo' => 'bar']);
$bag->add(['bar' => 'bas']);
$this->assertEquals(['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 = new ParameterBag(['foo' => 'bar']);
$bag->add(['bar' => 'bas']);
$this->assertEquals(['foo' => 'bar', 'bar' => 'bas'], $bag->all());
$bag->remove('bar');
$this->assertEquals(array('foo' => 'bar'), $bag->all());
$this->assertEquals(['foo' => 'bar'], $bag->all());
}
public function testReplace()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$bag = new ParameterBag(['foo' => 'bar']);
$bag->replace(array('FOO' => 'BAR'));
$this->assertEquals(array('FOO' => 'BAR'), $bag->all(), '->replace() replaces the input with the argument');
$bag->replace(['FOO' => 'BAR']);
$this->assertEquals(['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));
$bag = new ParameterBag(['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');
@@ -69,49 +69,14 @@ class ParameterBagTest extends TestCase
public function testGetDoesNotUseDeepByDefault()
{
$bag = new ParameterBag(array('foo' => array('bar' => 'moo')));
$bag = new ParameterBag(['foo' => ['bar' => 'moo']]);
$this->assertNull($bag->get('foo[bar]'));
}
/**
* @group legacy
* @dataProvider getInvalidPaths
* @expectedException \InvalidArgumentException
*/
public function testGetDeepWithInvalidPaths($path)
{
$bag = new ParameterBag(array('foo' => array('bar' => 'moo')));
$bag->get($path, null, true);
}
public function getInvalidPaths()
{
return array(
array('foo[['),
array('foo[d'),
array('foo[bar]]'),
array('foo[bar]d'),
);
}
/**
* @group legacy
*/
public function testGetDeep()
{
$bag = new ParameterBag(array('foo' => array('bar' => array('moo' => 'boo'))));
$this->assertEquals(array('moo' => 'boo'), $bag->get('foo[bar]', null, true));
$this->assertEquals('boo', $bag->get('foo[bar][moo]', null, true));
$this->assertEquals('default', $bag->get('foo[bar][foo]', 'default', true));
$this->assertEquals('default', $bag->get('bar[moo][foo]', 'default', true));
}
public function testSet()
{
$bag = new ParameterBag(array());
$bag = new ParameterBag([]);
$bag->set('foo', 'bar');
$this->assertEquals('bar', $bag->get('foo'), '->set() sets the value of parameter');
@@ -122,7 +87,7 @@ class ParameterBagTest extends TestCase
public function testHas()
{
$bag = new ParameterBag(array('foo' => 'bar'));
$bag = new ParameterBag(['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');
@@ -130,7 +95,7 @@ class ParameterBagTest extends TestCase
public function testGetAlpha()
{
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
$bag = new ParameterBag(['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');
@@ -138,7 +103,7 @@ class ParameterBagTest extends TestCase
public function testGetAlnum()
{
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
$bag = new ParameterBag(['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');
@@ -146,7 +111,7 @@ class ParameterBagTest extends TestCase
public function testGetDigits()
{
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
$bag = new ParameterBag(['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');
@@ -154,7 +119,7 @@ class ParameterBagTest extends TestCase
public function testGetInt()
{
$bag = new ParameterBag(array('digits' => '0123'));
$bag = new ParameterBag(['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');
@@ -162,42 +127,42 @@ class ParameterBagTest extends TestCase
public function testFilter()
{
$bag = new ParameterBag(array(
$bag = new ParameterBag([
'digits' => '0123ab',
'email' => 'example@example.com',
'url' => 'http://example.com/foo',
'dec' => '256',
'hex' => '0x100',
'array' => array('bang'),
));
'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('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('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->assertEquals('http://example.com/foo', $bag->filter('url', '', \FILTER_VALIDATE_URL, ['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->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('dec', '', \FILTER_VALIDATE_INT, [
'flags' => \FILTER_FLAG_ALLOW_HEX,
'options' => ['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->assertFalse($bag->filter('hex', '', \FILTER_VALIDATE_INT, [
'flags' => \FILTER_FLAG_ALLOW_HEX,
'options' => ['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');
$this->assertEquals(['bang'], $bag->filter('array', ''), '->filter() gets a value of parameter as an array');
}
public function testGetIterator()
{
$parameters = array('foo' => 'bar', 'hello' => 'world');
$parameters = ['foo' => 'bar', 'hello' => 'world'];
$bag = new ParameterBag($parameters);
$i = 0;
@@ -211,7 +176,7 @@ class ParameterBagTest extends TestCase
public function testCount()
{
$parameters = array('foo' => 'bar', 'hello' => 'world');
$parameters = ['foo' => 'bar', 'hello' => 'world'];
$bag = new ParameterBag($parameters);
$this->assertCount(\count($parameters), $bag);
@@ -219,7 +184,7 @@ class ParameterBagTest extends TestCase
public function testGetBoolean()
{
$parameters = array('string_true' => 'true', 'string_false' => 'false');
$parameters = ['string_true' => 'true', 'string_false' => 'false'];
$bag = new ParameterBag($parameters);
$this->assertTrue($bag->getBoolean('string_true'), '->getBoolean() gets the string true as boolean true');
+23 -15
View File
@@ -20,26 +20,19 @@ class RedirectResponseTest extends TestCase
{
$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())
));
$this->assertMatchesRegularExpression('#<meta http-equiv="refresh" content="\d+;url=\'foo\.bar\'" />#', preg_replace('/\s+/', ' ', $response->getContent()));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRedirectResponseConstructorNullUrl()
{
$response = new RedirectResponse(null);
$this->expectException('InvalidArgumentException');
new RedirectResponse(null);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRedirectResponseConstructorWrongStatusCode()
{
$response = new RedirectResponse('foo.bar', 404);
$this->expectException('InvalidArgumentException');
new RedirectResponse('foo.bar', 404);
}
public function testGenerateLocationHeader()
@@ -65,11 +58,9 @@ class RedirectResponseTest extends TestCase
$this->assertEquals('baz.beep', $response->getTargetUrl());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testSetTargetUrlNull()
{
$this->expectException('InvalidArgumentException');
$response = new RedirectResponse('foo.bar');
$response->setTargetUrl(null);
}
@@ -81,4 +72,21 @@ class RedirectResponseTest extends TestCase
$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, ['cache-control' => 'max-age=86400']);
$this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
$this->assertTrue($response->headers->hasCacheControlDirective('max-age'));
$response = new RedirectResponse('foo.bar', 301, ['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'));
}
}
+20 -20
View File
@@ -34,20 +34,20 @@ class RequestMatcherTest extends TestCase
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),
);
return [
['get', 'get', true],
['get', ['get', 'post'], true],
['get', 'post', false],
['get', 'GET', true],
['get', ['GET', 'POST'], true],
['get', 'POST', false],
];
}
public function testScheme()
{
$httpRequest = $request = $request = Request::create('');
$httpsRequest = $request = $request = Request::create('', 'get', array(), array(), array(), array('HTTPS' => 'on'));
$httpsRequest = $request = $request = Request::create('', 'get', [], [], [], ['HTTPS' => 'on']);
$matcher = new RequestMatcher();
$matcher->matchScheme('https');
@@ -69,7 +69,7 @@ class RequestMatcherTest extends TestCase
public function testHost($pattern, $isMatch)
{
$matcher = new RequestMatcher();
$request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com'));
$request = Request::create('', 'get', [], [], [], ['HTTP_HOST' => 'foo.example.com']);
$matcher->matchHost($pattern);
$this->assertSame($isMatch, $matcher->matches($request));
@@ -80,16 +80,16 @@ class RequestMatcherTest extends TestCase
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),
);
return [
['.*\.example\.com', true],
['\.example\.com$', true],
['^.*\.example\.com$', true],
['.*\.sensio\.com', false],
['.*\.example\.COM', true],
['\.example\.COM$', true],
['^.*\.example\.COM$', true],
['.*\.sensio\.COM', false],
];
}
public function testPath()
File diff suppressed because it is too large Load Diff
+146 -92
View File
@@ -20,91 +20,67 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag;
*/
class ResponseHeaderBagTest extends TestCase
{
/**
* @dataProvider provideAllPreserveCase
*/
public function testAllPreserveCase($headers, $expected)
public function testAllPreserveCase()
{
$headers = [
'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();
$this->assertEquals($expected, $bag->allPreserveCase(), '->allPreserveCase() gets all input keys in original case');
}
public function provideAllPreserveCase()
{
return array(
array(
array('fOo' => 'BAR'),
array('fOo' => array('BAR'), 'Cache-Control' => array('no-cache')),
),
array(
array('ETag' => 'xyzzy'),
array('ETag' => array('xyzzy'), 'Cache-Control' => array('private, must-revalidate')),
),
array(
array('Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ=='),
array('Content-MD5' => array('Q2hlY2sgSW50ZWdyaXR5IQ=='), 'Cache-Control' => array('no-cache')),
),
array(
array('P3P' => 'CP="CAO PSA OUR"'),
array('P3P' => array('CP="CAO PSA OUR"'), 'Cache-Control' => array('no-cache')),
),
array(
array('WWW-Authenticate' => 'Basic realm="WallyWorld"'),
array('WWW-Authenticate' => array('Basic realm="WallyWorld"'), 'Cache-Control' => array('no-cache')),
),
array(
array('X-UA-Compatible' => 'IE=edge,chrome=1'),
array('X-UA-Compatible' => array('IE=edge,chrome=1'), 'Cache-Control' => array('no-cache')),
),
array(
array('X-XSS-Protection' => '1; mode=block'),
array('X-XSS-Protection' => array('1; mode=block'), 'Cache-Control' => array('no-cache')),
),
);
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', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag([]);
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
$bag = new ResponseHeaderBag(array('Cache-Control' => 'public'));
$bag = new ResponseHeaderBag(['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'));
$bag = new ResponseHeaderBag(['ETag' => 'abcde']);
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('private'));
$this->assertTrue($bag->hasCacheControlDirective('must-revalidate'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
$this->assertFalse($bag->hasCacheControlDirective('max-age'));
$bag = new ResponseHeaderBag(array('Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT'));
$bag = new ResponseHeaderBag(['Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT']);
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array(
$bag = new ResponseHeaderBag([
'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'));
$bag = new ResponseHeaderBag(['Last-Modified' => 'abcde']);
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('Etag' => 'abcde', 'Last-Modified' => 'abcde'));
$bag = new ResponseHeaderBag(['Etag' => 'abcde', 'Last-Modified' => 'abcde']);
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('cache-control' => 'max-age=100'));
$bag = new ResponseHeaderBag(['cache-control' => 'max-age=100']);
$this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('cache-control' => 's-maxage=100'));
$bag = new ResponseHeaderBag(['cache-control' => 's-maxage=100']);
$this->assertEquals('s-maxage=100', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag(array('cache-control' => 'private, max-age=100'));
$bag = new ResponseHeaderBag(['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'));
$bag = new ResponseHeaderBag(['cache-control' => 'public, max-age=100']);
$this->assertEquals('max-age=100, public', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag();
@@ -112,7 +88,7 @@ class ResponseHeaderBagTest extends TestCase
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag();
$bag->set('Cache-Control', array('public', 'must-revalidate'));
$bag->set('Cache-Control', ['public', 'must-revalidate']);
$this->assertCount(1, $bag->get('Cache-Control', null, false));
$this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
@@ -123,47 +99,63 @@ class ResponseHeaderBagTest extends TestCase
$this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
}
public function testCacheControlClone()
{
$headers = ['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 = new ResponseHeaderBag([]);
$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).'; path=/; httponly', $bag);
$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 = new ResponseHeaderBag([]);
$bag->clearCookie('foo', '/', null, true, false);
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; secure', $bag);
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag);
}
public function testClearCookieSamesite()
{
$bag = new ResponseHeaderBag([]);
$bag->clearCookie('foo', '/', null, true, false, 'none');
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure; samesite=none', $bag);
}
public function testReplace()
{
$bag = new ResponseHeaderBag(array());
$this->assertEquals('no-cache', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag([]);
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
$bag->replace(array('Cache-Control' => 'public'));
$bag->replace(['Cache-Control' => 'public']);
$this->assertEquals('public', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('public'));
}
public function testReplaceWithRemove()
{
$bag = new ResponseHeaderBag(array());
$this->assertEquals('no-cache', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag([]);
$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', $bag->get('Cache-Control'));
$bag->replace([]);
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
}
@@ -176,6 +168,13 @@ class ResponseHeaderBagTest extends TestCase
$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([
'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);
@@ -183,6 +182,7 @@ class ResponseHeaderBagTest extends TestCase
$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']);
@@ -192,18 +192,23 @@ class ResponseHeaderBagTest extends TestCase
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);
@@ -227,21 +232,33 @@ class ResponseHeaderBagTest extends TestCase
$this->assertFalse(isset($cookies['']['/']['bar']));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testSetCookieHeader()
{
$bag = new ResponseHeaderBag();
$bag->set('set-cookie', 'foo=bar');
$this->assertEquals([new Cookie('foo', 'bar', 0, '/', null, false, false, true)], $bag->getCookies());
$bag->set('set-cookie', 'foo2=bar2', false);
$this->assertEquals([
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([], $bag->getCookies());
}
public function testGetCookiesWithInvalidArgument()
{
$this->expectException('InvalidArgumentException');
$bag = new ResponseHeaderBag();
$bag->getCookies('invalid_argument');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionInvalidDisposition()
{
$this->expectException('InvalidArgumentException');
$headers = new ResponseHeaderBag();
$headers->makeDisposition('invalid', 'foo.html');
@@ -267,28 +284,28 @@ class ResponseHeaderBagTest extends TestCase
(string) $headers;
$allHeaders = $headers->allPreserveCase();
$this->assertEquals(array('http://www.symfony.com'), $allHeaders['Location']);
$this->assertEquals(array('text/html'), $allHeaders['Content-type']);
$this->assertEquals(['http://www.symfony.com'], $allHeaders['Location']);
$this->assertEquals(['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'),
);
return [
['attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'],
['attachment', 'foo.html', '', 'attachment; filename="foo.html"'],
['attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'],
['attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'],
['attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'],
['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)
{
$this->expectException('InvalidArgumentException');
$headers = new ResponseHeaderBag();
$headers->makeDisposition($disposition, $filename);
@@ -296,18 +313,55 @@ class ResponseHeaderBagTest extends TestCase
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'),
);
return [
['attachment', 'foo%20bar.html'],
['attachment', 'foo/bar.html'],
['attachment', '/foo.html'],
['attachment', 'foo\bar.html'],
['attachment', '\foo.html'],
['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(['Date' => $someDate]);
$this->assertEquals($someDate, $bag->get('Date'));
}
public function testDateHeaderWillBeRecreatedWhenRemoved()
{
$someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
$bag = new ResponseHeaderBag(['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([]);
$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));
$this->assertMatchesRegularExpression('#^Set-Cookie:\s+'.preg_quote($expected, '#').'$#m', str_replace("\r\n", "\n", (string) $actual));
}
}
+127 -81
View File
@@ -21,7 +21,7 @@ class ResponseTest extends ResponseTestCase
{
public function testCreate()
{
$response = Response::create('foo', 301, array('Foo' => 'bar'));
$response = Response::create('foo', 301, ['Foo' => 'bar']);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
$this->assertEquals(301, $response->getStatusCode());
@@ -33,7 +33,7 @@ class ResponseTest extends ResponseTestCase
$response = new Response();
$response = explode("\r\n", $response);
$this->assertEquals('HTTP/1.0 200 OK', $response[0]);
$this->assertEquals('Cache-Control: no-cache', $response[1]);
$this->assertEquals('Cache-Control: no-cache, private', $response[1]);
}
public function testClone()
@@ -253,10 +253,10 @@ class ResponseTest extends ResponseTestCase
public function testIsValidateable()
{
$response = new Response('', 200, array('Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
$response = new Response('', 200, ['Last-Modified' => $this->createDateTimeOneHourAgo()->format(\DATE_RFC2822)]);
$this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present');
$response = new Response('', 200, array('ETag' => '"12345"'));
$response = new Response('', 200, ['ETag' => '"12345"']);
$this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present');
$response = new Response();
@@ -266,7 +266,7 @@ class ResponseTest extends ResponseTestCase
public function testGetDate()
{
$oneHourAgo = $this->createDateTimeOneHourAgo();
$response = new Response('', 200, array('Date' => $oneHourAgo->format(DATE_RFC2822)));
$response = new Response('', 200, ['Date' => $oneHourAgo->format(\DATE_RFC2822)]);
$date = $response->getDate();
$this->assertEquals($oneHourAgo->getTimestamp(), $date->getTimestamp(), '->getDate() returns the Date header if present');
@@ -274,9 +274,9 @@ class ResponseTest extends ResponseTestCase
$date = $response->getDate();
$this->assertEquals(time(), $date->getTimestamp(), '->getDate() returns the current Date if no Date header present');
$response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
$response = new Response('', 200, ['Date' => $this->createDateTimeOneHourAgo()->format(\DATE_RFC2822)]);
$now = $this->createDateTimeNow();
$response->headers->set('Date', $now->format(DATE_RFC2822));
$response->headers->set('Date', $now->format(\DATE_RFC2822));
$date = $response->getDate();
$this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified');
@@ -299,13 +299,13 @@ class ResponseTest extends ResponseTestCase
$response = new Response();
$response->headers->set('Cache-Control', 'must-revalidate');
$response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
$response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(\DATE_RFC2822));
$this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present');
$response = new Response();
$response->headers->set('Cache-Control', 'must-revalidate');
$response->headers->set('Expires', -1);
$this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822));
$this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(\DATE_RFC822));
$response = new Response();
$this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available');
@@ -364,26 +364,32 @@ class ResponseTest extends ResponseTestCase
$this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired');
$response = new Response();
$response->headers->set('Expires', date(DATE_RFC2822, time() + 600));
$response->headers->set('Expires', date(\DATE_RFC2822, time() + 600));
$response->expire();
$this->assertNull($response->headers->get('Expires'), '->expire() removes the Expires header when the response is fresh');
}
public function testNullExpireHeader()
{
$response = new Response(null, 200, ['Expires' => null]);
$this->assertNull($response->getExpires());
}
public function testGetTtl()
{
$response = new Response();
$this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present');
$response = new Response();
$response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
$response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(\DATE_RFC2822));
$this->assertEquals(3600, $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present');
$response = new Response();
$response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822));
$response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(\DATE_RFC2822));
$this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past');
$response = new Response();
$response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822));
$response->headers->set('Expires', $response->getDate()->format(\DATE_RFC2822));
$response->headers->set('Age', 0);
$this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero');
@@ -414,21 +420,21 @@ class ResponseTest extends ResponseTestCase
public function testGetVary()
{
$response = new Response();
$this->assertEquals(array(), $response->getVary(), '->getVary() returns an empty array if no Vary header is present');
$this->assertEquals([], $response->getVary(), '->getVary() returns an empty array if no Vary header is present');
$response = new Response();
$response->headers->set('Vary', 'Accept-Language');
$this->assertEquals(array('Accept-Language'), $response->getVary(), '->getVary() parses a single header name value');
$this->assertEquals(['Accept-Language'], $response->getVary(), '->getVary() parses a single header name value');
$response = new Response();
$response->headers->set('Vary', 'Accept-Language User-Agent X-Foo');
$this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by spaces');
$this->assertEquals(['Accept-Language', 'User-Agent', 'X-Foo'], $response->getVary(), '->getVary() parses multiple header name values separated by spaces');
$response = new Response();
$response->headers->set('Vary', 'Accept-Language,User-Agent, X-Foo');
$this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas');
$this->assertEquals(['Accept-Language', 'User-Agent', 'X-Foo'], $response->getVary(), '->getVary() parses multiple header name values separated by commas');
$vary = array('Accept-Language', 'User-Agent', 'X-foo');
$vary = ['Accept-Language', 'User-Agent', 'X-foo'];
$response = new Response();
$response->headers->set('Vary', $vary);
@@ -443,24 +449,24 @@ class ResponseTest extends ResponseTestCase
{
$response = new Response();
$response->setVary('Accept-Language');
$this->assertEquals(array('Accept-Language'), $response->getVary());
$this->assertEquals(['Accept-Language'], $response->getVary());
$response->setVary('Accept-Language, User-Agent');
$this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default');
$this->assertEquals(['Accept-Language', 'User-Agent'], $response->getVary(), '->setVary() replace the vary header by default');
$response->setVary('X-Foo', false);
$this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false');
$this->assertEquals(['Accept-Language', 'User-Agent', 'X-Foo'], $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false');
}
public function testDefaultContentType()
{
$headerMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\ResponseHeaderBag')->setMethods(array('set'))->getMock();
$headerMock->expects($this->at(0))
$headerMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\ResponseHeaderBag')->setMethods(['set'])->getMock();
$headerMock->expects($this->exactly(2))
->method('set')
->with('Content-Type', 'text/html');
$headerMock->expects($this->at(1))
->method('set')
->with('Content-Type', 'text/html; charset=UTF-8');
->withConsecutive(
['Content-Type', 'text/html'],
['Content-Type', 'text/html; charset=UTF-8']
);
$response = new Response('foo');
$response->headers = $headerMock;
@@ -532,7 +538,6 @@ class ResponseTest extends ResponseTestCase
$response->prepare($request);
$this->assertEquals('', $response->getContent());
$this->assertFalse($response->headers->has('Content-Type'));
$this->assertFalse($response->headers->has('Content-Type'));
$response->setContent('content');
$response->setStatusCode(304);
@@ -576,50 +581,56 @@ class ResponseTest extends ResponseTestCase
public function testSetCache()
{
$response = new Response();
//array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public')
// ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public']
try {
$response->setCache(array('wrong option' => 'value'));
$response->setCache(['wrong option' => 'value']);
$this->fail('->setCache() throws an InvalidArgumentException if an option is not supported');
} catch (\Exception $e) {
$this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported');
$this->assertContains('"wrong option"', $e->getMessage());
$this->assertStringContainsString('"wrong option"', $e->getMessage());
}
$options = array('etag' => '"whatever"');
$options = ['etag' => '"whatever"'];
$response->setCache($options);
$this->assertEquals($response->getEtag(), '"whatever"');
$this->assertEquals('"whatever"', $response->getEtag());
$now = $this->createDateTimeNow();
$options = array('last_modified' => $now);
$options = ['last_modified' => $now];
$response->setCache($options);
$this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp());
$this->assertEquals($now->getTimestamp(), $response->getLastModified()->getTimestamp());
$options = array('max_age' => 100);
$options = ['max_age' => 100];
$response->setCache($options);
$this->assertEquals($response->getMaxAge(), 100);
$this->assertEquals(100, $response->getMaxAge());
$options = array('s_maxage' => 200);
$options = ['s_maxage' => 200];
$response->setCache($options);
$this->assertEquals($response->getMaxAge(), 200);
$this->assertEquals(200, $response->getMaxAge());
$this->assertTrue($response->headers->hasCacheControlDirective('public'));
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
$response->setCache(array('public' => true));
$response->setCache(['public' => true]);
$this->assertTrue($response->headers->hasCacheControlDirective('public'));
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
$response->setCache(array('public' => false));
$response->setCache(['public' => false]);
$this->assertFalse($response->headers->hasCacheControlDirective('public'));
$this->assertTrue($response->headers->hasCacheControlDirective('private'));
$response->setCache(array('private' => true));
$response->setCache(['private' => true]);
$this->assertFalse($response->headers->hasCacheControlDirective('public'));
$this->assertTrue($response->headers->hasCacheControlDirective('private'));
$response->setCache(array('private' => false));
$response->setCache(['private' => false]);
$this->assertTrue($response->headers->hasCacheControlDirective('public'));
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
$response->setCache(['immutable' => true]);
$this->assertTrue($response->headers->hasCacheControlDirective('immutable'));
$response->setCache(['immutable' => false]);
$this->assertFalse($response->headers->hasCacheControlDirective('immutable'));
}
public function testSendContent()
@@ -629,7 +640,7 @@ class ResponseTest extends ResponseTestCase
ob_start();
$response->sendContent();
$string = ob_get_clean();
$this->assertContains('test response rendering', $string);
$this->assertStringContainsString('test response rendering', $string);
}
public function testSetPublic()
@@ -641,6 +652,22 @@ class ResponseTest extends ResponseTestCase
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
}
public function testSetImmutable()
{
$response = new Response();
$response->setImmutable();
$this->assertTrue($response->headers->hasCacheControlDirective('immutable'));
}
public function testIsImmutable()
{
$response = new Response();
$response->setImmutable();
$this->assertTrue($response->isImmutable());
}
public function testSetExpires()
{
$response = new Response();
@@ -703,14 +730,14 @@ class ResponseTest extends ResponseTestCase
public function getStatusCodeFixtures()
{
return array(
array('200', null, 'OK'),
array('200', false, ''),
array('200', 'foo', 'foo'),
array('199', null, 'unknown status'),
array('199', false, ''),
array('199', 'foo', 'foo'),
);
return [
['200', null, 'OK'],
['200', false, ''],
['200', 'foo', 'foo'],
['199', null, 'unknown status'],
['199', false, ''],
['199', 'foo', 'foo'],
];
}
public function testIsInformational()
@@ -724,7 +751,7 @@ class ResponseTest extends ResponseTestCase
public function testIsRedirectRedirection()
{
foreach (array(301, 302, 303, 307) as $code) {
foreach ([301, 302, 303, 307] as $code) {
$response = new Response('', $code);
$this->assertTrue($response->isRedirection());
$this->assertTrue($response->isRedirect());
@@ -742,7 +769,7 @@ class ResponseTest extends ResponseTestCase
$this->assertFalse($response->isRedirection());
$this->assertFalse($response->isRedirect());
$response = new Response('', 301, array('Location' => '/good-uri'));
$response = new Response('', 301, ['Location' => '/good-uri']);
$this->assertFalse($response->isRedirect('/bad-uri'));
$this->assertTrue($response->isRedirect('/good-uri'));
}
@@ -758,7 +785,7 @@ class ResponseTest extends ResponseTestCase
public function testIsEmpty()
{
foreach (array(204, 304) as $code) {
foreach ([204, 304] as $code) {
$response = new Response('', $code);
$this->assertTrue($response->isEmpty());
}
@@ -807,7 +834,7 @@ class ResponseTest extends ResponseTestCase
public function testSetEtag()
{
$response = new Response('', 200, array('ETag' => '"12345"'));
$response = new Response('', 200, ['ETag' => '"12345"']);
$response->setEtag();
$this->assertNull($response->headers->get('Etag'), '->setEtag() removes Etags when call with null');
@@ -824,11 +851,11 @@ class ResponseTest extends ResponseTestCase
}
/**
* @expectedException \UnexpectedValueException
* @dataProvider invalidContentProvider
*/
public function testSetContentInvalid($content)
{
$this->expectException('UnexpectedValueException');
$response = new Response();
$response->setContent($content);
}
@@ -837,7 +864,7 @@ class ResponseTest extends ResponseTestCase
{
$response = new Response();
$setters = array(
$setters = [
'setProtocolVersion' => '1.0',
'setCharset' => 'UTF-8',
'setPublic' => null,
@@ -848,29 +875,39 @@ class ResponseTest extends ResponseTestCase
'setSharedMaxAge' => 1,
'setTtl' => 1,
'setClientTtl' => 1,
);
];
foreach ($setters as $setter => $arg) {
$this->assertEquals($response, $response->{$setter}($arg));
}
}
public function testNoDeprecationsAreTriggered()
{
new DefaultResponse();
$this->getMockBuilder(Response::class)->getMock();
// we just need to ensure that subclasses of Response can be created without any deprecations
// being triggered if the subclass does not override any final methods
$this->addToAssertionCount(1);
}
public function validContentProvider()
{
return array(
'obj' => array(new StringableObject()),
'string' => array('Foo'),
'int' => array(2),
);
return [
'obj' => [new StringableObject()],
'string' => ['Foo'],
'int' => [2],
];
}
public function invalidContentProvider()
{
return array(
'obj' => array(new \stdClass()),
'array' => array(array()),
'bool' => array(true, '1'),
);
return [
'obj' => [new \stdClass()],
'array' => [[]],
'bool' => [true, '1'],
];
}
protected function createDateTimeOneHourAgo()
@@ -896,11 +933,11 @@ class ResponseTest extends ResponseTestCase
}
/**
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
*
* @author Fábio Pacheco
* @author Fábio Pacheco
* @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/
public function ianaCodesReasonPhrasesProvider()
{
@@ -910,19 +947,24 @@ class ResponseTest extends ResponseTestCase
$ianaHttpStatusCodes = new \DOMDocument();
libxml_set_streams_context(stream_context_create(array(
'http' => array(
$context = stream_context_create([
'http' => [
'method' => 'GET',
'timeout' => 30,
),
)));
'user_agent' => __METHOD__,
],
]);
$ianaHttpStatusCodes->load('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml');
if (!$rawStatusCodes = file_get_contents('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml', false, $context)) {
$this->markTestSkipped('The IANA server is throttling the list of status codes');
}
$ianaHttpStatusCodes->loadXML($rawStatusCodes);
if (!$ianaHttpStatusCodes->relaxNGValidate(__DIR__.'/schema/http-status-codes.rng')) {
self::fail('Invalid IANA\'s HTTP status code list.');
}
$ianaCodesReasonPhrases = array();
$ianaCodesReasonPhrases = [];
$xpath = new \DOMXPath($ianaHttpStatusCodes);
$xpath->registerNamespace('ns', 'http://www.iana.org/assignments');
@@ -932,16 +974,16 @@ class ResponseTest extends ResponseTestCase
$value = $xpath->query('.//ns:value', $record)->item(0)->nodeValue;
$description = $xpath->query('.//ns:description', $record)->item(0)->nodeValue;
if (\in_array($description, array('Unassigned', '(Unused)'), true)) {
if (\in_array($description, ['Unassigned', '(Unused)'], true)) {
continue;
}
if (preg_match('/^([0-9]+)\s*\-\s*([0-9]+)$/', $value, $matches)) {
for ($value = $matches[1]; $value <= $matches[2]; ++$value) {
$ianaCodesReasonPhrases[] = array($value, $description);
$ianaCodesReasonPhrases[] = [$value, $description];
}
} else {
$ianaCodesReasonPhrases[] = array($value, $description);
$ianaCodesReasonPhrases[] = [$value, $description];
}
}
@@ -964,3 +1006,7 @@ class StringableObject
return 'Foo';
}
}
class DefaultResponse extends Response
{
}
+33 -33
View File
@@ -23,7 +23,7 @@ class ServerBagTest extends TestCase
{
public function testShouldExtractHeadersFromServerArray()
{
$server = array(
$server = [
'SOME_SERVER_VARIABLE' => 'value',
'SOME_SERVER_VARIABLE2' => 'value',
'ROOT' => 'value',
@@ -32,45 +32,45 @@ class ServerBagTest extends TestCase
'HTTP_ETAG' => 'asdf',
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => 'bar',
);
];
$bag = new ServerBag($server);
$this->assertEquals(array(
$this->assertEquals([
'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());
], $bag->getHeaders());
}
public function testHttpPasswordIsOptional()
{
$bag = new ServerBag(array('PHP_AUTH_USER' => 'foo'));
$bag = new ServerBag(['PHP_AUTH_USER' => 'foo']);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => '',
), $bag->getHeaders());
], $bag->getHeaders());
}
public function testHttpBasicAuthWithPhpCgi()
{
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')));
$bag = new ServerBag(['HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')]);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => 'bar',
), $bag->getHeaders());
], $bag->getHeaders());
}
public function testHttpBasicAuthWithPhpCgiBogus()
{
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic_'.base64_encode('foo:bar')));
$bag = new ServerBag(['HTTP_AUTHORIZATION' => 'Basic_'.base64_encode('foo:bar')]);
// Username and passwords should not be set as the header is bogus
$headers = $bag->getHeaders();
@@ -80,41 +80,41 @@ class ServerBagTest extends TestCase
public function testHttpBasicAuthWithPhpCgiRedirect()
{
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word')));
$bag = new ServerBag(['REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word')]);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'),
'PHP_AUTH_USER' => 'username',
'PHP_AUTH_PW' => 'pass:word',
), $bag->getHeaders());
], $bag->getHeaders());
}
public function testHttpBasicAuthWithPhpCgiEmptyPassword()
{
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:')));
$bag = new ServerBag(['HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:')]);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => '',
), $bag->getHeaders());
], $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));
$bag = new ServerBag(['HTTP_AUTHORIZATION' => $digest]);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => $digest,
'PHP_AUTH_DIGEST' => $digest,
), $bag->getHeaders());
], $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));
$bag = new ServerBag(['HTTP_AUTHORIZATION' => $digest]);
// Username and passwords should not be set as the header is bogus
$headers = $bag->getHeaders();
@@ -125,32 +125,32 @@ class ServerBagTest extends TestCase
public function testHttpDigestAuthWithPhpCgiRedirect()
{
$digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $digest));
$bag = new ServerBag(['REDIRECT_HTTP_AUTHORIZATION' => $digest]);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => $digest,
'PHP_AUTH_DIGEST' => $digest,
), $bag->getHeaders());
], $bag->getHeaders());
}
public function testOAuthBearerAuth()
{
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => $headerContent));
$bag = new ServerBag(['HTTP_AUTHORIZATION' => $headerContent]);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => $headerContent,
), $bag->getHeaders());
], $bag->getHeaders());
}
public function testOAuthBearerAuthWithRedirect()
{
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $headerContent));
$bag = new ServerBag(['REDIRECT_HTTP_AUTHORIZATION' => $headerContent]);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => $headerContent,
), $bag->getHeaders());
], $bag->getHeaders());
}
/**
@@ -159,12 +159,12 @@ class ServerBagTest extends TestCase
public function testItDoesNotOverwriteTheAuthorizationHeaderIfItIsAlreadySet()
{
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
$bag = new ServerBag(array('PHP_AUTH_USER' => 'foo', 'HTTP_AUTHORIZATION' => $headerContent));
$bag = new ServerBag(['PHP_AUTH_USER' => 'foo', 'HTTP_AUTHORIZATION' => $headerContent]);
$this->assertEquals(array(
$this->assertEquals([
'AUTHORIZATION' => $headerContent,
'PHP_AUTH_USER' => 'foo',
'PHP_AUTH_PW' => '',
), $bag->getHeaders());
], $bag->getHeaders());
}
}
@@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
*/
class AttributeBagTest extends TestCase
{
private $array = array();
private $array = [];
/**
* @var AttributeBag
@@ -30,21 +30,21 @@ class AttributeBagTest extends TestCase
protected function setUp()
{
$this->array = array(
$this->array = [
'hello' => 'world',
'always' => 'be happy',
'user.login' => 'drak',
'csrf.token' => array(
'csrf.token' => [
'a' => '1234',
'b' => '4321',
),
'category' => array(
'fishing' => array(
],
'category' => [
'fishing' => [
'first' => 'cod',
'second' => 'sole',
),
),
);
],
],
];
$this->bag = new AttributeBag('_sf2');
$this->bag->initialize($this->array);
}
@@ -52,7 +52,7 @@ class AttributeBagTest extends TestCase
protected function tearDown()
{
$this->bag = null;
$this->array = array();
$this->array = [];
}
public function testInitialize()
@@ -60,7 +60,7 @@ class AttributeBagTest extends TestCase
$bag = new AttributeBag();
$bag->initialize($this->array);
$this->assertEquals($this->array, $bag->all());
$array = array('should' => 'change');
$array = ['should' => 'change'];
$bag->initialize($array);
$this->assertEquals($array, $bag->all());
}
@@ -122,7 +122,7 @@ class AttributeBagTest extends TestCase
public function testReplace()
{
$array = array();
$array = [];
$array['name'] = 'jack';
$array['foo.bar'] = 'beep';
$this->bag->replace($array);
@@ -150,22 +150,22 @@ class AttributeBagTest extends TestCase
public function testClear()
{
$this->bag->clear();
$this->assertEquals(array(), $this->bag->all());
$this->assertEquals([], $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),
);
return [
['hello', 'world', true],
['always', 'be happy', true],
['user.login', 'drak', true],
['csrf.token', ['a' => '1234', 'b' => '4321'], true],
['category', ['fishing' => ['first' => 'cod', 'second' => 'sole']], true],
['user2.login', null, false],
['never', null, false],
['bye', null, false],
['bye/for/now', null, false],
];
}
public function testGetIterator()
@@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
*/
class NamespacedAttributeBagTest extends TestCase
{
private $array = array();
private $array = [];
/**
* @var NamespacedAttributeBag
@@ -30,21 +30,21 @@ class NamespacedAttributeBagTest extends TestCase
protected function setUp()
{
$this->array = array(
$this->array = [
'hello' => 'world',
'always' => 'be happy',
'user.login' => 'drak',
'csrf.token' => array(
'csrf.token' => [
'a' => '1234',
'b' => '4321',
),
'category' => array(
'fishing' => array(
],
'category' => [
'fishing' => [
'first' => 'cod',
'second' => 'sole',
),
),
);
],
],
];
$this->bag = new NamespacedAttributeBag('_sf2', '/');
$this->bag->initialize($this->array);
}
@@ -52,7 +52,7 @@ class NamespacedAttributeBagTest extends TestCase
protected function tearDown()
{
$this->bag = null;
$this->array = array();
$this->array = [];
}
public function testInitialize()
@@ -60,7 +60,7 @@ class NamespacedAttributeBagTest extends TestCase
$bag = new NamespacedAttributeBag();
$bag->initialize($this->array);
$this->assertEquals($this->array, $this->bag->all());
$array = array('should' => 'not stick');
$array = ['should' => 'not stick'];
$bag->initialize($array);
// should have remained the same
@@ -139,7 +139,7 @@ class NamespacedAttributeBagTest extends TestCase
public function testReplace()
{
$array = array();
$array = [];
$array['name'] = 'jack';
$array['foo.bar'] = 'beep';
$this->bag->replace($array);
@@ -177,28 +177,28 @@ class NamespacedAttributeBagTest extends TestCase
public function testClear()
{
$this->bag->clear();
$this->assertEquals(array(), $this->bag->all());
$this->assertEquals([], $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),
);
return [
['hello', 'world', true],
['always', 'be happy', true],
['user.login', 'drak', true],
['csrf.token', ['a' => '1234', 'b' => '4321'], true],
['csrf.token/a', '1234', true],
['csrf.token/b', '4321', true],
['category', ['fishing' => ['first' => 'cod', 'second' => 'sole']], true],
['category/fishing', ['first' => 'cod', 'second' => 'sole'], true],
['category/fishing/missing/first', null, false],
['category/fishing/first', 'cod', true],
['category/fishing/second', 'sole', true],
['category/fishing/missing/second', null, false],
['user2.login', null, false],
['never', null, false],
['bye', null, false],
['bye/for/now', null, false],
];
}
}
@@ -26,13 +26,13 @@ class AutoExpireFlashBagTest extends TestCase
*/
private $bag;
protected $array = array();
protected $array = [];
protected function setUp()
{
parent::setUp();
$this->bag = new FlashBag();
$this->array = array('new' => array('notice' => array('A previous flash message')));
$this->array = ['new' => ['notice' => ['A previous flash message']]];
$this->bag->initialize($this->array);
}
@@ -45,21 +45,21 @@ class AutoExpireFlashBagTest extends TestCase
public function testInitialize()
{
$bag = new FlashBag();
$array = array('new' => array('notice' => array('A previous flash message')));
$array = ['new' => ['notice' => ['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'),
));
$this->assertEquals(['A previous flash message'], $bag->peek('notice'));
$array = ['new' => [
'notice' => ['Something else'],
'error' => ['a'],
]];
$bag->initialize($array);
$this->assertEquals(array('Something else'), $bag->peek('notice'));
$this->assertEquals(array('a'), $bag->peek('error'));
$this->assertEquals(['Something else'], $bag->peek('notice'));
$this->assertEquals(['a'], $bag->peek('error'));
}
public function testGetStorageKey()
{
$this->assertEquals('_sf2_flashes', $this->bag->getStorageKey());
$this->assertEquals('_symfony_flashes', $this->bag->getStorageKey());
$attributeBag = new FlashBag('test');
$this->assertEquals('test', $attributeBag->getStorageKey());
}
@@ -73,16 +73,16 @@ class AutoExpireFlashBagTest extends TestCase
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'));
$this->assertEquals([], $this->bag->peek('non_existing'));
$this->assertEquals(['default'], $this->bag->peek('non_existing', ['default']));
$this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
$this->assertEquals(['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'));
$this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
}
public function testHas()
@@ -93,43 +93,43 @@ class AutoExpireFlashBagTest extends TestCase
public function testKeys()
{
$this->assertEquals(array('notice'), $this->bag->keys());
$this->assertEquals(['notice'], $this->bag->keys());
}
public function testPeekAll()
{
$array = array(
'new' => array(
$array = [
'new' => [
'notice' => 'Foo',
'error' => 'Bar',
),
);
],
];
$this->bag->initialize($array);
$this->assertEquals(array(
$this->assertEquals([
'notice' => 'Foo',
'error' => 'Bar',
), $this->bag->peekAll()
], $this->bag->peekAll()
);
$this->assertEquals(array(
$this->assertEquals([
'notice' => 'Foo',
'error' => 'Bar',
), $this->bag->peekAll()
], $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'));
$this->assertEquals([], $this->bag->get('non_existing'));
$this->assertEquals(['default'], $this->bag->get('non_existing', ['default']));
$this->assertEquals(['A previous flash message'], $this->bag->get('notice'));
$this->assertEquals([], $this->bag->get('notice'));
}
public function testSetAll()
{
$this->bag->setAll(array('a' => 'first', 'b' => 'second'));
$this->bag->setAll(['a' => 'first', 'b' => 'second']);
$this->assertFalse($this->bag->has('a'));
$this->assertFalse($this->bag->has('b'));
}
@@ -138,17 +138,17 @@ class AutoExpireFlashBagTest extends TestCase
{
$this->bag->set('notice', 'Foo');
$this->bag->set('error', 'Bar');
$this->assertEquals(array(
'notice' => array('A previous flash message'),
), $this->bag->all()
$this->assertEquals([
'notice' => ['A previous flash message'],
], $this->bag->all()
);
$this->assertEquals(array(), $this->bag->all());
$this->assertEquals([], $this->bag->all());
}
public function testClear()
{
$this->assertEquals(array('notice' => array('A previous flash message')), $this->bag->clear());
$this->assertEquals(['notice' => ['A previous flash message']], $this->bag->clear());
}
public function testDoNotRemoveTheNewFlashesWhenDisplayingTheExistingOnes()
@@ -156,6 +156,6 @@ class AutoExpireFlashBagTest extends TestCase
$this->bag->add('success', 'Something');
$this->bag->all();
$this->assertEquals(array('new' => array('success' => array('Something')), 'display' => array()), $this->array);
$this->assertEquals(['new' => ['success' => ['Something']], 'display' => []], $this->array);
}
}
@@ -26,13 +26,13 @@ class FlashBagTest extends TestCase
*/
private $bag;
protected $array = array();
protected $array = [];
protected function setUp()
{
parent::setUp();
$this->bag = new FlashBag();
$this->array = array('notice' => array('A previous flash message'));
$this->array = ['notice' => ['A previous flash message']];
$this->bag->initialize($this->array);
}
@@ -47,14 +47,14 @@ class FlashBagTest extends TestCase
$bag = new FlashBag();
$bag->initialize($this->array);
$this->assertEquals($this->array, $bag->peekAll());
$array = array('should' => array('change'));
$array = ['should' => ['change']];
$bag->initialize($array);
$this->assertEquals($array, $bag->peekAll());
}
public function testGetStorageKey()
{
$this->assertEquals('_sf2_flashes', $this->bag->getStorageKey());
$this->assertEquals('_symfony_flashes', $this->bag->getStorageKey());
$attributeBag = new FlashBag('test');
$this->assertEquals('test', $attributeBag->getStorageKey());
}
@@ -68,49 +68,49 @@ class FlashBagTest extends TestCase
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'));
$this->assertEquals([], $this->bag->peek('non_existing'));
$this->assertEquals(['default'], $this->bag->peek('not_existing', ['default']));
$this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
$this->assertEquals(['A previous flash message'], $this->bag->peek('notice'));
}
public function testAdd()
{
$tab = array('bar' => 'baz');
$tab = ['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'));
$this->assertEquals(['lorem'], $this->bag->get('string_message'));
$this->assertEquals([new \stdClass()], $this->bag->get('object_message'));
$this->assertEquals([$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'));
$this->assertEquals([], $this->bag->get('non_existing'));
$this->assertEquals(['default'], $this->bag->get('not_existing', ['default']));
$this->assertEquals(['A previous flash message'], $this->bag->get('notice'));
$this->assertEquals([], $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([
'notice' => ['Foo'],
'error' => ['Bar'], ], $this->bag->all()
);
$this->assertEquals(array(), $this->bag->all());
$this->assertEquals([], $this->bag->all());
}
public function testSet()
{
$this->bag->set('notice', 'Foo');
$this->bag->set('notice', 'Bar');
$this->assertEquals(array('Bar'), $this->bag->peek('notice'));
$this->assertEquals(['Bar'], $this->bag->peek('notice'));
}
public function testHas()
@@ -121,7 +121,7 @@ class FlashBagTest extends TestCase
public function testKeys()
{
$this->assertEquals(array('notice'), $this->bag->keys());
$this->assertEquals(['notice'], $this->bag->keys());
}
public function testSetAll()
@@ -130,48 +130,28 @@ class FlashBagTest extends TestCase
$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->bag->setAll(['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());
$this->assertSame(['unique_flash' => 'FooBar'], $this->bag->all());
$this->assertSame([], $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->assertEquals([
'notice' => ['Foo'],
'error' => ['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()
$this->assertEquals([
'notice' => ['Foo'],
'error' => ['Bar'],
], $this->bag->peekAll()
);
}
/**
* @group legacy
*/
public function testLegacyGetIterator()
{
$flashes = array('hello' => 'world', 'beep' => 'boop', 'notice' => 'nope');
foreach ($flashes as $key => $val) {
$this->bag->set($key, $val);
}
$i = 0;
foreach ($this->bag as $key => $val) {
$this->assertEquals(array($flashes[$key]), $val);
++$i;
}
$this->assertEquals(\count($flashes), $i);
$this->assertCount(0, $this->bag->all());
}
}
+56 -13
View File
@@ -15,6 +15,7 @@ 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\SessionBagProxy;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
/**
@@ -127,10 +128,10 @@ class SessionTest extends TestCase
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());
$this->session->replace(['happiness' => 'be good', 'symfony' => 'awesome']);
$this->assertEquals(['happiness' => 'be good', 'symfony' => 'awesome'], $this->session->all());
$this->session->replace([]);
$this->assertEquals([], $this->session->all());
}
/**
@@ -150,16 +151,16 @@ class SessionTest extends TestCase
$this->session->set('hi', 'fabien');
$this->session->set($key, $value);
$this->session->clear();
$this->assertEquals(array(), $this->session->all());
$this->assertEquals([], $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')),
);
return [
['foo', 'bar', ['foo' => 'bar']],
['foo.bar', 'too much beer', ['foo.bar' => 'too much beer']],
['great', 'symfony is great', ['great' => 'symfony is great']],
];
}
/**
@@ -170,14 +171,14 @@ class SessionTest extends TestCase
$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());
$this->assertEquals(['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());
$this->assertEquals([], $this->session->all());
}
public function testMigrate()
@@ -216,7 +217,7 @@ class SessionTest extends TestCase
public function testGetIterator()
{
$attributes = array('hello' => 'world', 'symfony' => 'rocks');
$attributes = ['hello' => 'world', 'symfony' => 'rocks'];
foreach ($attributes as $key => $val) {
$this->session->set($key, $val);
}
@@ -242,4 +243,46 @@ class SessionTest extends TestCase
{
$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());
}
public function testGetBagWithBagImplementingGetBag()
{
$bag = new AttributeBag();
$bag->setName('foo');
$storage = new MockArraySessionStorage();
$storage->registerBag($bag);
$this->assertSame($bag, (new Session($storage))->getBag('foo'));
}
public function testGetBagWithBagNotImplementingGetBag()
{
$data = [];
$bag = new AttributeBag();
$bag->setName('foo');
$storage = new MockArraySessionStorage();
$storage->registerBag(new SessionBagProxy($bag, $data, $usageIndex));
$this->assertSame($bag, (new Session($storage))->getBag('foo'));
}
}
@@ -1,114 +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\LegacyPdoSessionHandler;
/**
* @group legacy
* @group time-sensitive
* @requires extension pdo_sqlite
*/
class LegacyPdoSessionHandlerTest extends TestCase
{
private $pdo;
protected function setUp()
{
parent::setUp();
$this->pdo = new \PDO('sqlite::memory:');
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)';
$this->pdo->exec($sql);
}
public function testIncompleteOptions()
{
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
$storage = new LegacyPdoSessionHandler($this->pdo, array());
}
public function testWrongPdoErrMode()
{
$pdo = new \PDO('sqlite::memory:');
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
$pdo->exec('CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)');
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
$storage = new LegacyPdoSessionHandler($pdo, array('db_table' => 'sessions'));
}
public function testWrongTableOptionsWrite()
{
$storage = new LegacyPdoSessionHandler($this->pdo, array('db_table' => 'bad_name'));
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('RuntimeException');
$storage->write('foo', 'bar');
}
public function testWrongTableOptionsRead()
{
$storage = new LegacyPdoSessionHandler($this->pdo, array('db_table' => 'bad_name'));
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('RuntimeException');
$storage->read('foo');
}
public function testWriteRead()
{
$storage = new LegacyPdoSessionHandler($this->pdo, array('db_table' => 'sessions'));
$storage->write('foo', 'bar');
$this->assertEquals('bar', $storage->read('foo'), 'written value can be read back correctly');
}
public function testMultipleInstances()
{
$storage1 = new LegacyPdoSessionHandler($this->pdo, array('db_table' => 'sessions'));
$storage1->write('foo', 'bar');
$storage2 = new LegacyPdoSessionHandler($this->pdo, array('db_table' => 'sessions'));
$this->assertEquals('bar', $storage2->read('foo'), 'values persist between instances');
}
public function testSessionDestroy()
{
$storage = new LegacyPdoSessionHandler($this->pdo, array('db_table' => 'sessions'));
$storage->write('foo', 'bar');
$this->assertCount(1, $this->pdo->query('SELECT * FROM sessions')->fetchAll());
$storage->destroy('foo');
$this->assertCount(0, $this->pdo->query('SELECT * FROM sessions')->fetchAll());
}
public function testSessionGC()
{
$storage = new LegacyPdoSessionHandler($this->pdo, array('db_table' => 'sessions'));
$storage->write('foo', 'bar');
$storage->write('baz', 'bar');
$this->assertCount(2, $this->pdo->query('SELECT * FROM sessions')->fetchAll());
$storage->gc(-1);
$this->assertCount(0, $this->pdo->query('SELECT * FROM sessions')->fetchAll());
}
public function testGetConnection()
{
$storage = new LegacyPdoSessionHandler($this->pdo, array('db_table' => 'sessions'), array());
$method = new \ReflectionMethod($storage, 'getConnection');
$method->setAccessible(true);
$this->assertInstanceOf('\PDO', $method->invoke($storage));
}
}
@@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHand
/**
* @requires extension memcache
* @group time-sensitive
* @group legacy
*/
class MemcacheSessionHandlerTest extends TestCase
{
@@ -40,7 +41,7 @@ class MemcacheSessionHandlerTest extends TestCase
$this->memcache = $this->getMockBuilder('Memcache')->getMock();
$this->storage = new MemcacheSessionHandler(
$this->memcache,
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
['prefix' => self::PREFIX, 'expiretime' => self::TTL]
);
}
@@ -78,7 +79,7 @@ class MemcacheSessionHandlerTest extends TestCase
->expects($this->once())
->method('set')
->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2))
->will($this->returnValue(true))
->willReturn(true)
;
$this->assertTrue($this->storage->write('id', 'data'));
@@ -90,7 +91,7 @@ class MemcacheSessionHandlerTest extends TestCase
->expects($this->once())
->method('delete')
->with(self::PREFIX.'id')
->will($this->returnValue(true))
->willReturn(true)
;
$this->assertTrue($this->storage->destroy('id'));
@@ -116,12 +117,12 @@ class MemcacheSessionHandlerTest extends TestCase
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),
);
return [
[['prefix' => 'session'], true],
[['expiretime' => 100], true],
[['prefix' => 'session', 'expiretime' => 200], true],
[['expiretime' => 100, 'foo' => 'bar'], false],
];
}
public function testGetConnection()
@@ -45,7 +45,7 @@ class MemcachedSessionHandlerTest extends TestCase
$this->memcached = $this->getMockBuilder('Memcached')->getMock();
$this->storage = new MemcachedSessionHandler(
$this->memcached,
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
['prefix' => self::PREFIX, 'expiretime' => self::TTL]
);
}
@@ -63,6 +63,12 @@ class MemcachedSessionHandlerTest extends TestCase
public function testCloseSession()
{
$this->memcached
->expects($this->once())
->method('quit')
->willReturn(true)
;
$this->assertTrue($this->storage->close());
}
@@ -83,7 +89,7 @@ class MemcachedSessionHandlerTest extends TestCase
->expects($this->once())
->method('set')
->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2))
->will($this->returnValue(true))
->willReturn(true)
;
$this->assertTrue($this->storage->write('id', 'data'));
@@ -95,7 +101,7 @@ class MemcachedSessionHandlerTest extends TestCase
->expects($this->once())
->method('delete')
->with(self::PREFIX.'id')
->will($this->returnValue(true))
->willReturn(true)
;
$this->assertTrue($this->storage->destroy('id'));
@@ -121,12 +127,12 @@ class MemcachedSessionHandlerTest extends TestCase
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),
);
return [
[['prefix' => 'session'], true],
[['expiretime' => 100], true],
[['prefix' => 'session', 'expiretime' => 200], true],
[['expiretime' => 100, 'foo' => 'bar'], false],
];
}
public function testGetConnection()
@@ -11,17 +11,19 @@
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
use PHPUnit\Framework\MockObject\MockObject;
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
* @var MockObject
*/
private $mongo;
private $storage;
@@ -49,32 +51,28 @@ class MongoDbSessionHandlerTest extends TestCase
->disableOriginalConstructor()
->getMock();
$this->options = array(
$this->options = [
'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()
{
$this->expectException('InvalidArgumentException');
new MongoDbSessionHandler(new \stdClass(), $this->options);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testConstructorShouldThrowExceptionForMissingOptions()
{
new MongoDbSessionHandler($this->mongo, array());
$this->expectException('InvalidArgumentException');
new MongoDbSessionHandler($this->mongo, []);
}
public function testOpenMethodAlwaysReturnTrue()
@@ -94,9 +92,7 @@ class MongoDbSessionHandlerTest extends TestCase
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$that = $this;
->willReturn($collection);
// defining the timeout before the actual method call
// allows to test for "greater than" values in the $criteria
@@ -104,35 +100,35 @@ class MongoDbSessionHandlerTest extends TestCase
$collection->expects($this->once())
->method('findOne')
->will($this->returnCallback(function ($criteria) use ($that, $testTimeout) {
$that->assertArrayHasKey($that->options['id_field'], $criteria);
$that->assertEquals($criteria[$that->options['id_field']], 'foo');
->willReturnCallback(function ($criteria) use ($testTimeout) {
$this->assertArrayHasKey($this->options['id_field'], $criteria);
$this->assertEquals('foo', $criteria[$this->options['id_field']]);
$that->assertArrayHasKey($that->options['expiry_field'], $criteria);
$that->assertArrayHasKey('$gte', $criteria[$that->options['expiry_field']]);
$this->assertArrayHasKey($this->options['expiry_field'], $criteria);
$this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
if (phpversion('mongodb')) {
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$that->options['expiry_field']]['$gte']);
$that->assertGreaterThanOrEqual(round((string) $criteria[$that->options['expiry_field']]['$gte'] / 1000), $testTimeout);
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
$this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout);
} else {
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$gte']);
$that->assertGreaterThanOrEqual($criteria[$that->options['expiry_field']]['$gte']->sec, $testTimeout);
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
$this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
}
$fields = array(
$that->options['id_field'] => 'foo',
);
$fields = [
$this->options['id_field'] => 'foo',
];
if (phpversion('mongodb')) {
$fields[$that->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
$fields[$that->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
$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[$that->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
$fields[$that->options['id_field']] = new \MongoDate();
$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'));
}
@@ -144,53 +140,52 @@ class MongoDbSessionHandlerTest extends TestCase
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
->willReturn($collection);
$that = $this;
$data = array();
$data = [];
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->once())
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
$that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
->willReturnCallback(function ($criteria, $updateData, $options) use (&$data) {
$this->assertEquals([$this->options['id_field'] => 'foo'], $criteria);
if (phpversion('mongodb')) {
$that->assertEquals(array('upsert' => true), $options);
$this->assertEquals(['upsert' => true], $options);
} else {
$that->assertEquals(array('upsert' => true, 'multiple' => false), $options);
$this->assertEquals(['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')) {
$that->assertEquals('bar', $data[$that->options['data_field']]->getData());
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['expiry_field']]);
$that->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$that->options['expiry_field']] / 1000));
$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 {
$that->assertEquals('bar', $data[$that->options['data_field']]->bin);
$that->assertInstanceOf('MongoDate', $data[$that->options['time_field']]);
$that->assertInstanceOf('MongoDate', $data[$that->options['expiry_field']]);
$that->assertGreaterThanOrEqual($expectedExpiry, $data[$that->options['expiry_field']]->sec);
$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(
$this->options = [
'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);
@@ -199,37 +194,36 @@ class MongoDbSessionHandlerTest extends TestCase
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
->willReturn($collection);
$that = $this;
$data = array();
$data = [];
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->once())
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) {
$that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria);
->willReturnCallback(function ($criteria, $updateData, $options) use (&$data) {
$this->assertEquals([$this->options['id_field'] => 'foo'], $criteria);
if (phpversion('mongodb')) {
$that->assertEquals(array('upsert' => true), $options);
$this->assertEquals(['upsert' => true], $options);
} else {
$that->assertEquals(array('upsert' => true, 'multiple' => false), $options);
$this->assertEquals(['upsert' => true, 'multiple' => false], $options);
}
$data = $updateData['$set'];
}));
});
$this->assertTrue($this->storage->write('foo', 'bar'));
if (phpversion('mongodb')) {
$that->assertEquals('bar', $data[$that->options['data_field']]->getData());
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['time_field']]);
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$that->options['expiry_field']]);
$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 {
$that->assertEquals('bar', $data[$that->options['data_field']]->bin);
$that->assertInstanceOf('MongoDate', $data[$that->options['time_field']]);
$that->assertInstanceOf('MongoDate', $data[$that->options['expiry_field']]);
$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']]);
}
}
@@ -240,17 +234,17 @@ class MongoDbSessionHandlerTest extends TestCase
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
->willReturn($collection);
$data = array();
$data = [];
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
$collection->expects($this->exactly(2))
->method($methodName)
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
->willReturnCallback(function ($criteria, $updateData, $options) use (&$data) {
$data = $updateData;
}));
});
$this->storage->write('foo', 'bar');
$this->storage->write('foo', 'foobar');
@@ -269,13 +263,13 @@ class MongoDbSessionHandlerTest extends TestCase
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
->willReturn($collection);
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
$collection->expects($this->once())
->method($methodName)
->with(array($this->options['id_field'] => 'foo'));
->with([$this->options['id_field'] => 'foo']);
$this->assertTrue($this->storage->destroy('foo'));
}
@@ -287,23 +281,21 @@ class MongoDbSessionHandlerTest extends TestCase
$this->mongo->expects($this->once())
->method('selectCollection')
->with($this->options['database'], $this->options['collection'])
->will($this->returnValue($collection));
$that = $this;
->willReturn($collection);
$methodName = phpversion('mongodb') ? 'deleteMany' : 'remove';
$collection->expects($this->once())
->method($methodName)
->will($this->returnCallback(function ($criteria) use ($that) {
->willReturnCallback(function ($criteria) {
if (phpversion('mongodb')) {
$that->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$that->options['expiry_field']]['$lt']);
$that->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$that->options['expiry_field']]['$lt'] / 1000));
$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 {
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$lt']);
$that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['expiry_field']]['$lt']->sec);
$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));
}
@@ -27,15 +27,10 @@ class NativeFileSessionHandlerTest extends TestCase
{
public function testConstruct()
{
$storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir()));
$storage = new NativeSessionStorage(['name' => 'TESTING'], new NativeFileSessionHandler(sys_get_temp_dir()));
if (\PHP_VERSION_ID < 50400) {
$this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName());
$this->assertEquals('files', ini_get('session.save_handler'));
} else {
$this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName());
$this->assertEquals('user', ini_get('session.save_handler'));
}
$this->assertEquals('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'));
@@ -46,9 +41,9 @@ class NativeFileSessionHandlerTest extends TestCase
*/
public function testConstructSavePath($savePath, $expectedSavePath, $path)
{
$handler = new NativeFileSessionHandler($savePath);
new NativeFileSessionHandler($savePath);
$this->assertEquals($expectedSavePath, ini_get('session.save_path'));
$this->assertTrue(is_dir(realpath($path)));
$this->assertDirectoryExists(realpath($path));
rmdir($path);
}
@@ -57,25 +52,23 @@ class NativeFileSessionHandlerTest extends TestCase
{
$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"),
);
return [
["$base/foo", "$base/foo", "$base/foo"],
["5;$base/foo", "5;$base/foo", "$base/foo"],
["5;0600;$base/foo", "5;0600;$base/foo", "$base/foo"],
];
}
/**
* @expectedException \InvalidArgumentException
*/
public function testConstructException()
{
$handler = new NativeFileSessionHandler('something;invalid;with;too-many-args');
$this->expectException('InvalidArgumentException');
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());
new NativeSessionStorage(['name' => 'TESTING'], new NativeFileSessionHandler());
$this->assertEquals($path, ini_get('session.save_path'));
}
@@ -21,21 +21,18 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandle
*
* @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();
// note for PHPUnit optimisers - the use of assertTrue/False
// here is deliberate since the tests do not require the classes to exist - drak
if (\PHP_VERSION_ID < 50400) {
$this->assertFalse($handler instanceof \SessionHandler);
$this->assertTrue($handler instanceof NativeSessionHandler);
} else {
$this->assertTrue($handler instanceof \SessionHandler);
$this->assertTrue($handler instanceof NativeSessionHandler);
}
$this->assertInstanceOf('SessionHandler', $handler);
$this->assertTrue($handler instanceof NativeSessionHandler);
}
}
@@ -28,7 +28,7 @@ class NullSessionHandlerTest extends TestCase
{
public function testSaveHandlers()
{
$storage = $this->getStorage();
$this->getStorage();
$this->assertEquals('user', ini_get('session.save_handler'));
}
@@ -54,6 +54,6 @@ class NullSessionHandlerTest extends TestCase
public function getStorage()
{
return new NativeSessionStorage(array(), new NullSessionHandler());
return new NativeSessionStorage([], new NullSessionHandler());
}
}
@@ -48,34 +48,28 @@ class PdoSessionHandlerTest extends TestCase
return $pdo;
}
/**
* @expectedException \InvalidArgumentException
*/
public function testWrongPdoErrMode()
{
$this->expectException('InvalidArgumentException');
$pdo = $this->getMemorySqlitePdo();
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
$storage = new PdoSessionHandler($pdo);
new PdoSessionHandler($pdo);
}
/**
* @expectedException \RuntimeException
*/
public function testInexistentTable()
{
$storage = new PdoSessionHandler($this->getMemorySqlitePdo(), array('db_table' => 'inexistent_table'));
$this->expectException('RuntimeException');
$storage = new PdoSessionHandler($this->getMemorySqlitePdo(), ['db_table' => 'inexistent_table']);
$storage->open('', 'sid');
$storage->read('id');
$storage->write('id', 'data');
$storage->close();
}
/**
* @expectedException \RuntimeException
*/
public function testCreateTableTwice()
{
$this->expectException('RuntimeException');
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
$storage->createTable();
}
@@ -147,7 +141,7 @@ class PdoSessionHandlerTest extends TestCase
$stream = $this->createStream($content);
$pdo->prepareResult->expects($this->once())->method('fetchAll')
->will($this->returnValue(array(array($stream, 42, time()))));
->willReturn([[$stream, 42, time()]]);
$storage = new PdoSessionHandler($pdo);
$result = $storage->read('foo');
@@ -160,6 +154,9 @@ class PdoSessionHandlerTest extends TestCase
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 (filter_var(ini_get('session.use_strict_mode'), \FILTER_VALIDATE_BOOLEAN)) {
$this->markTestSkipped('Strict mode needs no locking for new sessions.');
}
$pdo = new MockPdo('pgsql');
$selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
@@ -174,14 +171,14 @@ class PdoSessionHandlerTest extends TestCase
$exception = null;
$selectStmt->expects($this->atLeast(2))->method('fetchAll')
->will($this->returnCallback(function () use (&$exception, $stream) {
return $exception ? array(array($stream, 42, time())) : array();
}));
->willReturnCallback(function () use (&$exception, $stream) {
return $exception ? [[$stream, 42, time()]] : [];
});
$insertStmt->expects($this->once())->method('execute')
->will($this->returnCallback(function () use (&$exception) {
->willReturnCallback(function () use (&$exception) {
throw $exception = new \PDOException('', '23');
}));
});
$storage = new PdoSessionHandler($pdo);
$result = $storage->read('foo');
@@ -321,6 +318,41 @@ class PdoSessionHandlerTest extends TestCase
$this->assertInstanceOf('\PDO', $method->invoke($storage));
}
/**
* @dataProvider provideUrlDsnPairs
*/
public function testUrlDsn($url, $expectedDsn, $expectedUser = null, $expectedPassword = null)
{
$storage = new PdoSessionHandler($url);
$reflection = new \ReflectionClass(PdoSessionHandler::class);
foreach (['dsn' => $expectedDsn, 'username' => $expectedUser, 'password' => $expectedPassword] as $property => $expectedValue) {
if (!isset($expectedValue)) {
continue;
}
$property = $reflection->getProperty($property);
$property->setAccessible(true);
$this->assertSame($expectedValue, $property->getValue($storage));
}
}
public function provideUrlDsnPairs()
{
yield ['mysql://localhost/test', 'mysql:host=localhost;dbname=test;'];
yield ['mysql://localhost:56/test', 'mysql:host=localhost;port=56;dbname=test;'];
yield ['mysql2://root:pwd@localhost/test', 'mysql:host=localhost;dbname=test;', 'root', 'pwd'];
yield ['postgres://localhost/test', 'pgsql:host=localhost;dbname=test;'];
yield ['postgresql://localhost:5634/test', 'pgsql:host=localhost;port=5634;dbname=test;'];
yield ['postgres://root:pwd@localhost/test', 'pgsql:host=localhost;dbname=test;', 'root', 'pwd'];
yield 'sqlite relative path' => ['sqlite://localhost/tmp/test', 'sqlite:tmp/test'];
yield 'sqlite absolute path' => ['sqlite://localhost//tmp/test', 'sqlite:/tmp/test'];
yield 'sqlite relative path without host' => ['sqlite:///tmp/test', 'sqlite:tmp/test'];
yield 'sqlite absolute path without host' => ['sqlite3:////tmp/test', 'sqlite:/tmp/test'];
yield ['sqlite://localhost/:memory:', 'sqlite::memory:'];
yield ['mssql://localhost/test', 'sqlsrv:server=localhost;Database=test'];
yield ['mssql://localhost:56/test', 'sqlsrv:server=localhost,56;Database=test'];
}
private function createStream($content)
{
$stream = tmpfile();
@@ -356,7 +388,7 @@ class MockPdo extends \PDO
return parent::getAttribute($attribute);
}
public function prepare($statement, $driverOptions = array())
public function prepare($statement, $driverOptions = [])
{
return \is_callable($this->prepareResult)
? \call_user_func($this->prepareResult, $statement, $driverOptions)
@@ -16,6 +16,8 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHa
/**
* @author Adrien Brault <adrien.brault@gmail.com>
*
* @group legacy
*/
class WriteCheckSessionHandlerTest extends TestCase
{
@@ -28,7 +30,7 @@ class WriteCheckSessionHandlerTest extends TestCase
->expects($this->once())
->method('close')
->with()
->will($this->returnValue(true))
->willReturn(true)
;
$this->assertTrue($writeCheckSessionHandler->close());
@@ -43,7 +45,7 @@ class WriteCheckSessionHandlerTest extends TestCase
->expects($this->once())
->method('write')
->with('foo', 'bar')
->will($this->returnValue(true))
->willReturn(true)
;
$this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
@@ -58,7 +60,7 @@ class WriteCheckSessionHandlerTest extends TestCase
->expects($this->once())
->method('read')
->with('foo')
->will($this->returnValue('bar'))
->willReturn('bar')
;
$wrappedSessionHandlerMock
@@ -79,14 +81,14 @@ class WriteCheckSessionHandlerTest extends TestCase
->expects($this->once())
->method('read')
->with('foo')
->will($this->returnValue('bar'))
->willReturn('bar')
;
$wrappedSessionHandlerMock
->expects($this->once())
->method('write')
->with('foo', 'baZZZ')
->will($this->returnValue(true))
->willReturn(true)
;
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
@@ -26,26 +26,26 @@ class MetadataBagTest extends TestCase
*/
protected $bag;
protected $array = array();
protected $array = [];
protected function setUp()
{
parent::setUp();
$this->bag = new MetadataBag();
$this->array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 0);
$this->array = [MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 0];
$this->bag->initialize($this->array);
}
protected function tearDown()
{
$this->array = array();
$this->array = [];
$this->bag = null;
parent::tearDown();
}
public function testInitialize()
{
$sessionMetadata = array();
$sessionMetadata = [];
$bag1 = new MetadataBag();
$bag1->initialize($sessionMetadata);
@@ -82,7 +82,7 @@ class MetadataBagTest extends TestCase
public function testGetLifetime()
{
$bag = new MetadataBag();
$array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 1000);
$array = [MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 1000];
$bag->initialize($array);
$this->assertEquals(1000, $bag->getLifetime());
}
@@ -111,11 +111,11 @@ class MetadataBagTest extends TestCase
$timeStamp = time();
$created = $timeStamp - 15;
$sessionMetadata = array(
$sessionMetadata = [
MetadataBag::CREATED => $created,
MetadataBag::UPDATED => $created,
MetadataBag::LIFETIME => 1000,
);
];
$bag->initialize($sessionMetadata);
$this->assertEquals($created, $sessionMetadata[MetadataBag::UPDATED]);
@@ -127,11 +127,11 @@ class MetadataBagTest extends TestCase
$timeStamp = time();
$created = $timeStamp - 45;
$sessionMetadata = array(
$sessionMetadata = [
MetadataBag::CREATED => $created,
MetadataBag::UPDATED => $created,
MetadataBag::LIFETIME => 1000,
);
];
$bag->initialize($sessionMetadata);
$this->assertEquals($timeStamp, $sessionMetadata[MetadataBag::UPDATED]);
@@ -45,10 +45,10 @@ class MockArraySessionStorageTest extends TestCase
$this->attributes = new AttributeBag();
$this->flashes = new FlashBag();
$this->data = array(
$this->attributes->getStorageKey() => array('foo' => 'bar'),
$this->flashes->getStorageKey() => array('notice' => 'hello'),
);
$this->data = [
$this->attributes->getStorageKey() => ['foo' => 'bar'],
$this->flashes->getStorageKey() => ['notice' => 'hello'],
];
$this->storage = new MockArraySessionStorage();
$this->storage->registerBag($this->flashes);
@@ -80,14 +80,14 @@ class MockArraySessionStorageTest extends TestCase
$id = $this->storage->getId();
$this->storage->regenerate();
$this->assertNotEquals($id, $this->storage->getId());
$this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all());
$this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll());
$this->assertEquals(['foo' => 'bar'], $this->storage->getBag('attributes')->all());
$this->assertEquals(['notice' => 'hello'], $this->storage->getBag('flashes')->peekAll());
$id = $this->storage->getId();
$this->storage->regenerate(true);
$this->assertNotEquals($id, $this->storage->getId());
$this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all());
$this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll());
$this->assertEquals(['foo' => 'bar'], $this->storage->getBag('attributes')->all());
$this->assertEquals(['notice' => 'hello'], $this->storage->getBag('flashes')->peekAll());
}
public function testGetId()
@@ -101,8 +101,8 @@ class MockArraySessionStorageTest extends TestCase
{
$this->storage->clear();
$this->assertSame(array(), $this->storage->getBag('attributes')->all());
$this->assertSame(array(), $this->storage->getBag('flashes')->peekAll());
$this->assertSame([], $this->storage->getBag('attributes')->all());
$this->assertSame([], $this->storage->getBag('flashes')->peekAll());
}
public function testClearStartsSession()
@@ -121,11 +121,9 @@ class MockArraySessionStorageTest extends TestCase
$this->assertTrue($storage->isStarted());
}
/**
* @expectedException \RuntimeException
*/
public function testUnstartedSave()
{
$this->expectException('RuntimeException');
$this->storage->save();
}
}
@@ -41,12 +41,12 @@ class MockFileSessionStorageTest extends TestCase
protected function tearDown()
{
array_map('unlink', glob($this->sessionDir.'/*'));
if (is_dir($this->sessionDir)) {
@rmdir($this->sessionDir);
}
$this->sessionDir = null;
$this->storage = null;
array_map('unlink', glob($this->sessionDir.'/*.session'));
if (is_dir($this->sessionDir)) {
rmdir($this->sessionDir);
}
}
public function testStart()
@@ -91,7 +91,7 @@ class MockFileSessionStorageTest extends TestCase
$storage->start();
$this->assertEquals('108', $storage->getBag('attributes')->get('new'));
$this->assertTrue($storage->getBag('flashes')->has('newkey'));
$this->assertEquals(array('test'), $storage->getBag('flashes')->peek('newkey'));
$this->assertEquals(['test'], $storage->getBag('flashes')->peek('newkey'));
}
public function testMultipleInstances()
@@ -107,11 +107,9 @@ class MockFileSessionStorageTest extends TestCase
$this->assertEquals('bar', $storage2->getBag('attributes')->get('foo'), 'values persist between instances');
}
/**
* @expectedException \RuntimeException
*/
public function testSaveWithoutStart()
{
$this->expectException('RuntimeException');
$storage1 = $this->getStorage();
$storage1->save();
}
@@ -14,10 +14,9 @@ namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
/**
@@ -37,7 +36,7 @@ class NativeSessionStorageTest extends TestCase
protected function setUp()
{
$this->iniSet('session.save_handler', 'files');
$this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test');
$this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sftest');
if (!is_dir($this->savePath)) {
mkdir($this->savePath);
}
@@ -48,7 +47,7 @@ class NativeSessionStorageTest extends TestCase
session_write_close();
array_map('unlink', glob($this->savePath.'/*'));
if (is_dir($this->savePath)) {
rmdir($this->savePath);
@rmdir($this->savePath);
}
$this->savePath = null;
@@ -57,7 +56,7 @@ class NativeSessionStorageTest extends TestCase
/**
* @return NativeSessionStorage
*/
protected function getStorage(array $options = array())
protected function getStorage(array $options = [])
{
$storage = new NativeSessionStorage($options);
$storage->registerBag(new AttributeBag());
@@ -73,20 +72,16 @@ class NativeSessionStorageTest extends TestCase
$this->assertSame($bag, $storage->getBag($bag->getName()));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRegisterBagException()
{
$this->expectException('InvalidArgumentException');
$storage = $this->getStorage();
$storage->getBag('non_existing');
}
/**
* @expectedException \LogicException
*/
public function testRegisterBagForAStartedSessionThrowsException()
{
$this->expectException('LogicException');
$storage = $this->getStorage();
$storage->start();
$storage->registerBag(new AttributeBag());
@@ -99,7 +94,7 @@ class NativeSessionStorageTest extends TestCase
$storage->start();
$id = $storage->getId();
$this->assertInternalType('string', $id);
$this->assertIsString($id);
$this->assertNotSame('', $id);
$storage->save();
@@ -128,6 +123,19 @@ class NativeSessionStorageTest extends TestCase
$this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
}
public function testRegenerateWithCustomLifetime()
{
$storage = $this->getStorage();
$storage->start();
$id = $storage->getId();
$lifetime = 999999;
$storage->getBag('attributes')->set('legs', 11);
$storage->regenerate(false, $lifetime);
$this->assertNotEquals($id, $storage->getId());
$this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
$this->assertEquals($lifetime, ini_get('session.cookie_lifetime'));
}
public function testSessionGlobalIsUpToDateAfterIdRegeneration()
{
$storage = $this->getStorage();
@@ -150,7 +158,7 @@ class NativeSessionStorageTest extends TestCase
{
$this->iniSet('session.cache_limiter', 'nocache');
$storage = new NativeSessionStorage();
new NativeSessionStorage();
$this->assertEquals('', ini_get('session.cache_limiter'));
}
@@ -158,23 +166,27 @@ class NativeSessionStorageTest extends TestCase
{
$this->iniSet('session.cache_limiter', 'nocache');
$storage = new NativeSessionStorage(array('cache_limiter' => 'public'));
new NativeSessionStorage(['cache_limiter' => 'public']);
$this->assertEquals('public', ini_get('session.cache_limiter'));
}
public function testCookieOptions()
{
$options = array(
$options = [
'cookie_lifetime' => 123456,
'cookie_path' => '/my/cookie/path',
'cookie_domain' => 'symfony.example.com',
'cookie_secure' => true,
'cookie_httponly' => false,
);
];
if (\PHP_VERSION_ID >= 70300) {
$options['cookie_samesite'] = 'lax';
}
$this->getStorage($options);
$temp = session_get_cookie_params();
$gco = array();
$gco = [];
foreach ($temp as $key => $value) {
$gco['cookie_'.$key] = $value;
@@ -189,10 +201,10 @@ class NativeSessionStorageTest extends TestCase
$this->markTestSkipped('HHVM is not handled in this test case.');
}
$options = array(
$options = [
'url_rewriter.tags' => 'a=href',
'cache_expire' => '200',
);
];
$this->getStorage($options);
@@ -200,41 +212,14 @@ class NativeSessionStorageTest extends TestCase
$this->assertSame('200', ini_get('session.cache_expire'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testSetSaveHandlerException()
{
$this->expectException('InvalidArgumentException');
$storage = $this->getStorage();
$storage->setSaveHandler(new \stdClass());
}
public function testSetSaveHandler53()
{
if (\PHP_VERSION_ID >= 50400) {
$this->markTestSkipped('Test skipped, for PHP 5.3 only.');
}
$this->iniSet('session.save_handler', 'files');
$storage = $this->getStorage();
$storage->setSaveHandler();
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
$storage->setSaveHandler(null);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new NativeSessionHandler());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new NullSessionHandler());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new NativeProxy());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
}
/**
* @requires PHP 5.4
*/
public function testSetSaveHandler54()
public function testSetSaveHandler()
{
$this->iniSet('session.save_handler', 'files');
$storage = $this->getStorage();
@@ -242,9 +227,9 @@ class NativeSessionStorageTest extends TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(null);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new SessionHandlerProxy(new NativeSessionHandler()));
$storage->setSaveHandler(new SessionHandlerProxy(new NativeFileSessionHandler()));
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new NativeSessionHandler());
$storage->setSaveHandler(new NativeFileSessionHandler());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
@@ -252,11 +237,9 @@ class NativeSessionStorageTest extends TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
}
/**
* @expectedException \RuntimeException
*/
public function testStartedOutside()
public function testStarted()
{
$this->expectException('RuntimeException');
$storage = $this->getStorage();
$this->assertFalse($storage->getSaveHandler()->isActive());
@@ -264,10 +247,8 @@ class NativeSessionStorageTest extends TestCase
session_start();
$this->assertTrue(isset($_SESSION));
if (\PHP_VERSION_ID >= 50400) {
// this only works in PHP >= 5.4 where session_status is available
$this->assertTrue($storage->getSaveHandler()->isActive());
}
$this->assertTrue($storage->getSaveHandler()->isActive());
// PHP session might have started, but the storage driver has not, so false is correct here
$this->assertFalse($storage->isStarted());
@@ -288,9 +269,6 @@ class NativeSessionStorageTest extends TestCase
$this->assertSame(7, $storage->getBag('attributes')->get('lucky'), 'Data still available');
}
/**
* @requires PHP 5.4
*/
public function testCanCreateNativeSessionStorageWhenSessionAlreadyStarted()
{
session_start();
@@ -300,23 +278,17 @@ class NativeSessionStorageTest extends TestCase
$this->addToAssertionCount(1);
}
/**
* @requires PHP 5.4
*/
public function testSetSessionOptionsOnceSessionStartedIsIgnored()
{
session_start();
$this->getStorage(array(
$this->getStorage([
'name' => 'something-else',
));
]);
// Assert no exception has been thrown by `getStorage()`
$this->addToAssertionCount(1);
}
/**
* @requires PHP 5.4
*/
public function testGetBagsOnceSessionStartedIsIgnored()
{
session_start();
@@ -43,7 +43,7 @@ class PhpBridgeSessionStorageTest extends TestCase
session_write_close();
array_map('unlink', glob($this->savePath.'/*'));
if (is_dir($this->savePath)) {
rmdir($this->savePath);
@rmdir($this->savePath);
}
$this->savePath = null;
@@ -60,34 +60,7 @@ class PhpBridgeSessionStorageTest extends TestCase
return $storage;
}
public function testPhpSession53()
{
if (\PHP_VERSION_ID >= 50400) {
$this->markTestSkipped('Test skipped, for PHP 5.3 only.');
}
$storage = $this->getStorage();
$this->assertFalse(isset($_SESSION));
$this->assertFalse($storage->getSaveHandler()->isActive());
session_start();
$this->assertTrue(isset($_SESSION));
// in PHP 5.3 we cannot reliably tell if a session has started
$this->assertFalse($storage->getSaveHandler()->isActive());
// PHP session might have started, but the storage driver has not, so false is correct here
$this->assertFalse($storage->isStarted());
$key = $storage->getMetadataBag()->getStorageKey();
$this->assertArrayNotHasKey($key, $_SESSION);
$storage->start();
$this->assertArrayHasKey($key, $_SESSION);
}
/**
* @requires PHP 5.4
*/
public function testPhpSession54()
public function testPhpSession()
{
$storage = $this->getStorage();
@@ -114,10 +87,10 @@ class PhpBridgeSessionStorageTest extends TestCase
$_SESSION['drak'] = 'loves symfony';
$storage->getBag('attributes')->set('symfony', 'greatness');
$key = $storage->getBag('attributes')->getStorageKey();
$this->assertEquals($_SESSION[$key], array('symfony' => 'greatness'));
$this->assertEquals($_SESSION['drak'], 'loves symfony');
$this->assertEquals(['symfony' => 'greatness'], $_SESSION[$key]);
$this->assertEquals('loves symfony', $_SESSION['drak']);
$storage->clear();
$this->assertEquals($_SESSION[$key], array());
$this->assertEquals($_SESSION['drak'], 'loves symfony');
$this->assertEquals([], $_SESSION[$key]);
$this->assertEquals('loves symfony', $_SESSION['drak']);
}
}
@@ -13,39 +13,7 @@ namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
// Note until PHPUnit_Mock_Objects 1.2 is released you cannot mock abstracts due to
// https://github.com/sebastianbergmann/phpunit-mock-objects/issues/73
class ConcreteProxy extends AbstractProxy
{
}
class ConcreteSessionHandlerInterfaceProxy extends AbstractProxy implements \SessionHandlerInterface
{
public function open($savePath, $sessionName)
{
}
public function close()
{
}
public function read($id)
{
}
public function write($id, $data)
{
}
public function destroy($id)
{
}
public function gc($maxlifetime)
{
}
}
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
/**
* Test class for AbstractProxy.
@@ -61,7 +29,7 @@ class AbstractProxyTest extends TestCase
protected function setUp()
{
$this->proxy = new ConcreteProxy();
$this->proxy = $this->getMockForAbstractClass(AbstractProxy::class);
}
protected function tearDown()
@@ -77,7 +45,7 @@ class AbstractProxyTest extends TestCase
public function testIsSessionHandlerInterface()
{
$this->assertFalse($this->proxy->isSessionHandlerInterface());
$sh = new ConcreteSessionHandlerInterfaceProxy();
$sh = new SessionHandlerProxy(new \SessionHandler());
$this->assertTrue($sh->isSessionHandlerInterface());
}
@@ -86,50 +54,17 @@ class AbstractProxyTest extends TestCase
$this->assertFalse($this->proxy->isWrapper());
}
public function testIsActivePhp53()
{
if (\PHP_VERSION_ID >= 50400) {
$this->markTestSkipped('Test skipped, for PHP 5.3 only.');
}
$this->assertFalse($this->proxy->isActive());
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
* @requires PHP 5.4
*/
public function testIsActivePhp54()
public function testIsActive()
{
$this->assertFalse($this->proxy->isActive());
session_start();
$this->assertTrue($this->proxy->isActive());
}
public function testSetActivePhp53()
{
if (\PHP_VERSION_ID >= 50400) {
$this->markTestSkipped('Test skipped, for PHP 5.3 only.');
}
$this->proxy->setActive(true);
$this->assertTrue($this->proxy->isActive());
$this->proxy->setActive(false);
$this->assertFalse($this->proxy->isActive());
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
* @expectedException \LogicException
* @requires PHP 5.4
*/
public function testSetActivePhp54()
{
$this->proxy->setActive(true);
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
@@ -142,27 +77,13 @@ class AbstractProxyTest extends TestCase
$this->assertEquals(session_name(), $this->proxy->getName());
}
/**
* @expectedException \LogicException
*/
public function testNameExceptionPhp53()
{
if (\PHP_VERSION_ID >= 50400) {
$this->markTestSkipped('Test skipped, for PHP 5.3 only.');
}
$this->proxy->setActive(true);
$this->proxy->setName('foo');
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
* @expectedException \LogicException
* @requires PHP 5.4
*/
public function testNameExceptionPhp54()
public function testNameException()
{
$this->expectException('LogicException');
session_start();
$this->proxy->setName('foo');
}
@@ -179,27 +100,13 @@ class AbstractProxyTest extends TestCase
$this->assertEquals(session_id(), $this->proxy->getId());
}
/**
* @expectedException \LogicException
*/
public function testIdExceptionPhp53()
{
if (\PHP_VERSION_ID >= 50400) {
$this->markTestSkipped('Test skipped, for PHP 5.3 only.');
}
$this->proxy->setActive(true);
$this->proxy->setId('foo');
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
* @expectedException \LogicException
* @requires PHP 5.4
*/
public function testIdExceptionPhp54()
public function testIdException()
{
$this->expectException('LogicException');
session_start();
$this->proxy->setId('foo');
}
@@ -17,6 +17,8 @@ use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
/**
* Test class for NativeProxy.
*
* @group legacy
*
* @author Drak <drak@zikula.org>
*/
class NativeProxyTest extends TestCase
@@ -25,7 +25,7 @@ use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
class SessionHandlerProxyTest extends TestCase
{
/**
* @var \PHPUnit_Framework_MockObject_Matcher
* @var \PHPUnit\Framework\MockObject\Matcher
*/
private $mock;
@@ -46,26 +46,22 @@ class SessionHandlerProxyTest extends TestCase
$this->proxy = null;
}
public function testOpen()
public function testOpenTrue()
{
$this->mock->expects($this->once())
->method('open')
->will($this->returnValue(true));
->willReturn(true);
$this->assertFalse($this->proxy->isActive());
$this->proxy->open('name', 'id');
if (\PHP_VERSION_ID < 50400) {
$this->assertTrue($this->proxy->isActive());
} else {
$this->assertFalse($this->proxy->isActive());
}
$this->assertFalse($this->proxy->isActive());
}
public function testOpenFalse()
{
$this->mock->expects($this->once())
->method('open')
->will($this->returnValue(false));
->willReturn(false);
$this->assertFalse($this->proxy->isActive());
$this->proxy->open('name', 'id');
@@ -76,7 +72,7 @@ class SessionHandlerProxyTest extends TestCase
{
$this->mock->expects($this->once())
->method('close')
->will($this->returnValue(true));
->willReturn(true);
$this->assertFalse($this->proxy->isActive());
$this->proxy->close();
@@ -87,7 +83,7 @@ class SessionHandlerProxyTest extends TestCase
{
$this->mock->expects($this->once())
->method('close')
->will($this->returnValue(false));
->willReturn(false);
$this->assertFalse($this->proxy->isActive());
$this->proxy->close();
@@ -125,4 +121,42 @@ class SessionHandlerProxyTest extends TestCase
$this->proxy->gc(86400);
}
/**
* @requires PHPUnit 5.1
*/
public function testValidateId()
{
$mock = $this->getMockBuilder(TestSessionHandler::class)->getMock();
$mock->expects($this->once())
->method('validateId');
$proxy = new SessionHandlerProxy($mock);
$proxy->validateId('id');
$this->assertTrue($this->proxy->validateId('id'));
}
/**
* @requires PHPUnit 5.1
*/
public function testUpdateTimestamp()
{
$mock = $this->getMockBuilder(TestSessionHandler::class)->getMock();
$mock->expects($this->once())
->method('updateTimestamp')
->willReturn(false);
$proxy = new SessionHandlerProxy($mock);
$proxy->updateTimestamp('id', 'data');
$this->mock->expects($this->once())
->method('write');
$this->proxy->updateTimestamp('id', 'data');
}
}
abstract class TestSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
{
}
@@ -19,7 +19,7 @@ class StreamedResponseTest extends TestCase
{
public function testConstructor()
{
$response = new StreamedResponse(function () { echo 'foo'; }, 404, array('Content-Type' => 'text/plain'));
$response = new StreamedResponse(function () { echo 'foo'; }, 404, ['Content-Type' => 'text/plain']);
$this->assertEquals(404, $response->getStatusCode());
$this->assertEquals('text/plain', $response->headers->get('Content-Type'));
@@ -51,7 +51,7 @@ class StreamedResponseTest extends TestCase
public function testPrepareWithHeadRequest()
{
$response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Content-Length' => '123'));
$response = new StreamedResponse(function () { echo 'foo'; }, 200, ['Content-Length' => '123']);
$request = Request::create('/', 'HEAD');
$response->prepare($request);
@@ -61,7 +61,7 @@ class StreamedResponseTest extends TestCase
public function testPrepareWithCacheHeaders()
{
$response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Cache-Control' => 'max-age=600, public'));
$response = new StreamedResponse(function () { echo 'foo'; }, 200, ['Cache-Control' => 'max-age=600, public']);
$request = Request::create('/', 'GET');
$response->prepare($request);
@@ -81,29 +81,16 @@ class StreamedResponseTest extends TestCase
$this->assertEquals(1, $called);
}
/**
* @expectedException \LogicException
*/
public function testSendContentWithNonCallable()
{
$this->expectException('LogicException');
$response = new StreamedResponse(null);
$response->sendContent();
}
/**
* @expectedException \LogicException
*/
public function testSetCallbackNonCallable()
{
$response = new StreamedResponse(null);
$response->setCallback(null);
}
/**
* @expectedException \LogicException
*/
public function testSetContent()
{
$this->expectException('LogicException');
$response = new StreamedResponse(function () { echo 'foo'; });
$response->setContent('foo');
}
+5 -11
View File
@@ -16,13 +16,12 @@
}
],
"require": {
"php": ">=5.3.9",
"symfony/polyfill-php54": "~1.0",
"symfony/polyfill-php55": "~1.0",
"symfony/polyfill-mbstring": "~1.1"
"php": "^5.5.9|>=7.0.8",
"symfony/polyfill-mbstring": "~1.1",
"symfony/polyfill-php70": "~1.6"
},
"require-dev": {
"symfony/expression-language": "~2.4|~3.0.0"
"symfony/expression-language": "~2.8|~3.0|~4.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\HttpFoundation\\": "" },
@@ -30,10 +29,5 @@
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
}
"minimum-stability": "dev"
}