composer update

This commit is contained in:
Manish Verma
2018-12-05 10:50:52 +05:30
parent 9eabcacfa7
commit 4addd1e9c6
3328 changed files with 156676 additions and 138988 deletions

View File

@@ -49,7 +49,7 @@ class AcceptHeaderItem
}
/**
* Returns header value's string representation.
* Returns header value's string representation.
*
* @return string
*/

View File

@@ -40,7 +40,7 @@ class BinaryFileResponse extends Response
* @param int $status The response status code
* @param array $headers An array of response headers
* @param bool $public Files are public by default
* @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param bool $autoEtag Whether the ETag header should be automatically set
* @param bool $autoLastModified Whether the Last-Modified header should be automatically set
*/
@@ -60,7 +60,7 @@ class BinaryFileResponse extends Response
* @param int $status The response status code
* @param array $headers An array of response headers
* @param bool $public Files are public by default
* @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename
* @param bool $autoEtag Whether the ETag header should be automatically set
* @param bool $autoLastModified Whether the Last-Modified header should be automatically set
*
@@ -219,8 +219,8 @@ class BinaryFileResponse extends Response
// Do X-Accel-Mapping substitutions.
// @link http://wiki.nginx.org/X-accel#X-Accel-Redirect
$parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',=');
$mappings = HeaderUtils::combine($parts);
foreach ($mappings as $pathPrefix => $location) {
foreach ($parts as $part) {
list($pathPrefix, $location) = $part;
if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
$path = $location.substr($path, \strlen($pathPrefix));
break;

View File

@@ -1,6 +1,14 @@
CHANGELOG
=========
4.2.0
-----
* the default value of the "$secure" and "$samesite" arguments of Cookie's constructor
will respectively change from "false" to "null" and from "null" to "lax" in Symfony
5.0, you should define their values explicitly or use "Cookie::create()" instead.
* added `matchPort()` in RequestMatcher
4.1.3
-----

View File

@@ -27,6 +27,7 @@ class Cookie
protected $httpOnly;
private $raw;
private $sameSite;
private $secureDefault = false;
const SAMESITE_LAX = 'lax';
const SAMESITE_STRICT = 'strict';
@@ -66,21 +67,30 @@ class Cookie
return new static($name, $value, $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
}
public static function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = self::SAMESITE_LAX): self
{
return new self($name, $value, $expire, $path, $domain, $secure, $httpOnly, $raw, $sameSite);
}
/**
* @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|null $secure Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
* @param bool $raw Whether the cookie value should be sent with no url encoding
* @param string|null $sameSite Whether the cookie will be available for cross-site requests
*
* @throws \InvalidArgumentException
*/
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null)
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null)
{
if (9 > \func_num_args()) {
@trigger_error(sprintf('The default value of the "$secure" and "$samesite" arguments of "%s"\'s constructor will respectively change from "false" to "null" and from "null" to "lax" in Symfony 5.0, you should define their values explicitly or use "Cookie::create()" instead.', __METHOD__), E_USER_DEPRECATED);
}
// from PHP source code
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
@@ -110,7 +120,9 @@ class Cookie
$this->httpOnly = $httpOnly;
$this->raw = $raw;
if (null !== $sameSite) {
if ('' === $sameSite) {
$sameSite = null;
} elseif (null !== $sameSite) {
$sameSite = strtolower($sameSite);
}
@@ -232,7 +244,7 @@ class Cookie
*/
public function isSecure()
{
return $this->secure;
return $this->secure ?? $this->secureDefault;
}
/**
@@ -274,4 +286,12 @@ class Cookie
{
return $this->sameSite;
}
/**
* @param bool $default The default value of the "secure" flag when it is set to null
*/
public function setSecureDefault(bool $default): void
{
$this->secureDefault = $default;
}
}

View File

@@ -206,7 +206,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
* @param string $key The parameter key
* @param \DateTime $default The default value
*
* @return null|\DateTime The parsed DateTime or the default value if the header does not exist
* @return \DateTime|null The parsed DateTime or the default value if the header does not exist
*
* @throws \RuntimeException When the HTTP header is not parseable
*/

View File

@@ -18,6 +18,9 @@ namespace Symfony\Component\HttpFoundation;
*/
class HeaderUtils
{
public const DISPOSITION_ATTACHMENT = 'attachment';
public const DISPOSITION_INLINE = 'inline';
/**
* This class should not be instantiated.
*/
@@ -143,6 +146,54 @@ class HeaderUtils
return preg_replace('/\\\\(.)|"/', '$1', $s);
}
/**
* Generates a HTTP Content-Disposition field-value.
*
* @param string $disposition One of "inline" or "attachment"
* @param string $filename A unicode string
* @param string $filenameFallback A string containing only ASCII characters that
* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @return string A string suitable for use as a Content-Disposition field-value
*
* @throws \InvalidArgumentException
*
* @see RFC 6266
*/
public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = ''): string
{
if (!\in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
}
if ('' === $filenameFallback) {
$filenameFallback = $filename;
}
// filenameFallback is not ASCII.
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
}
// percent characters aren't safe in fallback.
if (false !== strpos($filenameFallback, '%')) {
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
}
// path separators aren't allowed in either.
if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
}
$params = array('filename' => $filenameFallback);
if ($filename !== $filenameFallback) {
$params['filename*'] = "utf-8''".rawurlencode($filename);
}
return $disposition.'; '.self::toString($params, ';');
}
private static function groupParts(array $matches, string $separators): array
{
$separator = $separators[0];

View File

@@ -174,7 +174,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
* Returns the parameter value converted to boolean.
*
* @param string $key The parameter key
* @param mixed $default The default value if the parameter key does not exist
* @param bool $default The default value if the parameter key does not exist
*
* @return bool The filtered value
*/

View File

@@ -344,7 +344,7 @@ class Request
if (isset($components['port'])) {
$server['SERVER_PORT'] = $components['port'];
$server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port'];
$server['HTTP_HOST'] .= ':'.$components['port'];
}
if (isset($components['user'])) {
@@ -545,10 +545,13 @@ class Request
$requestOrder = ini_get('request_order') ?: ini_get('variables_order');
$requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
$_REQUEST = array();
$_REQUEST = array(array());
foreach (str_split($requestOrder) as $order) {
$_REQUEST = array_merge($_REQUEST, $request[$order]);
$_REQUEST[] = $request[$order];
}
$_REQUEST = array_merge(...$_REQUEST);
}
/**
@@ -1059,7 +1062,7 @@ class Request
}
$sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
$targetDirs = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path);
$targetDirs = explode('/', substr($path, 1));
array_pop($sourceDirs);
$targetFile = array_pop($targetDirs);
@@ -1284,7 +1287,7 @@ class Request
{
$canonicalMimeType = null;
if (false !== $pos = strpos($mimeType, ';')) {
$canonicalMimeType = substr($mimeType, 0, $pos);
$canonicalMimeType = trim(substr($mimeType, 0, $pos));
}
if (null === static::$formats) {
@@ -1325,7 +1328,7 @@ class Request
* * _format request attribute
* * $default
*
* @param string $default The default format
* @param string|null $default The default format
*
* @return string The request format
*/
@@ -1448,7 +1451,7 @@ class Request
*
* @see https://tools.ietf.org/html/rfc7231#section-4.2.3
*
* @return bool
* @return bool True for GET and HEAD, false otherwise
*/
public function isMethodCacheable()
{
@@ -1695,10 +1698,16 @@ 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));
$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

View File

@@ -28,6 +28,11 @@ class RequestMatcher implements RequestMatcherInterface
*/
private $host;
/**
* @var int|null
*/
private $port;
/**
* @var string[]
*/
@@ -56,13 +61,14 @@ class RequestMatcher implements RequestMatcherInterface
* @param array $attributes
* @param string|string[]|null $schemes
*/
public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null)
public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null, int $port = null)
{
$this->matchPath($path);
$this->matchHost($host);
$this->matchMethod($methods);
$this->matchIps($ips);
$this->matchScheme($schemes);
$this->matchPort($port);
foreach ($attributes as $k => $v) {
$this->matchAttribute($k, $v);
@@ -89,6 +95,16 @@ class RequestMatcher implements RequestMatcherInterface
$this->host = $regexp;
}
/**
* Adds a check for the the URL port.
*
* @param int|null $port The port number to connect to
*/
public function matchPort(int $port = null)
{
$this->port = $port;
}
/**
* Adds a check for the URL path info.
*
@@ -167,6 +183,10 @@ class RequestMatcher implements RequestMatcherInterface
return false;
}
if (null !== $this->port && 0 < $this->port && $request->getPort() !== $this->port) {
return false;
}
if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
return true;
}

View File

@@ -313,6 +313,12 @@ class Response
$this->ensureIEOverSSLCompatibility($request);
if ($request->isSecure()) {
foreach ($headers->getCookies() as $cookie) {
$cookie->setSecureDefault(true);
}
}
return $this;
}
@@ -330,8 +336,9 @@ class Response
// headers
foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
$replace = 0 === strcasecmp($name, 'Content-Type');
foreach ($values as $value) {
header($name.': '.$value, false, $this->statusCode);
header($name.': '.$value, $replace, $this->statusCode);
}
}
@@ -933,7 +940,7 @@ class Response
public function setCache(array $options)
{
if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'))) {
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff))));
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff)));
}
if (isset($options['etag'])) {

View File

@@ -247,55 +247,15 @@ class ResponseHeaderBag extends HeaderBag
*/
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
{
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
}
/**
* Generates a HTTP Content-Disposition field-value.
*
* @param string $disposition One of "inline" or "attachment"
* @param string $filename A unicode string
* @param string $filenameFallback A string containing only ASCII characters that
* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @return string A string suitable for use as a Content-Disposition field-value
*
* @throws \InvalidArgumentException
*
* @see RFC 6266
* @see HeaderUtils::makeDisposition()
*/
public function makeDisposition($disposition, $filename, $filenameFallback = '')
{
if (!\in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
}
if ('' == $filenameFallback) {
$filenameFallback = $filename;
}
// filenameFallback is not ASCII.
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
}
// percent characters aren't safe in fallback.
if (false !== strpos($filenameFallback, '%')) {
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
}
// path separators aren't allowed in either.
if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
}
$params = array('filename' => $filenameFallback);
if ($filename !== $filenameFallback) {
$params['filename*'] = "utf-8''".rawurlencode($filename);
}
return $disposition.'; '.HeaderUtils::toString($params, ';');
return HeaderUtils::makeDisposition((string) $disposition, (string) $filename, (string) $filenameFallback);
}
/**

View File

@@ -209,7 +209,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
*/
public function setId($id)
{
$this->storage->setId($id);
if ($this->storage->getId() !== $id) {
$this->storage->setId($id);
}
}
/**

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation\Session;
/**
* Session utility functions.
*
* @author Nicolas Grekas <p@tchwork.com>
* @author Rémon van de Kamp <rpkamp@gmail.com>
*
* @internal
*/
final class SessionUtils
{
/**
* Finds the session header amongst the headers that are to be sent, removes it, and returns
* it so the caller can process it further.
*/
public static function popSessionCookie(string $sessionName, string $sessionId): ?string
{
$sessionCookie = null;
$sessionCookiePrefix = sprintf(' %s=', urlencode($sessionName));
$sessionCookieWithId = sprintf('%s%s;', $sessionCookiePrefix, urlencode($sessionId));
$otherCookies = array();
foreach (headers_list() as $h) {
if (0 !== stripos($h, 'Set-Cookie:')) {
continue;
}
if (11 === strpos($h, $sessionCookiePrefix, 11)) {
$sessionCookie = $h;
if (11 !== strpos($h, $sessionCookieWithId, 11)) {
$otherCookies[] = $h;
}
} else {
$otherCookies[] = $h;
}
}
if (null === $sessionCookie) {
return null;
}
header_remove('Set-Cookie');
foreach ($otherCookies as $h) {
header($h, false);
}
return $sessionCookie;
}
}

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
use Symfony\Component\HttpFoundation\Session\SessionUtils;
/**
* This abstract session handler provides a generic implementation
* of the PHP 7.0 SessionUpdateTimestampHandlerInterface,
@@ -117,36 +119,20 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess
*/
public function destroy($sessionId)
{
if (!headers_sent() && ini_get('session.use_cookies')) {
if (!headers_sent() && filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN)) {
if (!$this->sessionName) {
throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', \get_class($this)));
}
$sessionCookie = sprintf(' %s=', urlencode($this->sessionName));
$sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId));
$sessionCookieFound = false;
$otherCookies = array();
foreach (headers_list() as $h) {
if (0 !== stripos($h, 'Set-Cookie:')) {
continue;
}
if (11 === strpos($h, $sessionCookie, 11)) {
$sessionCookieFound = true;
if (11 !== strpos($h, $sessionCookieWithId, 11)) {
$otherCookies[] = $h;
}
$cookie = SessionUtils::popSessionCookie($this->sessionName, $sessionId);
if (null === $cookie) {
if (\PHP_VERSION_ID < 70300) {
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), filter_var(ini_get('session.cookie_secure'), FILTER_VALIDATE_BOOLEAN), filter_var(ini_get('session.cookie_httponly'), FILTER_VALIDATE_BOOLEAN));
} else {
$otherCookies[] = $h;
$params = session_get_cookie_params();
unset($params['lifetime']);
setcookie($this->sessionName, '', $params);
}
}
if ($sessionCookieFound) {
header_remove('Set-Cookie');
foreach ($otherCookies as $h) {
header($h, false);
}
} else {
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'));
}
}
return $this->newSessionId === $sessionId || $this->doDestroy($sessionId);

View File

@@ -50,9 +50,7 @@ class MemcachedSessionHandler extends AbstractSessionHandler
$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)
));
throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
}
$this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;

View File

@@ -71,7 +71,7 @@ class PdoSessionHandler extends AbstractSessionHandler
private $pdo;
/**
* @var string|null|false DSN string or null for session.save_path or false when lazy connection disabled
* @var string|false|null DSN string or null for session.save_path or false when lazy connection disabled
*/
private $dsn = false;
@@ -637,7 +637,7 @@ class PdoSessionHandler extends AbstractSessionHandler
throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.');
}
if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
if (!filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN) && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
// In strict mode, session fixation is not possible: new sessions always start with a unique
// random id, so that concurrency is not possible and this code path can be skipped.
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block

View File

@@ -12,6 +12,7 @@
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
use Predis\Response\ErrorInterface;
use Symfony\Component\Cache\Traits\RedisClusterProxy;
use Symfony\Component\Cache\Traits\RedisProxy;
/**
@@ -45,7 +46,8 @@ class RedisSessionHandler extends AbstractSessionHandler
!$redis instanceof \RedisArray &&
!$redis instanceof \RedisCluster &&
!$redis instanceof \Predis\Client &&
!$redis instanceof RedisProxy
!$redis instanceof RedisProxy &&
!$redis instanceof RedisClusterProxy
) {
throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis)));
}

View File

@@ -12,6 +12,7 @@
namespace Symfony\Component\HttpFoundation\Session\Storage;
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionUtils;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
@@ -48,6 +49,11 @@ 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.
@@ -67,6 +73,7 @@ class NativeSessionStorage implements SessionStorageInterface
* cookie_lifetime, "0"
* cookie_path, "/"
* cookie_secure, ""
* cookie_samesite, null
* gc_divisor, "100"
* gc_maxlifetime, "1440"
* gc_probability, "1"
@@ -134,7 +141,7 @@ class NativeSessionStorage implements SessionStorageInterface
throw new \RuntimeException('Failed to start the session: already started by PHP.');
}
if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
if (filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN) && headers_sent($file, $line)) {
throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
}
@@ -143,6 +150,13 @@ class NativeSessionStorage implements SessionStorageInterface
throw new \RuntimeException('Failed to start the session');
}
if (null !== $this->emulateSameSite) {
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
if (null !== $originalCookie) {
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite));
}
}
$this->loadSession();
return true;
@@ -208,6 +222,13 @@ class NativeSessionStorage implements SessionStorageInterface
// @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));
}
}
return $isRegenerated;
}
@@ -227,29 +248,22 @@ class NativeSessionStorage implements SessionStorageInterface
unset($_SESSION[$key]);
}
// Register custom error handler to catch a possible failure warning during session write
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline);
}, E_WARNING);
// 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 {
$e = null;
session_write_close();
} catch (\ErrorException $e) {
} finally {
restore_error_handler();
$_SESSION = $session;
}
if (null !== $e) {
// The default PHP error message is not very helpful, as it does not give any information on the current save handler.
// Therefore, we catch this error and trigger a warning with a better error message
$handler = $this->getSaveHandler();
if ($handler instanceof SessionHandlerProxy) {
$handler = $handler->getHandler();
}
trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', \get_class($handler)), E_USER_WARNING);
}
$this->closed = true;
$this->started = false;
@@ -347,7 +361,7 @@ class NativeSessionStorage implements SessionStorageInterface
$validOptions = array_flip(array(
'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
'cookie_lifetime', 'cookie_path', 'cookie_secure',
'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite',
'gc_divisor', 'gc_maxlifetime', 'gc_probability',
'lazy_write', 'name', 'referer_check',
'serialize_handler', 'use_strict_mode', 'use_cookies',
@@ -359,6 +373,12 @@ class NativeSessionStorage implements SessionStorageInterface
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);
}
}

View File

@@ -17,7 +17,7 @@ namespace Symfony\Component\HttpFoundation;
* A StreamedResponse uses a callback for its content.
*
* The callback should use the standard PHP functions like echo
* to stream the response back to the client. The flush() method
* to stream the response back to the client. The flush() function
* can also be used if needed.
*
* @see flush()
@@ -129,6 +129,8 @@ class StreamedResponse extends Response
throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
}
$this->streamed = true;
return $this;
}
@@ -141,16 +143,4 @@ class StreamedResponse extends Response
{
return false;
}
/**
* {@inheritdoc}
*
* @return $this
*/
public function setNotModified()
{
$this->setCallback(function () {});
return parent::setNotModified();
}
}

View File

@@ -337,7 +337,8 @@ class BinaryFileResponseTest extends ResponseTestCase
{
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'),
array('/home/Foo/bar.txt', '/var/www/=/files/,/home/Foo/=/baz/', '/baz/bar.txt'),
array('/home/Foo/bar.txt', '"/var/www/"="/files/", "/home/Foo/"="/baz/"', '/baz/bar.txt'),
);
}

View File

@@ -45,7 +45,7 @@ class CookieTest extends TestCase
*/
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
{
new Cookie($name);
Cookie::create($name);
}
/**
@@ -53,12 +53,12 @@ class CookieTest extends TestCase
*/
public function testInvalidExpiration()
{
new Cookie('MyCookie', 'foo', 'bar');
Cookie::create('MyCookie', 'foo', 'bar');
}
public function testNegativeExpirationIsNotPossible()
{
$cookie = new Cookie('foo', 'bar', -100);
$cookie = Cookie::create('foo', 'bar', -100);
$this->assertSame(0, $cookie->getExpiresTime());
}
@@ -66,32 +66,32 @@ class CookieTest extends TestCase
public function testGetValue()
{
$value = 'MyValue';
$cookie = new Cookie('MyCookie', $value);
$cookie = Cookie::create('MyCookie', $value);
$this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value');
}
public function testGetPath()
{
$cookie = new Cookie('foo', 'bar');
$cookie = Cookie::create('foo', 'bar');
$this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path');
}
public function testGetExpiresTime()
{
$cookie = new Cookie('foo', 'bar');
$cookie = Cookie::create('foo', 'bar');
$this->assertEquals(0, $cookie->getExpiresTime(), '->getExpiresTime() returns the default expire date');
$cookie = new Cookie('foo', 'bar', $expire = time() + 3600);
$cookie = Cookie::create('foo', 'bar', $expire = time() + 3600);
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
public function testGetExpiresTimeIsCastToInt()
{
$cookie = new Cookie('foo', 'bar', 3600.9);
$cookie = Cookie::create('foo', 'bar', 3600.9);
$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
}
@@ -99,7 +99,7 @@ class CookieTest extends TestCase
public function testConstructorWithDateTime()
{
$expire = new \DateTime();
$cookie = new Cookie('foo', 'bar', $expire);
$cookie = Cookie::create('foo', 'bar', $expire);
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
@@ -107,7 +107,7 @@ class CookieTest extends TestCase
public function testConstructorWithDateTimeImmutable()
{
$expire = new \DateTimeImmutable();
$cookie = new Cookie('foo', 'bar', $expire);
$cookie = Cookie::create('foo', 'bar', $expire);
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
}
@@ -115,7 +115,7 @@ class CookieTest extends TestCase
public function testGetExpiresTimeWithStringValue()
{
$value = '+1 day';
$cookie = new Cookie('foo', 'bar', $value);
$cookie = Cookie::create('foo', 'bar', $value);
$expire = strtotime($value);
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1);
@@ -123,99 +123,99 @@ class CookieTest extends TestCase
public function testGetDomain()
{
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com');
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com');
$this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
}
public function testIsSecure()
{
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', true);
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com', true);
$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
}
public function testIsHttpOnly()
{
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
}
public function testCookieIsNotCleared()
{
$cookie = new Cookie('foo', 'bar', time() + 3600 * 24);
$cookie = Cookie::create('foo', 'bar', time() + 3600 * 24);
$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
}
public function testCookieIsCleared()
{
$cookie = new Cookie('foo', 'bar', time() - 20);
$cookie = Cookie::create('foo', 'bar', time() - 20);
$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
$cookie = new Cookie('foo', 'bar');
$cookie = Cookie::create('foo', 'bar');
$this->assertFalse($cookie->isCleared());
$cookie = new Cookie('foo', 'bar', 0);
$cookie = Cookie::create('foo', 'bar');
$this->assertFalse($cookie->isCleared());
$cookie = new Cookie('foo', 'bar', -1);
$cookie = Cookie::create('foo', 'bar', -1);
$this->assertFalse($cookie->isCleared());
}
public function testToString()
{
$cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
$cookie = Cookie::create('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
$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);
$cookie = Cookie::create('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
$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');
$cookie = Cookie::create('foo', null, 1, '/admin/', '.myfoodomain.com', false, true, false, 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);
$cookie = Cookie::create('foo', 'bar');
$this->assertEquals('foo=bar; path=/; httponly; samesite=lax', (string) $cookie);
}
public function testRawCookie()
{
$cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false);
$cookie = Cookie::create('foo', 'b a r', 0, '/', null, false, false, false, null);
$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);
$cookie = Cookie::create('foo', 'b+a+r', 0, '/', null, false, false, true, null);
$this->assertTrue($cookie->isRaw());
$this->assertEquals('foo=b+a+r; path=/', (string) $cookie);
}
public function testGetMaxAge()
{
$cookie = new Cookie('foo', 'bar');
$cookie = Cookie::create('foo', 'bar');
$this->assertEquals(0, $cookie->getMaxAge());
$cookie = new Cookie('foo', 'bar', $expire = time() + 100);
$cookie = Cookie::create('foo', 'bar', $expire = time() + 100);
$this->assertEquals($expire - time(), $cookie->getMaxAge());
$cookie = new Cookie('foo', 'bar', $expire = time() - 100);
$cookie = Cookie::create('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);
$this->assertEquals(Cookie::create('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true, null), $cookie);
$cookie = Cookie::fromString('foo=bar', true);
$this->assertEquals(new Cookie('foo', 'bar', 0, '/', null, false, false), $cookie);
$this->assertEquals(Cookie::create('foo', 'bar', 0, '/', null, false, false, false, null), $cookie);
$cookie = Cookie::fromString('foo', true);
$this->assertEquals(new Cookie('foo', null, 0, '/', null, false, false), $cookie);
$this->assertEquals(Cookie::create('foo', null, 0, '/', null, false, false, false, null), $cookie);
}
public function testFromStringWithHttpOnly()
@@ -227,9 +227,27 @@ class CookieTest extends TestCase
$this->assertFalse($cookie->isHttpOnly());
}
public function testSameSiteAttributeIsCaseInsensitive()
public function testSameSiteAttribute()
{
$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, 'Lax');
$this->assertEquals('lax', $cookie->getSameSite());
$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, '');
$this->assertNull($cookie->getSameSite());
}
public function testSetSecureDefault()
{
$cookie = Cookie::create('foo', 'bar');
$this->assertFalse($cookie->isSecure());
$cookie->setSecureDefault(true);
$this->assertTrue($cookie->isSecure());
$cookie->setSecureDefault(false);
$this->assertFalse($cookie->isSecure());
}
}

View File

@@ -22,7 +22,7 @@ error_reporting(-1);
ini_set('html_errors', 0);
ini_set('display_errors', 1);
if (ini_get('xdebug.default_enable')) {
if (filter_var(ini_get('xdebug.default_enable'), FILTER_VALIDATE_BOOLEAN)) {
xdebug_disable();
}

View File

@@ -4,7 +4,7 @@ use Symfony\Component\HttpFoundation\Cookie;
$r = require __DIR__.'/common.inc';
$r->headers->setCookie(new Cookie('foo', 'bar', 253402310800, '', null, false, false));
$r->headers->setCookie(new Cookie('foo', 'bar', 253402310800, '', null, false, false, false, null));
$r->sendHeaders();
setcookie('foo2', 'bar', 253402310800, '/');

View File

@@ -6,7 +6,7 @@ $r = require __DIR__.'/common.inc';
$str = '?*():@&+$/%#[]';
$r->headers->setCookie(new Cookie($str, $str, 0, '/', null, false, false, true));
$r->headers->setCookie(new Cookie($str, $str, 0, '/', null, false, false, true, null));
$r->sendHeaders();
setrawcookie($str, $str, 0, '/', null, false, false);

View File

@@ -6,7 +6,7 @@ $r = require __DIR__.'/common.inc';
$str = '?*():@&+$/%#[]';
$r->headers->setCookie(new Cookie($str, $str, 0, '', null, false, false));
$r->headers->setCookie(new Cookie($str, $str, 0, '', null, false, false, false, null));
$r->sendHeaders();
setcookie($str, $str, 0, '/');

View File

@@ -5,7 +5,7 @@ use Symfony\Component\HttpFoundation\Cookie;
$r = require __DIR__.'/common.inc';
try {
$r->headers->setCookie(new Cookie('Hello + world', 'hodor'));
$r->headers->setCookie(Cookie::create('Hello + world', 'hodor'));
} catch (\InvalidArgumentException $e) {
echo $e->getMessage();
}

View File

@@ -82,4 +82,53 @@ class HeaderUtilsTest extends TestCase
$this->assertEquals('foo "bar"', HeaderUtils::unquote('"foo \"\b\a\r\""'));
$this->assertEquals('foo \\ bar', HeaderUtils::unquote('"foo \\\\ bar"'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionInvalidDisposition()
{
HeaderUtils::makeDisposition('invalid', 'foo.html');
}
/**
* @dataProvider provideMakeDisposition
*/
public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
{
$this->assertEquals($expected, HeaderUtils::makeDisposition($disposition, $filename, $filenameFallback));
}
public function provideMakeDisposition()
{
return array(
array('attachment', 'foo.html', 'foo.html', 'attachment; filename=foo.html'),
array('attachment', 'foo.html', '', 'attachment; filename=foo.html'),
array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'),
array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'),
array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'),
array('attachment', 'föö.html', 'foo.html', 'attachment; filename=foo.html; filename*=utf-8\'\'f%C3%B6%C3%B6.html'),
);
}
/**
* @dataProvider provideMakeDispositionFail
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionFail($disposition, $filename)
{
HeaderUtils::makeDisposition($disposition, $filename);
}
public function provideMakeDispositionFail()
{
return array(
array('attachment', 'foo%20bar.html'),
array('attachment', 'foo/bar.html'),
array('attachment', '/foo.html'),
array('attachment', 'foo\bar.html'),
array('attachment', '\foo.html'),
array('attachment', 'föö.html'),
);
}
}

View File

@@ -78,6 +78,21 @@ class RequestMatcherTest extends TestCase
$this->assertSame($isMatch, $matcher->matches($request));
}
public function testPort()
{
$matcher = new RequestMatcher();
$request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => null, 'SERVER_PORT' => 8000));
$matcher->matchPort(8000);
$this->assertTrue($matcher->matches($request));
$matcher->matchPort(9000);
$this->assertFalse($matcher->matches($request));
$matcher = new RequestMatcher(null, null, null, null, array(), null, 8000);
$this->assertTrue($matcher->matches($request));
}
public function getHostData()
{
return array(

View File

@@ -232,6 +232,55 @@ class RequestTest extends TestCase
$this->assertEquals(80, $request->getPort());
$this->assertEquals('test.com', $request->getHttpHost());
$this->assertFalse($request->isSecure());
// Fragment should not be included in the URI
$request = Request::create('http://test.com/foo#bar');
$this->assertEquals('http://test.com/foo', $request->getUri());
}
public function testCreateWithRequestUri()
{
$request = Request::create('http://test.com:80/foo');
$request->server->set('REQUEST_URI', 'http://test.com:80/foo');
$this->assertEquals('http://test.com/foo', $request->getUri());
$this->assertEquals('/foo', $request->getPathInfo());
$this->assertEquals('test.com', $request->getHost());
$this->assertEquals('test.com', $request->getHttpHost());
$this->assertEquals(80, $request->getPort());
$this->assertFalse($request->isSecure());
$request = Request::create('http://test.com:8080/foo');
$request->server->set('REQUEST_URI', 'http://test.com:8080/foo');
$this->assertEquals('http://test.com:8080/foo', $request->getUri());
$this->assertEquals('/foo', $request->getPathInfo());
$this->assertEquals('test.com', $request->getHost());
$this->assertEquals('test.com:8080', $request->getHttpHost());
$this->assertEquals(8080, $request->getPort());
$this->assertFalse($request->isSecure());
$request = Request::create('http://test.com/foo?bar=foo', 'GET', array('bar' => 'baz'));
$request->server->set('REQUEST_URI', 'http://test.com/foo?bar=foo');
$this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
$this->assertEquals('/foo', $request->getPathInfo());
$this->assertEquals('bar=baz', $request->getQueryString());
$this->assertEquals('test.com', $request->getHost());
$this->assertEquals('test.com', $request->getHttpHost());
$this->assertEquals(80, $request->getPort());
$this->assertFalse($request->isSecure());
$request = Request::create('https://test.com:443/foo');
$request->server->set('REQUEST_URI', 'https://test.com:443/foo');
$this->assertEquals('https://test.com/foo', $request->getUri());
$this->assertEquals('/foo', $request->getPathInfo());
$this->assertEquals('test.com', $request->getHost());
$this->assertEquals('test.com', $request->getHttpHost());
$this->assertEquals(443, $request->getPort());
$this->assertTrue($request->isSecure());
// Fragment should not be included in the URI
$request = Request::create('http://test.com/foo#bar');
$request->server->set('REQUEST_URI', 'http://test.com/foo#bar');
$this->assertEquals('http://test.com/foo', $request->getUri());
}
public function testCreateCheckPrecedence()
@@ -332,6 +381,9 @@ class RequestTest extends TestCase
{
$request = new Request();
$this->assertEquals('json', $request->getFormat('application/json; charset=utf-8'));
$this->assertEquals('json', $request->getFormat('application/json;charset=utf-8'));
$this->assertEquals('json', $request->getFormat('application/json ; charset=utf-8'));
$this->assertEquals('json', $request->getFormat('application/json ;charset=utf-8'));
}
/**

View File

@@ -110,9 +110,9 @@ class ResponseHeaderBagTest extends TestCase
public function testToStringIncludesCookieHeaders()
{
$bag = new ResponseHeaderBag(array());
$bag->setCookie(new Cookie('foo', 'bar'));
$bag->setCookie(Cookie::create('foo', 'bar'));
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/; httponly; samesite=lax', $bag);
$bag->clearCookie('foo');
@@ -154,24 +154,24 @@ class ResponseHeaderBagTest extends TestCase
public function testCookiesWithSameNames()
{
$bag = new ResponseHeaderBag();
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar'));
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo'));
$bag->setCookie(new Cookie('foo', 'bar'));
$bag->setCookie(Cookie::create('foo', 'bar', 0, '/path/foo', 'foo.bar'));
$bag->setCookie(Cookie::create('foo', 'bar', 0, '/path/bar', 'foo.bar'));
$bag->setCookie(Cookie::create('foo', 'bar', 0, '/path/bar', 'bar.foo'));
$bag->setCookie(Cookie::create('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; samesite=lax', $bag->get('set-cookie'));
$this->assertEquals(array(
'foo=bar; path=/path/foo; domain=foo.bar; httponly',
'foo=bar; path=/path/bar; domain=foo.bar; httponly',
'foo=bar; path=/path/bar; domain=bar.foo; httponly',
'foo=bar; path=/; httponly',
'foo=bar; path=/path/foo; domain=foo.bar; httponly; samesite=lax',
'foo=bar; path=/path/bar; domain=foo.bar; httponly; samesite=lax',
'foo=bar; path=/path/bar; domain=bar.foo; httponly; samesite=lax',
'foo=bar; path=/; httponly; samesite=lax',
), $bag->get('set-cookie', null, false));
$this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly; samesite=lax', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly; samesite=lax', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly; samesite=lax', $bag);
$this->assertSetCookieHeader('foo=bar; path=/; httponly; samesite=lax', $bag);
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
@@ -186,8 +186,8 @@ class ResponseHeaderBagTest extends TestCase
$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'));
$bag->setCookie(Cookie::create('foo', 'bar', 0, '/path/foo', 'foo.bar'));
$bag->setCookie(Cookie::create('bar', 'foo', 0, '/path/bar', 'foo.bar'));
$this->assertTrue($bag->has('set-cookie'));
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
@@ -209,8 +209,8 @@ class ResponseHeaderBagTest extends TestCase
public function testRemoveCookieWithNullRemove()
{
$bag = new ResponseHeaderBag();
$bag->setCookie(new Cookie('foo', 'bar', 0));
$bag->setCookie(new Cookie('bar', 'foo', 0));
$bag->setCookie(Cookie::create('foo', 'bar'));
$bag->setCookie(Cookie::create('bar', 'foo'));
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertArrayHasKey('/', $cookies['']);
@@ -228,12 +228,12 @@ class ResponseHeaderBagTest extends TestCase
{
$bag = new ResponseHeaderBag();
$bag->set('set-cookie', 'foo=bar');
$this->assertEquals(array(new Cookie('foo', 'bar', 0, '/', null, false, false, true)), $bag->getCookies());
$this->assertEquals(array(Cookie::create('foo', 'bar', 0, '/', null, false, false, true, null)), $bag->getCookies());
$bag->set('set-cookie', 'foo2=bar2', false);
$this->assertEquals(array(
new Cookie('foo', 'bar', 0, '/', null, false, false, true),
new Cookie('foo2', 'bar2', 0, '/', null, false, false, true),
Cookie::create('foo', 'bar', 0, '/', null, false, false, true, null),
Cookie::create('foo2', 'bar2', 0, '/', null, false, false, true, null),
), $bag->getCookies());
$bag->remove('set-cookie');
@@ -250,26 +250,6 @@ class ResponseHeaderBagTest extends TestCase
$bag->getCookies('invalid_argument');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionInvalidDisposition()
{
$headers = new ResponseHeaderBag();
$headers->makeDisposition('invalid', 'foo.html');
}
/**
* @dataProvider provideMakeDisposition
*/
public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
{
$headers = new ResponseHeaderBag();
$this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback));
}
public function testToStringDoesntMessUpHeaders()
{
$headers = new ResponseHeaderBag();
@@ -284,41 +264,6 @@ class ResponseHeaderBagTest extends TestCase
$this->assertEquals(array('text/html'), $allHeaders['Content-type']);
}
public function provideMakeDisposition()
{
return array(
array('attachment', 'foo.html', 'foo.html', 'attachment; filename=foo.html'),
array('attachment', 'foo.html', '', 'attachment; filename=foo.html'),
array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'),
array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'),
array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'),
array('attachment', 'föö.html', 'foo.html', 'attachment; filename=foo.html; filename*=utf-8\'\'f%C3%B6%C3%B6.html'),
);
}
/**
* @dataProvider provideMakeDispositionFail
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionFail($disposition, $filename)
{
$headers = new ResponseHeaderBag();
$headers->makeDisposition($disposition, $filename);
}
public function provideMakeDispositionFail()
{
return array(
array('attachment', 'foo%20bar.html'),
array('attachment', 'foo/bar.html'),
array('attachment', '/foo.html'),
array('attachment', 'foo\bar.html'),
array('attachment', '\foo.html'),
array('attachment', 'föö.html'),
);
}
public function testDateHeaderAddedOnCreation()
{
$now = time();

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\HttpFoundation\Tests;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -573,6 +574,24 @@ class ResponseTest extends ResponseTestCase
$this->assertFalse($response->headers->has('expires'));
}
public function testPrepareSetsCookiesSecure()
{
$cookie = Cookie::create('foo', 'bar');
$response = new Response('foo');
$response->headers->setCookie($cookie);
$request = Request::create('/', 'GET');
$response->prepare($request);
$this->assertFalse($cookie->isSecure());
$request = Request::create('https://localhost/', 'GET');
$response->prepare($request);
$this->assertTrue($cookie->isSecure());
}
public function testSetCache()
{
$response = new Response();

View File

@@ -45,7 +45,7 @@ class AttributeBagTest extends TestCase
),
),
);
$this->bag = new AttributeBag('_sf2');
$this->bag = new AttributeBag('_sf');
$this->bag->initialize($this->array);
}
@@ -67,7 +67,7 @@ class AttributeBagTest extends TestCase
public function testGetStorageKey()
{
$this->assertEquals('_sf2', $this->bag->getStorageKey());
$this->assertEquals('_sf', $this->bag->getStorageKey());
$attributeBag = new AttributeBag('test');
$this->assertEquals('test', $attributeBag->getStorageKey());
}

View File

@@ -70,6 +70,27 @@ class SessionTest extends TestCase
$this->assertEquals('0123456789abcdef', $this->session->getId());
}
public function testSetIdAfterStart()
{
$this->session->start();
$id = $this->session->getId();
$e = null;
try {
$this->session->setId($id);
} catch (\Exception $e) {
}
$this->assertNull($e);
try {
$this->session->setId('different');
} catch (\Exception $e) {
}
$this->assertInstanceOf('\LogicException', $e);
}
public function testSetName()
{
$this->assertEquals('MOCKSESSID', $this->session->getName());

View File

@@ -0,0 +1,16 @@
open
validateId
read
doRead:
read
write
doWrite: foo|s:3:"bar";
close
Array
(
[0] => Content-Type: text/plain; charset=utf-8
[1] => Cache-Control: max-age=0, private, must-revalidate
[2] => Set-Cookie: sid=random_session_id; path=/; secure; HttpOnly; SameSite=lax
)
shutdown

View File

@@ -0,0 +1,13 @@
<?php
require __DIR__.'/common.inc';
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
$storage = new NativeSessionStorage(array('cookie_samesite' => 'lax'));
$storage->setSaveHandler(new TestSessionHandler());
$storage->start();
$_SESSION = array('foo' => 'bar');
ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); });

View File

@@ -0,0 +1,23 @@
open
validateId
read
doRead:
read
destroy
close
open
validateId
read
doRead:
read
write
doWrite: foo|s:3:"bar";
close
Array
(
[0] => Content-Type: text/plain; charset=utf-8
[1] => Cache-Control: max-age=0, private, must-revalidate
[2] => Set-Cookie: sid=random_session_id; path=/; secure; HttpOnly; SameSite=lax
)
shutdown

View File

@@ -0,0 +1,15 @@
<?php
require __DIR__.'/common.inc';
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
$storage = new NativeSessionStorage(array('cookie_samesite' => 'lax'));
$storage->setSaveHandler(new TestSessionHandler());
$storage->start();
$_SESSION = array('foo' => 'bar');
$storage->regenerate(true);
ob_start(function ($buffer) { return preg_replace('~_sf2_meta.*$~m', '', str_replace(session_id(), 'random_session_id', $buffer)); });

View File

@@ -45,7 +45,7 @@ class MongoDbSessionHandlerTest extends TestCase
'data_field' => 'data',
'time_field' => 'time',
'expiry_field' => 'expires_at',
'database' => 'sf2-test',
'database' => 'sf-test',
'collection' => 'session-test',
);

View File

@@ -33,7 +33,7 @@ class PdoSessionHandlerTest extends TestCase
protected function getPersistentSqliteDsn()
{
$this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions');
$this->dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_sessions');
return 'sqlite:'.$this->dbFile;
}
@@ -153,7 +153,7 @@ class PdoSessionHandlerTest extends TestCase
public function testReadLockedConvertsStreamToString()
{
if (ini_get('session.use_strict_mode')) {
if (filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN)) {
$this->markTestSkipped('Strict mode needs no locking for new sessions.');
}

View File

@@ -48,7 +48,7 @@ class MockArraySessionStorageTest extends TestCase
$this->data = array(
$this->attributes->getStorageKey() => array('foo' => 'bar'),
$this->flashes->getStorageKey() => array('notice' => 'hello'),
);
);
$this->storage = new MockArraySessionStorage();
$this->storage->registerBag($this->flashes);

View File

@@ -35,7 +35,7 @@ class MockFileSessionStorageTest extends TestCase
protected function setUp()
{
$this->sessionDir = sys_get_temp_dir().'/sf2test';
$this->sessionDir = sys_get_temp_dir().'/sftest';
$this->storage = $this->getStorage();
}

View File

@@ -36,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);
}
@@ -171,6 +171,10 @@ class NativeSessionStorageTest extends TestCase
'cookie_httponly' => false,
);
if (\PHP_VERSION_ID >= 70300) {
$options['cookie_samesite'] = 'lax';
}
$this->getStorage($options);
$temp = session_get_cookie_params();
$gco = array();

View File

@@ -32,7 +32,7 @@ class PhpBridgeSessionStorageTest 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);
}

View File

@@ -32,7 +32,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "4.1-dev"
"dev-master": "4.2-dev"
}
}
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"