From 4d440be93adcfd5e4ee0bdc7acd1c3260625728f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 4 Sep 2018 10:27:39 +0200 Subject: [PATCH 1/8] [HttpFoundation][FrameworkBundle] fix support for samesite in session cookies --- Session/SessionUtils.php | 59 +++++++++++++++++++ .../Handler/AbstractSessionHandler.php | 50 +++++++--------- Session/Storage/NativeSessionStorage.php | 42 +++++++++++-- .../Storage/Handler/Fixtures/storage.expected | 3 +- .../Fixtures/with_cookie_and_session.expected | 1 + .../Handler/Fixtures/with_samesite.expected | 16 +++++ .../Handler/Fixtures/with_samesite.php | 13 ++++ .../with_samesite_and_migration.expected | 23 ++++++++ .../Fixtures/with_samesite_and_migration.php | 15 +++++ .../Storage/NativeSessionStorageTest.php | 8 ++- 10 files changed, 192 insertions(+), 38 deletions(-) create mode 100644 Session/SessionUtils.php create mode 100644 Tests/Session/Storage/Handler/Fixtures/with_samesite.expected create mode 100644 Tests/Session/Storage/Handler/Fixtures/with_samesite.php create mode 100644 Tests/Session/Storage/Handler/Fixtures/with_samesite_and_migration.expected create mode 100644 Tests/Session/Storage/Handler/Fixtures/with_samesite_and_migration.php diff --git a/Session/SessionUtils.php b/Session/SessionUtils.php new file mode 100644 index 000000000..04a25f716 --- /dev/null +++ b/Session/SessionUtils.php @@ -0,0 +1,59 @@ + + * + * 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 + * @author Rémon van de Kamp + * + * @internal + */ +final class SessionUtils +{ + /** + * Find the session header amongst the headers that are to be sent, remove it, and return + * it so the caller can process it further. + */ + public static function popSessionCookie($sessionName, $sessionId) + { + $sessionCookie = null; + $sessionCookiePrefix = sprintf(' %s=', urlencode($sessionName)); + $sessionCookieWithId = sprintf('%s%s;', $sessionCookiePrefix, urlencode($sessionId)); + $otherCookies = []; + 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; + } +} diff --git a/Session/Storage/Handler/AbstractSessionHandler.php b/Session/Storage/Handler/AbstractSessionHandler.php index 84ba0ebba..ec59d895c 100644 --- a/Session/Storage/Handler/AbstractSessionHandler.php +++ b/Session/Storage/Handler/AbstractSessionHandler.php @@ -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, @@ -27,7 +29,7 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess private $igbinaryEmptyData; /** - * {@inheritdoc} + * @return bool */ public function open($savePath, $sessionName) { @@ -62,7 +64,7 @@ abstract protected function doWrite($sessionId, $data); abstract protected function doDestroy($sessionId); /** - * {@inheritdoc} + * @return bool */ public function validateId($sessionId) { @@ -73,7 +75,7 @@ public function validateId($sessionId) } /** - * {@inheritdoc} + * @return string */ public function read($sessionId) { @@ -99,7 +101,7 @@ public function read($sessionId) } /** - * {@inheritdoc} + * @return bool */ public function write($sessionId, $data) { @@ -124,7 +126,7 @@ public function write($sessionId, $data) } /** - * {@inheritdoc} + * @return bool */ public function destroy($sessionId) { @@ -135,31 +137,23 @@ public function destroy($sessionId) if (!$this->sessionName) { throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', static::class)); } - $sessionCookie = sprintf(' %s=', urlencode($this->sessionName)); - $sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId)); - $sessionCookieFound = false; - $otherCookies = []; - 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); + + /* + * We send an invalidation Set-Cookie header (zero lifetime) + * when either the session was started or a cookie with + * the session name was sent by the client (in which case + * we know it's invalid as a valid session cookie would've + * started the session). + */ + if (null === $cookie || isset($_COOKIE[$this->sessionName])) { + 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; - } - } - if ($sessionCookieFound) { - header_remove('Set-Cookie'); - foreach ($otherCookies as $h) { - header($h, false); + $params = session_get_cookie_params(); + unset($params['lifetime']); + setcookie($this->sessionName, '', $params); } - } else { - 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)); } } diff --git a/Session/Storage/NativeSessionStorage.php b/Session/Storage/NativeSessionStorage.php index cbf7cadc7..7502897a4 100644 --- a/Session/Storage/NativeSessionStorage.php +++ b/Session/Storage/NativeSessionStorage.php @@ -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 * entropy_file, "" * entropy_length, "0" * gc_divisor, "100" @@ -94,9 +101,7 @@ class NativeSessionStorage implements SessionStorageInterface * trans_sid_hosts, $_SERVER['HTTP_HOST'] * trans_sid_tags, "a=href,area=href,frame=src,form=" * - * @param array $options Session configuration options - * @param \SessionHandlerInterface|null $handler - * @param MetadataBag $metaBag MetadataBag + * @param AbstractProxy|\SessionHandlerInterface|null $handler */ public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null) { @@ -150,6 +155,13 @@ public function start() throw new \RuntimeException('Failed to start the session'); } + if (null !== $this->emulateSameSite) { + $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id()); + if (null !== $originalCookie) { + header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false); + } + } + $this->loadSession(); return true; @@ -215,6 +227,13 @@ public function regenerate($destroy = false, $lifetime = null) // @see https://bugs.php.net/70013 $this->loadSession(); + if (null !== $this->emulateSameSite) { + $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id()); + if (null !== $originalCookie) { + header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false); + } + } + return $isRegenerated; } @@ -223,6 +242,7 @@ public function regenerate($destroy = false, $lifetime = null) */ public function save() { + // Store a copy so we can restore the bags in case the session was not left empty $session = $_SESSION; foreach ($this->bags as $bag) { @@ -248,7 +268,11 @@ public function save() session_write_close(); } finally { restore_error_handler(); - $_SESSION = $session; + + // Restore only if not empty + if ($_SESSION) { + $_SESSION = $session; + } } $this->closed = true; @@ -347,7 +371,7 @@ public function setOptions(array $options) $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', @@ -360,6 +384,12 @@ public function setOptions(array $options) 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); } } @@ -381,7 +411,7 @@ public function setOptions(array $options) * @see https://php.net/sessionhandlerinterface * @see https://php.net/sessionhandler * - * @param \SessionHandlerInterface|null $saveHandler + * @param AbstractProxy|\SessionHandlerInterface|null $saveHandler * * @throws \InvalidArgumentException */ diff --git a/Tests/Session/Storage/Handler/Fixtures/storage.expected b/Tests/Session/Storage/Handler/Fixtures/storage.expected index 4533a10a1..05a5d5d0b 100644 --- a/Tests/Session/Storage/Handler/Fixtures/storage.expected +++ b/Tests/Session/Storage/Handler/Fixtures/storage.expected @@ -11,10 +11,11 @@ $_SESSION is not empty write destroy close -$_SESSION is not empty +$_SESSION is empty Array ( [0] => Content-Type: text/plain; charset=utf-8 [1] => Cache-Control: max-age=0, private, must-revalidate + [2] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly ) shutdown diff --git a/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected b/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected index 5de2d9e39..63078228d 100644 --- a/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected +++ b/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected @@ -20,5 +20,6 @@ Array [0] => Content-Type: text/plain; charset=utf-8 [1] => Cache-Control: max-age=10800, private, must-revalidate [2] => Set-Cookie: abc=def + [3] => Set-Cookie: sid=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly ) shutdown diff --git a/Tests/Session/Storage/Handler/Fixtures/with_samesite.expected b/Tests/Session/Storage/Handler/Fixtures/with_samesite.expected new file mode 100644 index 000000000..d20fb88ec --- /dev/null +++ b/Tests/Session/Storage/Handler/Fixtures/with_samesite.expected @@ -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 diff --git a/Tests/Session/Storage/Handler/Fixtures/with_samesite.php b/Tests/Session/Storage/Handler/Fixtures/with_samesite.php new file mode 100644 index 000000000..fc2c41828 --- /dev/null +++ b/Tests/Session/Storage/Handler/Fixtures/with_samesite.php @@ -0,0 +1,13 @@ + 'lax']); +$storage->setSaveHandler(new TestSessionHandler()); +$storage->start(); + +$_SESSION = ['foo' => 'bar']; + +ob_start(function ($buffer) { return str_replace(session_id(), 'random_session_id', $buffer); }); diff --git a/Tests/Session/Storage/Handler/Fixtures/with_samesite_and_migration.expected b/Tests/Session/Storage/Handler/Fixtures/with_samesite_and_migration.expected new file mode 100644 index 000000000..8b5fc08bd --- /dev/null +++ b/Tests/Session/Storage/Handler/Fixtures/with_samesite_and_migration.expected @@ -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 diff --git a/Tests/Session/Storage/Handler/Fixtures/with_samesite_and_migration.php b/Tests/Session/Storage/Handler/Fixtures/with_samesite_and_migration.php new file mode 100644 index 000000000..a28b6fedf --- /dev/null +++ b/Tests/Session/Storage/Handler/Fixtures/with_samesite_and_migration.php @@ -0,0 +1,15 @@ + 'lax']); +$storage->setSaveHandler(new TestSessionHandler()); +$storage->start(); + +$_SESSION = ['foo' => 'bar']; + +$storage->regenerate(true); + +ob_start(function ($buffer) { return preg_replace('~_sf2_meta.*$~m', '', str_replace(session_id(), 'random_session_id', $buffer)); }); diff --git a/Tests/Session/Storage/NativeSessionStorageTest.php b/Tests/Session/Storage/NativeSessionStorageTest.php index 9ce8108da..d2cf32452 100644 --- a/Tests/Session/Storage/NativeSessionStorageTest.php +++ b/Tests/Session/Storage/NativeSessionStorageTest.php @@ -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); } @@ -167,6 +167,10 @@ public function testCookieOptions() 'cookie_httponly' => false, ]; + if (\PHP_VERSION_ID >= 70300) { + $options['cookie_samesite'] = 'lax'; + } + $this->getStorage($options); $temp = session_get_cookie_params(); $gco = []; @@ -175,8 +179,6 @@ public function testCookieOptions() $gco['cookie_'.$key] = $value; } - unset($gco['cookie_samesite']); - $this->assertEquals($options, $gco); } From 1791cbfce69e9440f5e01b389ddca92b163561fa Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 15 Mar 2020 10:01:00 +0100 Subject: [PATCH 2/8] Add missing dots at the end of exception messages --- File/File.php | 6 +++--- File/MimeType/MimeTypeGuesser.php | 2 +- File/UploadedFile.php | 2 +- Session/Storage/Handler/MemcacheSessionHandler.php | 2 +- Session/Storage/Handler/MemcachedSessionHandler.php | 2 +- Session/Storage/Handler/MongoDbSessionHandler.php | 4 ++-- Session/Storage/Handler/NativeFileSessionHandler.php | 4 ++-- Session/Storage/Handler/PdoSessionHandler.php | 4 ++-- Session/Storage/MockArraySessionStorage.php | 2 +- Session/Storage/MockFileSessionStorage.php | 4 ++-- Session/Storage/NativeSessionStorage.php | 2 +- Session/Storage/Proxy/AbstractProxy.php | 4 ++-- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/File/File.php b/File/File.php index 34220588a..309339e5c 100644 --- a/File/File.php +++ b/File/File.php @@ -97,7 +97,7 @@ public function move($directory, $name = null) $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 @@ protected function getTargetFile($directory, $name = null) { 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)); diff --git a/File/MimeType/MimeTypeGuesser.php b/File/MimeType/MimeTypeGuesser.php index e05269fc8..8d971ed67 100644 --- a/File/MimeType/MimeTypeGuesser.php +++ b/File/MimeType/MimeTypeGuesser.php @@ -127,7 +127,7 @@ public function guess($path) } 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; diff --git a/File/UploadedFile.php b/File/UploadedFile.php index 86153ed49..5b75dd84f 100644 --- a/File/UploadedFile.php +++ b/File/UploadedFile.php @@ -196,7 +196,7 @@ public function move($directory, $name = null) $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()); diff --git a/Session/Storage/Handler/MemcacheSessionHandler.php b/Session/Storage/Handler/MemcacheSessionHandler.php index 3abc33caa..d4b68ae8b 100644 --- a/Session/Storage/Handler/MemcacheSessionHandler.php +++ b/Session/Storage/Handler/MemcacheSessionHandler.php @@ -47,7 +47,7 @@ class MemcacheSessionHandler implements \SessionHandlerInterface public function __construct(\Memcache $memcache, array $options = []) { if ($diff = array_diff(array_keys($options), ['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->memcache = $memcache; diff --git a/Session/Storage/Handler/MemcachedSessionHandler.php b/Session/Storage/Handler/MemcachedSessionHandler.php index a399be5fd..6711e0a55 100644 --- a/Session/Storage/Handler/MemcachedSessionHandler.php +++ b/Session/Storage/Handler/MemcachedSessionHandler.php @@ -47,7 +47,7 @@ public function __construct(\Memcached $memcached, array $options = []) $this->memcached = $memcached; if ($diff = array_diff(array_keys($options), ['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; diff --git a/Session/Storage/Handler/MongoDbSessionHandler.php b/Session/Storage/Handler/MongoDbSessionHandler.php index 1dd724066..d84f2e9d0 100644 --- a/Session/Storage/Handler/MongoDbSessionHandler.php +++ b/Session/Storage/Handler/MongoDbSessionHandler.php @@ -74,11 +74,11 @@ public function __construct($mongo, array $options) } 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; diff --git a/Session/Storage/Handler/NativeFileSessionHandler.php b/Session/Storage/Handler/NativeFileSessionHandler.php index 8b7615ec1..f24271fbf 100644 --- a/Session/Storage/Handler/NativeFileSessionHandler.php +++ b/Session/Storage/Handler/NativeFileSessionHandler.php @@ -38,7 +38,7 @@ public function __construct($savePath = null) 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 @@ -46,7 +46,7 @@ public function __construct($savePath = null) } 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); diff --git a/Session/Storage/Handler/PdoSessionHandler.php b/Session/Storage/Handler/PdoSessionHandler.php index 653ca6917..d1cf622bd 100644 --- a/Session/Storage/Handler/PdoSessionHandler.php +++ b/Session/Storage/Handler/PdoSessionHandler.php @@ -173,7 +173,7 @@ 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; @@ -465,7 +465,7 @@ private function buildDsnFromUrl($dsnOrUrl) } if (!isset($params['scheme'])) { - throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler'); + throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler.'); } $driverAliasMap = [ diff --git a/Session/Storage/MockArraySessionStorage.php b/Session/Storage/MockArraySessionStorage.php index c1e7523c5..969f272fb 100644 --- a/Session/Storage/MockArraySessionStorage.php +++ b/Session/Storage/MockArraySessionStorage.php @@ -152,7 +152,7 @@ public function setName($name) 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; diff --git a/Session/Storage/MockFileSessionStorage.php b/Session/Storage/MockFileSessionStorage.php index 9bbd1baf2..c5b1d1a36 100644 --- a/Session/Storage/MockFileSessionStorage.php +++ b/Session/Storage/MockFileSessionStorage.php @@ -38,7 +38,7 @@ public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag } 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,7 +88,7 @@ public function regenerate($destroy = false, $lifetime = null) 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.'); } $data = $this->data; diff --git a/Session/Storage/NativeSessionStorage.php b/Session/Storage/NativeSessionStorage.php index 7502897a4..33a146553 100644 --- a/Session/Storage/NativeSessionStorage.php +++ b/Session/Storage/NativeSessionStorage.php @@ -152,7 +152,7 @@ public function start() // 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) { diff --git a/Session/Storage/Proxy/AbstractProxy.php b/Session/Storage/Proxy/AbstractProxy.php index cd0635a16..b9c2682b3 100644 --- a/Session/Storage/Proxy/AbstractProxy.php +++ b/Session/Storage/Proxy/AbstractProxy.php @@ -88,7 +88,7 @@ public function getId() 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); @@ -114,7 +114,7 @@ public function getName() 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); From 01887e8604ffa17e5078c7815279282796711689 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 15 Mar 2020 11:08:38 +0100 Subject: [PATCH 3/8] Add missing dots at the end of exception messages --- Request.php | 2 +- Session/Storage/Handler/RedisSessionHandler.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Request.php b/Request.php index 6690b9b22..bff1c0cbf 100644 --- a/Request.php +++ b/Request.php @@ -725,7 +725,7 @@ public function getSession() if (null === $session) { @trigger_error(sprintf('Calling "%s()" when no session has been set is deprecated since Symfony 4.1 and will throw an exception in 5.0. Use "hasSession()" instead.', __METHOD__), E_USER_DEPRECATED); - // throw new \BadMethodCallException('Session has not been set'); + // throw new \BadMethodCallException('Session has not been set.'); } return $session; diff --git a/Session/Storage/Handler/RedisSessionHandler.php b/Session/Storage/Handler/RedisSessionHandler.php index e5be90d5f..1b83de09b 100644 --- a/Session/Storage/Handler/RedisSessionHandler.php +++ b/Session/Storage/Handler/RedisSessionHandler.php @@ -54,11 +54,11 @@ public function __construct($redis, array $options = []) !$redis instanceof RedisProxy && !$redis instanceof RedisClusterProxy ) { - throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, %s given', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); + throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, %s given.', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); } if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) { - 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->redis = $redis; From 13f9b084cddc2dd0a77836c703594ad970f09291 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 16 Mar 2020 08:32:23 +0100 Subject: [PATCH 4/8] Fix quotes in exception messages --- HeaderBag.php | 2 +- Session/Storage/MockArraySessionStorage.php | 2 +- Session/Storage/NativeSessionStorage.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HeaderBag.php b/HeaderBag.php index 35bd6ad8f..0b19faec5 100644 --- a/HeaderBag.php +++ b/HeaderBag.php @@ -225,7 +225,7 @@ public function getDate($key, \DateTime $default = null) } if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) { - throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value)); + throw new \RuntimeException(sprintf('The "%s" HTTP header is not parseable (%s).', $key, $value)); } return $date; diff --git a/Session/Storage/MockArraySessionStorage.php b/Session/Storage/MockArraySessionStorage.php index 969f272fb..5474d92e3 100644 --- a/Session/Storage/MockArraySessionStorage.php +++ b/Session/Storage/MockArraySessionStorage.php @@ -190,7 +190,7 @@ public function registerBag(SessionBagInterface $bag) 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) { diff --git a/Session/Storage/NativeSessionStorage.php b/Session/Storage/NativeSessionStorage.php index 33a146553..5165b5d0d 100644 --- a/Session/Storage/NativeSessionStorage.php +++ b/Session/Storage/NativeSessionStorage.php @@ -314,7 +314,7 @@ public function registerBag(SessionBagInterface $bag) 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()) { From f4dc52b1eb2b19ebf7c697d0017027aef6996c9c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 16 Mar 2020 11:25:47 +0100 Subject: [PATCH 5/8] Fix quotes in exception messages --- Session/Storage/Handler/RedisSessionHandler.php | 2 +- Session/Storage/Handler/SessionHandlerFactory.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Session/Storage/Handler/RedisSessionHandler.php b/Session/Storage/Handler/RedisSessionHandler.php index 1b83de09b..8fc19c6bc 100644 --- a/Session/Storage/Handler/RedisSessionHandler.php +++ b/Session/Storage/Handler/RedisSessionHandler.php @@ -54,7 +54,7 @@ public function __construct($redis, array $options = []) !$redis instanceof RedisProxy && !$redis instanceof RedisClusterProxy ) { - throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, %s given.', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); + throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, "%s" given.', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); } if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) { diff --git a/Session/Storage/Handler/SessionHandlerFactory.php b/Session/Storage/Handler/SessionHandlerFactory.php index b92bd0ffd..a5ebd29eb 100644 --- a/Session/Storage/Handler/SessionHandlerFactory.php +++ b/Session/Storage/Handler/SessionHandlerFactory.php @@ -27,7 +27,7 @@ class SessionHandlerFactory public static function createHandler($connection): AbstractSessionHandler { if (!\is_string($connection) && !\is_object($connection)) { - throw new \TypeError(sprintf('Argument 1 passed to %s() must be a string or a connection object, %s given.', __METHOD__, \gettype($connection))); + throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a string or a connection object, "%s" given.', __METHOD__, \gettype($connection))); } switch (true) { @@ -46,7 +46,7 @@ public static function createHandler($connection): AbstractSessionHandler return new PdoSessionHandler($connection); case !\is_string($connection): - throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection))); + throw new \InvalidArgumentException(sprintf('Unsupported Connection: "%s".', \get_class($connection))); case 0 === strpos($connection, 'file://'): return new StrictSessionHandler(new NativeFileSessionHandler(substr($connection, 7))); @@ -80,6 +80,6 @@ public static function createHandler($connection): AbstractSessionHandler return new PdoSessionHandler($connection); } - throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection)); + throw new \InvalidArgumentException(sprintf('Unsupported Connection: "%s".', $connection)); } } From ff006c7736a22ffac6c9e76db451db026e414325 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 16 Mar 2020 17:13:17 +0100 Subject: [PATCH 6/8] Fix more quotes in exception messages --- Session/Storage/Handler/RedisSessionHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Session/Storage/Handler/RedisSessionHandler.php b/Session/Storage/Handler/RedisSessionHandler.php index 8fc19c6bc..699d6da6f 100644 --- a/Session/Storage/Handler/RedisSessionHandler.php +++ b/Session/Storage/Handler/RedisSessionHandler.php @@ -54,7 +54,7 @@ public function __construct($redis, array $options = []) !$redis instanceof RedisProxy && !$redis instanceof RedisClusterProxy ) { - throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, "%s" given.', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); + throw new \InvalidArgumentException(sprintf('"%s()" expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, "%s" given.', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); } if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) { From 109ac257dfc057f3301034947e22c823f3f6d6e8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 24 Sep 2019 13:01:54 +0200 Subject: [PATCH 7/8] [DI] fix preloading script generation --- AcceptHeader.php | 3 +++ Request.php | 8 ++++++++ Response.php | 3 +++ Session/Session.php | 5 +++++ Session/Storage/NativeSessionStorage.php | 5 +++++ 5 files changed, 24 insertions(+) diff --git a/AcceptHeader.php b/AcceptHeader.php index bbbd62a6d..c3c8d0c35 100644 --- a/AcceptHeader.php +++ b/AcceptHeader.php @@ -11,6 +11,9 @@ namespace Symfony\Component\HttpFoundation; +// Help opcache.preload discover always-needed symbols +class_exists(AcceptHeaderItem::class); + /** * Represents an Accept-* header. * diff --git a/Request.php b/Request.php index bff1c0cbf..a5e50eb79 100644 --- a/Request.php +++ b/Request.php @@ -15,6 +15,14 @@ use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; use Symfony\Component\HttpFoundation\Session\SessionInterface; +// Help opcache.preload discover always-needed symbols +class_exists(AcceptHeader::class); +class_exists(FileBag::class); +class_exists(HeaderBag::class); +class_exists(HeaderUtils::class); +class_exists(ParameterBag::class); +class_exists(ServerBag::class); + /** * Request represents an HTTP request. * diff --git a/Response.php b/Response.php index b267cd8ce..714109d53 100644 --- a/Response.php +++ b/Response.php @@ -11,6 +11,9 @@ namespace Symfony\Component\HttpFoundation; +// Help opcache.preload discover always-needed symbols +class_exists(ResponseHeaderBag::class); + /** * Response represents an HTTP response. * diff --git a/Session/Session.php b/Session/Session.php index 2192c629e..b6973aaab 100644 --- a/Session/Session.php +++ b/Session/Session.php @@ -18,6 +18,11 @@ use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; +// Help opcache.preload discover always-needed symbols +class_exists(AttributeBag::class); +class_exists(FlashBag::class); +class_exists(SessionBagProxy::class); + /** * @author Fabien Potencier * @author Drak diff --git a/Session/Storage/NativeSessionStorage.php b/Session/Storage/NativeSessionStorage.php index 93a5d54e4..54933d953 100644 --- a/Session/Storage/NativeSessionStorage.php +++ b/Session/Storage/NativeSessionStorage.php @@ -17,6 +17,11 @@ use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; +// Help opcache.preload discover always-needed symbols +class_exists(MetadataBag::class); +class_exists(StrictSessionHandler::class); +class_exists(SessionHandlerProxy::class); + /** * This provides a base class for session attribute storage. * From a8833c56f6a4abcf17a319d830d71fdb0ba93675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20P=C3=A9delagrabe?= Date: Mon, 23 Mar 2020 11:02:50 +0100 Subject: [PATCH 8/8] [Http Foundation] Fix clear cookie samesite --- ResponseHeaderBag.php | 7 +++++-- Tests/ResponseHeaderBagTest.php | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ResponseHeaderBag.php b/ResponseHeaderBag.php index e11b98a10..9a6f87648 100644 --- a/ResponseHeaderBag.php +++ b/ResponseHeaderBag.php @@ -244,10 +244,13 @@ public function getCookies($format = self::COOKIES_FLAT) * @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)); } /** diff --git a/Tests/ResponseHeaderBagTest.php b/Tests/ResponseHeaderBagTest.php index 4e3a6c82b..632c96df5 100644 --- a/Tests/ResponseHeaderBagTest.php +++ b/Tests/ResponseHeaderBagTest.php @@ -128,6 +128,14 @@ public function testClearCookieSecureNotHttpOnly() $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([]); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy