updated-packages
This commit is contained in:
3
vendor/symfony/http-foundation/.gitignore
vendored
3
vendor/symfony/http-foundation/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(AcceptHeaderItem::class);
|
||||
|
||||
/**
|
||||
* Represents an Accept-* header.
|
||||
*
|
||||
@@ -24,7 +27,7 @@ class AcceptHeader
|
||||
/**
|
||||
* @var AcceptHeaderItem[]
|
||||
*/
|
||||
private $items = array();
|
||||
private $items = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
@@ -153,7 +156,7 @@ class AcceptHeader
|
||||
/**
|
||||
* Sorts items by descending quality.
|
||||
*/
|
||||
private function sort()
|
||||
private function sort(): void
|
||||
{
|
||||
if (!$this->sorted) {
|
||||
uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) {
|
||||
|
@@ -21,9 +21,9 @@ class AcceptHeaderItem
|
||||
private $value;
|
||||
private $quality = 1.0;
|
||||
private $index = 0;
|
||||
private $attributes = array();
|
||||
private $attributes = [];
|
||||
|
||||
public function __construct(string $value, array $attributes = array())
|
||||
public function __construct(string $value, array $attributes = [])
|
||||
{
|
||||
$this->value = $value;
|
||||
foreach ($attributes as $name => $value) {
|
||||
@@ -157,7 +157,7 @@ class AcceptHeaderItem
|
||||
*/
|
||||
public function getAttribute($name, $default = null)
|
||||
{
|
||||
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
|
||||
return $this->attributes[$name] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -11,9 +11,13 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ApacheRequest::class, Request::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Request represents an HTTP request from an Apache server.
|
||||
*
|
||||
* @deprecated since Symfony 4.4. Use the Request class instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ApacheRequest extends Request
|
||||
@@ -33,7 +37,7 @@ class ApacheRequest extends Request
|
||||
{
|
||||
$baseUrl = $this->server->get('SCRIPT_NAME');
|
||||
|
||||
if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) {
|
||||
if (!str_contains($this->server->get('REQUEST_URI'), $baseUrl)) {
|
||||
// assume mod_rewrite
|
||||
return rtrim(\dirname($baseUrl), '/\\');
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ class BinaryFileResponse extends Response
|
||||
protected $offset = 0;
|
||||
protected $maxlen = -1;
|
||||
protected $deleteFileAfterSend = false;
|
||||
protected $chunkSize = 8 * 1024;
|
||||
|
||||
/**
|
||||
* @param \SplFileInfo|string $file The file to stream
|
||||
@@ -44,7 +45,7 @@ class BinaryFileResponse extends Response
|
||||
* @param bool $autoEtag Whether the ETag header should be automatically set
|
||||
* @param bool $autoLastModified Whether the Last-Modified header should be automatically set
|
||||
*/
|
||||
public function __construct($file, int $status = 200, array $headers = array(), bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
|
||||
public function __construct($file, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
|
||||
{
|
||||
parent::__construct(null, $status, $headers);
|
||||
|
||||
@@ -66,7 +67,7 @@ class BinaryFileResponse extends Response
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
|
||||
public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
|
||||
{
|
||||
return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
|
||||
}
|
||||
@@ -124,6 +125,22 @@ class BinaryFileResponse extends Response
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response stream chunk size.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setChunkSize(int $chunkSize): self
|
||||
{
|
||||
if ($chunkSize < 1 || $chunkSize > \PHP_INT_MAX) {
|
||||
throw new \LogicException('The chunk size of a BinaryFileResponse cannot be less than 1 or greater than PHP_INT_MAX.');
|
||||
}
|
||||
|
||||
$this->chunkSize = $chunkSize;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically sets the Last-Modified header according the file modification date.
|
||||
*/
|
||||
@@ -159,7 +176,7 @@ class BinaryFileResponse extends Response
|
||||
$filename = $this->file->getFilename();
|
||||
}
|
||||
|
||||
if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) {
|
||||
if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || str_contains($filename, '%'))) {
|
||||
$encoding = mb_detect_encoding($filename, null, true) ?: '8bit';
|
||||
|
||||
for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) {
|
||||
@@ -184,15 +201,19 @@ class BinaryFileResponse extends Response
|
||||
*/
|
||||
public function prepare(Request $request)
|
||||
{
|
||||
if ($this->isInformational() || $this->isEmpty()) {
|
||||
parent::prepare($request);
|
||||
|
||||
$this->maxlen = 0;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (!$this->headers->has('Content-Type')) {
|
||||
$this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
|
||||
}
|
||||
|
||||
if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
|
||||
$this->setProtocolVersion('1.1');
|
||||
}
|
||||
|
||||
$this->ensureIEOverSSLCompatibility($request);
|
||||
parent::prepare($request);
|
||||
|
||||
$this->offset = 0;
|
||||
$this->maxlen = -1;
|
||||
@@ -200,11 +221,12 @@ class BinaryFileResponse extends Response
|
||||
if (false === $fileSize = $this->file->getSize()) {
|
||||
return $this;
|
||||
}
|
||||
$this->headers->remove('Transfer-Encoding');
|
||||
$this->headers->set('Content-Length', $fileSize);
|
||||
|
||||
if (!$this->headers->has('Accept-Ranges')) {
|
||||
// Only accept ranges on safe HTTP methods
|
||||
$this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
|
||||
$this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none');
|
||||
}
|
||||
|
||||
if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
|
||||
@@ -217,54 +239,66 @@ class BinaryFileResponse extends Response
|
||||
}
|
||||
if ('x-accel-redirect' === strtolower($type)) {
|
||||
// Do X-Accel-Mapping substitutions.
|
||||
// @link http://wiki.nginx.org/X-accel#X-Accel-Redirect
|
||||
// @link https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect
|
||||
$parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',=');
|
||||
foreach ($parts as $part) {
|
||||
list($pathPrefix, $location) = $part;
|
||||
[$pathPrefix, $location] = $part;
|
||||
if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
|
||||
$path = $location.substr($path, \strlen($pathPrefix));
|
||||
// Only set X-Accel-Redirect header if a valid URI can be produced
|
||||
// as nginx does not serve arbitrary file paths.
|
||||
$this->headers->set($type, $path);
|
||||
$this->maxlen = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->headers->set($type, $path);
|
||||
$this->maxlen = 0;
|
||||
}
|
||||
$this->headers->set($type, $path);
|
||||
$this->maxlen = 0;
|
||||
} elseif ($request->headers->has('Range')) {
|
||||
} elseif ($request->headers->has('Range') && $request->isMethod('GET')) {
|
||||
// Process the range headers.
|
||||
if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
|
||||
$range = $request->headers->get('Range');
|
||||
|
||||
list($start, $end) = explode('-', substr($range, 6), 2) + array(0);
|
||||
if (str_starts_with($range, 'bytes=')) {
|
||||
[$start, $end] = explode('-', substr($range, 6), 2) + [0];
|
||||
|
||||
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
|
||||
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
|
||||
|
||||
if ('' === $start) {
|
||||
$start = $fileSize - $end;
|
||||
$end = $fileSize - 1;
|
||||
} else {
|
||||
$start = (int) $start;
|
||||
}
|
||||
if ('' === $start) {
|
||||
$start = $fileSize - $end;
|
||||
$end = $fileSize - 1;
|
||||
} else {
|
||||
$start = (int) $start;
|
||||
}
|
||||
|
||||
if ($start <= $end) {
|
||||
if ($start < 0 || $end > $fileSize - 1) {
|
||||
$this->setStatusCode(416);
|
||||
$this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
|
||||
} elseif (0 !== $start || $end !== $fileSize - 1) {
|
||||
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
|
||||
$this->offset = $start;
|
||||
if ($start <= $end) {
|
||||
$end = min($end, $fileSize - 1);
|
||||
if ($start < 0 || $start > $end) {
|
||||
$this->setStatusCode(416);
|
||||
$this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
|
||||
} elseif ($end - $start < $fileSize - 1) {
|
||||
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
|
||||
$this->offset = $start;
|
||||
|
||||
$this->setStatusCode(206);
|
||||
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
|
||||
$this->headers->set('Content-Length', $end - $start + 1);
|
||||
$this->setStatusCode(206);
|
||||
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
|
||||
$this->headers->set('Content-Length', $end - $start + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->isMethod('HEAD')) {
|
||||
$this->maxlen = 0;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function hasValidIfRangeHeader($header)
|
||||
private function hasValidIfRangeHeader(?string $header): bool
|
||||
{
|
||||
if ($this->getEtag() === $header) {
|
||||
return true;
|
||||
@@ -284,24 +318,42 @@ class BinaryFileResponse extends Response
|
||||
*/
|
||||
public function sendContent()
|
||||
{
|
||||
if (!$this->isSuccessful()) {
|
||||
return parent::sendContent();
|
||||
}
|
||||
try {
|
||||
if (!$this->isSuccessful()) {
|
||||
return parent::sendContent();
|
||||
}
|
||||
|
||||
if (0 === $this->maxlen) {
|
||||
return $this;
|
||||
}
|
||||
if (0 === $this->maxlen) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$out = fopen('php://output', 'wb');
|
||||
$file = fopen($this->file->getPathname(), 'rb');
|
||||
$out = fopen('php://output', 'w');
|
||||
$file = fopen($this->file->getPathname(), 'r');
|
||||
|
||||
stream_copy_to_stream($file, $out, $this->maxlen, $this->offset);
|
||||
ignore_user_abort(true);
|
||||
|
||||
fclose($out);
|
||||
fclose($file);
|
||||
if (0 !== $this->offset) {
|
||||
fseek($file, $this->offset);
|
||||
}
|
||||
|
||||
if ($this->deleteFileAfterSend) {
|
||||
unlink($this->file->getPathname());
|
||||
$length = $this->maxlen;
|
||||
while ($length && !feof($file)) {
|
||||
$read = ($length > $this->chunkSize) ? $this->chunkSize : $length;
|
||||
$length -= $read;
|
||||
|
||||
stream_copy_to_stream($file, $out, $read);
|
||||
|
||||
if (connection_aborted()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose($out);
|
||||
fclose($file);
|
||||
} finally {
|
||||
if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) {
|
||||
unlink($this->file->getPathname());
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -317,12 +369,12 @@ class BinaryFileResponse extends Response
|
||||
if (null !== $content) {
|
||||
throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
@@ -338,7 +390,7 @@ class BinaryFileResponse extends Response
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is set to true, the file will be unlinked after the request is send
|
||||
* If this is set to true, the file will be unlinked after the request is sent
|
||||
* Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
|
||||
*
|
||||
* @param bool $shouldDelete
|
||||
|
32
vendor/symfony/http-foundation/CHANGELOG.md
vendored
32
vendor/symfony/http-foundation/CHANGELOG.md
vendored
@@ -1,6 +1,32 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* passing arguments to `Request::isMethodSafe()` is deprecated.
|
||||
* `ApacheRequest` is deprecated, use the `Request` class instead.
|
||||
* passing a third argument to `HeaderBag::get()` is deprecated, use method `all()` instead
|
||||
* [BC BREAK] `PdoSessionHandler` with MySQL changed the type of the lifetime column,
|
||||
make sure to run `ALTER TABLE sessions MODIFY sess_lifetime INTEGER UNSIGNED NOT NULL` to
|
||||
update your database.
|
||||
* `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column,
|
||||
make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database
|
||||
to speed up garbage collection of expired sessions.
|
||||
* added `SessionHandlerFactory` to create session handlers with a DSN
|
||||
* added `IpUtils::anonymize()` to help with GDPR compliance.
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* added PHPUnit constraints: `RequestAttributeValueSame`, `ResponseCookieValueSame`, `ResponseHasCookie`,
|
||||
`ResponseHasHeader`, `ResponseHeaderSame`, `ResponseIsRedirected`, `ResponseIsSuccessful`, and `ResponseStatusCodeSame`
|
||||
* deprecated `MimeTypeGuesserInterface` and `ExtensionGuesserInterface` in favor of `Symfony\Component\Mime\MimeTypesInterface`.
|
||||
* deprecated `MimeType` and `MimeTypeExtensionGuesser` in favor of `Symfony\Component\Mime\MimeTypes`.
|
||||
* deprecated `FileBinaryMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileBinaryMimeTypeGuesser`.
|
||||
* deprecated `FileinfoMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileinfoMimeTypeGuesser`.
|
||||
* added `UrlHelper` that allows to get an absolute URL and a relative path for a given path
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
@@ -67,7 +93,7 @@ CHANGELOG
|
||||
-----
|
||||
|
||||
* the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument,
|
||||
see http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info,
|
||||
see https://symfony.com/doc/current/deployment/proxies.html for more info,
|
||||
* deprecated the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods,
|
||||
* added `File\Stream`, to be passed to `BinaryFileResponse` when the size of the served file is unknown,
|
||||
disabling `Range` and `Content-Length` handling, switching to chunked encoding instead
|
||||
@@ -190,10 +216,10 @@ CHANGELOG
|
||||
* Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This
|
||||
implementation is ESI compatible.
|
||||
* Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire
|
||||
behaviour of messages auto expiring after one page page load. Messages must
|
||||
behavior of messages auto expiring after one page page load. Messages must
|
||||
be retrieved by `get()` or `all()`.
|
||||
* Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate
|
||||
attributes storage behaviour from 2.0.x (default).
|
||||
attributes storage behavior from 2.0.x (default).
|
||||
* Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for
|
||||
namespace session attributes.
|
||||
* Flash API can stores messages in an array so there may be multiple messages
|
||||
|
28
vendor/symfony/http-foundation/Cookie.php
vendored
28
vendor/symfony/http-foundation/Cookie.php
vendored
@@ -18,6 +18,10 @@ namespace Symfony\Component\HttpFoundation;
|
||||
*/
|
||||
class Cookie
|
||||
{
|
||||
public const SAMESITE_NONE = 'none';
|
||||
public const SAMESITE_LAX = 'lax';
|
||||
public const SAMESITE_STRICT = 'strict';
|
||||
|
||||
protected $name;
|
||||
protected $value;
|
||||
protected $domain;
|
||||
@@ -25,12 +29,14 @@ class Cookie
|
||||
protected $path;
|
||||
protected $secure;
|
||||
protected $httpOnly;
|
||||
|
||||
private $raw;
|
||||
private $sameSite;
|
||||
private $secureDefault = false;
|
||||
|
||||
const SAMESITE_LAX = 'lax';
|
||||
const SAMESITE_STRICT = 'strict';
|
||||
private static $reservedCharsList = "=,; \t\r\n\v\f";
|
||||
private const RESERVED_CHARS_FROM = ['=', ',', ';', ' ', "\t", "\r", "\n", "\v", "\f"];
|
||||
private const RESERVED_CHARS_TO = ['%3D', '%2C', '%3B', '%20', '%09', '%0D', '%0A', '%0B', '%0C'];
|
||||
|
||||
/**
|
||||
* Creates cookie from raw header string.
|
||||
@@ -42,7 +48,7 @@ class Cookie
|
||||
*/
|
||||
public static function fromString($cookie, $decode = false)
|
||||
{
|
||||
$data = array(
|
||||
$data = [
|
||||
'expires' => 0,
|
||||
'path' => '/',
|
||||
'domain' => null,
|
||||
@@ -50,7 +56,7 @@ class Cookie
|
||||
'httponly' => false,
|
||||
'raw' => !$decode,
|
||||
'samesite' => null,
|
||||
);
|
||||
];
|
||||
|
||||
$parts = HeaderUtils::split($cookie, ';=');
|
||||
$part = array_shift($parts);
|
||||
@@ -88,11 +94,11 @@ class Cookie
|
||||
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);
|
||||
@trigger_error(sprintf('The default value of the "$secure" and "$samesite" arguments of "%s"\'s constructor will respectively change from "false" to "null" and from "null" to "lax" in Symfony 5.0, you should define their values explicitly or use "Cookie::create()" instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
// from PHP source code
|
||||
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
|
||||
if ($raw && false !== strpbrk($name, self::$reservedCharsList)) {
|
||||
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
|
||||
}
|
||||
|
||||
@@ -126,7 +132,7 @@ class Cookie
|
||||
$sameSite = strtolower($sameSite);
|
||||
}
|
||||
|
||||
if (!\in_array($sameSite, array(self::SAMESITE_LAX, self::SAMESITE_STRICT, null), true)) {
|
||||
if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE, null], true)) {
|
||||
throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
|
||||
}
|
||||
|
||||
@@ -140,7 +146,13 @@ class Cookie
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
|
||||
if ($this->isRaw()) {
|
||||
$str = $this->getName();
|
||||
} else {
|
||||
$str = str_replace(self::RESERVED_CHARS_FROM, self::RESERVED_CHARS_TO, $this->getName());
|
||||
}
|
||||
|
||||
$str .= '=';
|
||||
|
||||
if ('' === (string) $this->getValue()) {
|
||||
$str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0';
|
||||
|
@@ -35,13 +35,13 @@ class ExpressionRequestMatcher extends RequestMatcher
|
||||
throw new \LogicException('Unable to match the request as the expression language is not available.');
|
||||
}
|
||||
|
||||
return $this->language->evaluate($this->expression, array(
|
||||
return $this->language->evaluate($this->expression, [
|
||||
'request' => $request,
|
||||
'method' => $request->getMethod(),
|
||||
'path' => rawurldecode($request->getPathInfo()),
|
||||
'host' => $request->getHost(),
|
||||
'ip' => $request->getClientIp(),
|
||||
'attributes' => $request->attributes->all(),
|
||||
)) && parent::matches($request);
|
||||
]) && parent::matches($request);
|
||||
}
|
||||
}
|
||||
|
@@ -18,9 +18,6 @@ namespace Symfony\Component\HttpFoundation\File\Exception;
|
||||
*/
|
||||
class AccessDeniedException extends FileException
|
||||
{
|
||||
/**
|
||||
* @param string $path The path to the accessed file
|
||||
*/
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(sprintf('The file %s could not be accessed', $path));
|
||||
|
@@ -18,9 +18,6 @@ namespace Symfony\Component\HttpFoundation\File\Exception;
|
||||
*/
|
||||
class FileNotFoundException extends FileException
|
||||
{
|
||||
/**
|
||||
* @param string $path The path to the file that was not found
|
||||
*/
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(sprintf('The file "%s" does not exist', $path));
|
||||
|
33
vendor/symfony/http-foundation/File/File.php
vendored
33
vendor/symfony/http-foundation/File/File.php
vendored
@@ -13,8 +13,7 @@ namespace Symfony\Component\HttpFoundation\File;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
/**
|
||||
* A file in the file system.
|
||||
@@ -50,33 +49,28 @@ class File extends \SplFileInfo
|
||||
*
|
||||
* @return string|null The guessed extension or null if it cannot be guessed
|
||||
*
|
||||
* @see ExtensionGuesser
|
||||
* @see MimeTypes
|
||||
* @see getMimeType()
|
||||
*/
|
||||
public function guessExtension()
|
||||
{
|
||||
$type = $this->getMimeType();
|
||||
$guesser = ExtensionGuesser::getInstance();
|
||||
|
||||
return $guesser->guess($type);
|
||||
return MimeTypes::getDefault()->getExtensions($this->getMimeType())[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime type of the file.
|
||||
*
|
||||
* The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(),
|
||||
* mime_content_type() and the system binary "file" (in this order), depending on
|
||||
* which of those are available.
|
||||
* The mime type is guessed using a MimeTypeGuesserInterface instance,
|
||||
* which uses finfo_file() then the "file" system binary,
|
||||
* depending on which of those are available.
|
||||
*
|
||||
* @return string|null The guessed mime type (e.g. "application/pdf")
|
||||
*
|
||||
* @see MimeTypeGuesser
|
||||
* @see MimeTypes
|
||||
*/
|
||||
public function getMimeType()
|
||||
{
|
||||
$guesser = MimeTypeGuesser::getInstance();
|
||||
|
||||
return $guesser->guess($this->getPathname());
|
||||
return MimeTypes::getDefault()->guessMimeType($this->getPathname());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +91,7 @@ class File extends \SplFileInfo
|
||||
$renamed = rename($this->getPathname(), $target);
|
||||
restore_error_handler();
|
||||
if (!$renamed) {
|
||||
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
|
||||
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s).', $this->getPathname(), $target, strip_tags($error)));
|
||||
}
|
||||
|
||||
@chmod($target, 0666 & ~umask());
|
||||
@@ -105,14 +99,17 @@ class File extends \SplFileInfo
|
||||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
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));
|
||||
@@ -125,7 +122,7 @@ class File extends \SplFileInfo
|
||||
*
|
||||
* @param string $name The new file name
|
||||
*
|
||||
* @return string containing
|
||||
* @return string
|
||||
*/
|
||||
protected function getName($name)
|
||||
{
|
||||
|
@@ -11,6 +11,10 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', ExtensionGuesser::class, MimeTypes::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* A singleton mime type to file extension guesser.
|
||||
*
|
||||
@@ -22,6 +26,8 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
* $guesser->register(new MyCustomExtensionGuesser());
|
||||
*
|
||||
* The last registered guesser is preferred over previously registered ones.
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link MimeTypes} instead
|
||||
*/
|
||||
class ExtensionGuesser implements ExtensionGuesserInterface
|
||||
{
|
||||
@@ -37,7 +43,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guessers = array();
|
||||
protected $guessers = [];
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
@@ -90,5 +96,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface
|
||||
return $extension;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -11,8 +11,12 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\Mime\MimeTypesInterface;
|
||||
|
||||
/**
|
||||
* Guesses the file extension corresponding to a given mime type.
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead
|
||||
*/
|
||||
interface ExtensionGuesserInterface
|
||||
{
|
||||
|
@@ -13,11 +13,16 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\Mime\FileBinaryMimeTypeGuesser as NewFileBinaryMimeTypeGuesser;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileBinaryMimeTypeGuesser::class, NewFileBinaryMimeTypeGuesser::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Guesses the mime type with the binary "file" (only available on *nix).
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link NewFileBinaryMimeTypeGuesser} instead
|
||||
*/
|
||||
class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
{
|
||||
@@ -31,7 +36,7 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
*
|
||||
* @param string $cmd The command to run to get the mime type of a file
|
||||
*/
|
||||
public function __construct(string $cmd = 'file -b --mime %s 2>/dev/null')
|
||||
public function __construct(string $cmd = 'file -b --mime -- %s 2>/dev/null')
|
||||
{
|
||||
$this->cmd = $cmd;
|
||||
}
|
||||
@@ -74,24 +79,24 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
}
|
||||
|
||||
if (!self::isSupported()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
// need to use --mime instead of -i. see #6641
|
||||
passthru(sprintf($this->cmd, escapeshellarg($path)), $return);
|
||||
passthru(sprintf($this->cmd, escapeshellarg((str_starts_with($path, '-') ? './' : '').$path)), $return);
|
||||
if ($return > 0) {
|
||||
ob_end_clean();
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = trim(ob_get_clean());
|
||||
|
||||
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
|
||||
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) {
|
||||
// it's not a type, but an error message
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $match[1];
|
||||
|
@@ -13,11 +13,16 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\Mime\FileinfoMimeTypeGuesser as NewFileinfoMimeTypeGuesser;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileinfoMimeTypeGuesser::class, NewFileinfoMimeTypeGuesser::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Guesses the mime type using the PECL extension FileInfo.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link NewFileinfoMimeTypeGuesser} instead
|
||||
*/
|
||||
class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
{
|
||||
@@ -26,7 +31,7 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
/**
|
||||
* @param string $magicFile A magic file to use with the finfo instance
|
||||
*
|
||||
* @see http://www.php.net/manual/en/function.finfo-open.php
|
||||
* @see https://php.net/finfo-open
|
||||
*/
|
||||
public function __construct(string $magicFile = null)
|
||||
{
|
||||
@@ -57,13 +62,19 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
}
|
||||
|
||||
if (!self::isSupported()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
|
||||
return;
|
||||
if (!$finfo = new \finfo(\FILEINFO_MIME_TYPE, $this->magicFile)) {
|
||||
return null;
|
||||
}
|
||||
$mimeType = $finfo->file($path);
|
||||
|
||||
if ($mimeType && 0 === (\strlen($mimeType) % 2)) {
|
||||
$mimeStart = substr($mimeType, 0, \strlen($mimeType) >> 1);
|
||||
$mimeType = $mimeStart.$mimeStart === $mimeType ? $mimeStart : $mimeType;
|
||||
}
|
||||
|
||||
return $finfo->file($path);
|
||||
return $mimeType;
|
||||
}
|
||||
}
|
||||
|
@@ -11,8 +11,14 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeExtensionGuesser::class, MimeTypes::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Provides a best-guess mapping of mime type to file extension.
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link MimeTypes} instead
|
||||
*/
|
||||
class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
{
|
||||
@@ -20,11 +26,11 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
* A map of mime types and their default extensions.
|
||||
*
|
||||
* This list has been placed under the public domain by the Apache HTTPD project.
|
||||
* This list has been updated from upstream on 2013-04-23.
|
||||
* This list has been updated from upstream on 2019-01-14.
|
||||
*
|
||||
* @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
* @see https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
*/
|
||||
protected $defaultExtensions = array(
|
||||
protected $defaultExtensions = [
|
||||
'application/andrew-inset' => 'ez',
|
||||
'application/applixware' => 'aw',
|
||||
'application/atom+xml' => 'atom',
|
||||
@@ -618,8 +624,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
'audio/adpcm' => 'adp',
|
||||
'audio/basic' => 'au',
|
||||
'audio/midi' => 'mid',
|
||||
'audio/mp4' => 'mp4a',
|
||||
'audio/mpeg' => 'mpga',
|
||||
'audio/mp4' => 'm4a',
|
||||
'audio/mpeg' => 'mp3',
|
||||
'audio/ogg' => 'oga',
|
||||
'audio/s3m' => 's3m',
|
||||
'audio/silk' => 'sil',
|
||||
@@ -639,6 +645,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
'audio/x-aiff' => 'aif',
|
||||
'audio/x-caf' => 'caf',
|
||||
'audio/x-flac' => 'flac',
|
||||
'audio/x-hx-aac-adts' => 'aac',
|
||||
'audio/x-matroska' => 'mka',
|
||||
'audio/x-mpegurl' => 'm3u',
|
||||
'audio/x-ms-wax' => 'wax',
|
||||
@@ -653,6 +660,11 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
'chemical/x-cml' => 'cml',
|
||||
'chemical/x-csml' => 'csml',
|
||||
'chemical/x-xyz' => 'xyz',
|
||||
'font/collection' => 'ttc',
|
||||
'font/otf' => 'otf',
|
||||
'font/ttf' => 'ttf',
|
||||
'font/woff' => 'woff',
|
||||
'font/woff2' => 'woff2',
|
||||
'image/bmp' => 'bmp',
|
||||
'image/x-ms-bmp' => 'bmp',
|
||||
'image/cgm' => 'cgm',
|
||||
@@ -669,8 +681,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
'image/tiff' => 'tiff',
|
||||
'image/vnd.adobe.photoshop' => 'psd',
|
||||
'image/vnd.dece.graphic' => 'uvi',
|
||||
'image/vnd.dvb.subtitle' => 'sub',
|
||||
'image/vnd.djvu' => 'djvu',
|
||||
'image/vnd.dvb.subtitle' => 'sub',
|
||||
'image/vnd.dwg' => 'dwg',
|
||||
'image/vnd.dxf' => 'dxf',
|
||||
'image/vnd.fastbidsheet' => 'fbs',
|
||||
@@ -732,8 +744,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
'text/vcard' => 'vcard',
|
||||
'text/vnd.curl' => 'curl',
|
||||
'text/vnd.curl.dcurl' => 'dcurl',
|
||||
'text/vnd.curl.scurl' => 'scurl',
|
||||
'text/vnd.curl.mcurl' => 'mcurl',
|
||||
'text/vnd.curl.scurl' => 'scurl',
|
||||
'text/vnd.dvb.subtitle' => 'sub',
|
||||
'text/vnd.fly' => 'fly',
|
||||
'text/vnd.fmi.flexstor' => 'flx',
|
||||
@@ -747,10 +759,10 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
'text/x-asm' => 's',
|
||||
'text/x-c' => 'c',
|
||||
'text/x-fortran' => 'f',
|
||||
'text/x-pascal' => 'p',
|
||||
'text/x-java-source' => 'java',
|
||||
'text/x-opml' => 'opml',
|
||||
'text/x-nfo' => 'nfo',
|
||||
'text/x-opml' => 'opml',
|
||||
'text/x-pascal' => 'p',
|
||||
'text/x-setext' => 'etx',
|
||||
'text/x-sfv' => 'sfv',
|
||||
'text/x-uuencode' => 'uu',
|
||||
@@ -796,13 +808,19 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
'video/x-sgi-movie' => 'movie',
|
||||
'video/x-smv' => 'smv',
|
||||
'x-conference/x-cooltalk' => 'ice',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function guess($mimeType)
|
||||
{
|
||||
return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null;
|
||||
if (isset($this->defaultExtensions[$mimeType])) {
|
||||
return $this->defaultExtensions[$mimeType];
|
||||
}
|
||||
|
||||
$lcMimeType = strtolower($mimeType);
|
||||
|
||||
return $this->defaultExtensions[$lcMimeType] ?? null;
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,9 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeGuesser::class, MimeTypes::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* A singleton mime type guesser.
|
||||
@@ -51,7 +54,7 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guessers = array();
|
||||
protected $guessers = [];
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
@@ -127,7 +130,9 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
}
|
||||
|
||||
if (2 === \count($this->guessers) && !FileBinaryMimeTypeGuesser::isSupported() && !FileinfoMimeTypeGuesser::isSupported()) {
|
||||
throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)');
|
||||
throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?).');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -13,11 +13,14 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\Mime\MimeTypesInterface;
|
||||
|
||||
/**
|
||||
* Guesses the mime type of a file.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead
|
||||
*/
|
||||
interface MimeTypeGuesserInterface
|
||||
{
|
||||
@@ -26,7 +29,7 @@ interface MimeTypeGuesserInterface
|
||||
*
|
||||
* @param string $path The path to the file
|
||||
*
|
||||
* @return string The mime type or NULL, if none could be guessed
|
||||
* @return string|null The mime type or NULL, if none could be guessed
|
||||
*
|
||||
* @throws FileNotFoundException If the file does not exist
|
||||
* @throws AccessDeniedException If the file could not be read
|
||||
|
@@ -20,7 +20,10 @@ class Stream extends File
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getSize()
|
||||
{
|
||||
return false;
|
||||
|
@@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\NoFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\PartialFileException;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
/**
|
||||
* A file uploaded through a form.
|
||||
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
|
||||
*/
|
||||
class UploadedFile extends File
|
||||
{
|
||||
private $test = false;
|
||||
private $test;
|
||||
private $originalName;
|
||||
private $mimeType;
|
||||
private $error;
|
||||
@@ -66,15 +66,15 @@ class UploadedFile extends File
|
||||
$this->mimeType = $mimeType ?: 'application/octet-stream';
|
||||
|
||||
if (4 < \func_num_args() ? !\is_bool($test) : null !== $error && @filesize($path) === $error) {
|
||||
@trigger_error(sprintf('Passing a size as 4th argument to the constructor of "%s" is deprecated since Symfony 4.1.', __CLASS__), E_USER_DEPRECATED);
|
||||
@trigger_error(sprintf('Passing a size as 4th argument to the constructor of "%s" is deprecated since Symfony 4.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
$error = $test;
|
||||
$test = 5 < \func_num_args() ? func_get_arg(5) : false;
|
||||
}
|
||||
|
||||
$this->error = $error ?: UPLOAD_ERR_OK;
|
||||
$this->error = $error ?: \UPLOAD_ERR_OK;
|
||||
$this->test = $test;
|
||||
|
||||
parent::__construct($path, UPLOAD_ERR_OK === $this->error);
|
||||
parent::__construct($path, \UPLOAD_ERR_OK === $this->error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +83,7 @@ class UploadedFile extends File
|
||||
* It is extracted from the request from which the file has been uploaded.
|
||||
* Then it should not be considered as a safe value.
|
||||
*
|
||||
* @return string|null The original name
|
||||
* @return string The original name
|
||||
*/
|
||||
public function getClientOriginalName()
|
||||
{
|
||||
@@ -100,7 +100,7 @@ class UploadedFile extends File
|
||||
*/
|
||||
public function getClientOriginalExtension()
|
||||
{
|
||||
return pathinfo($this->originalName, PATHINFO_EXTENSION);
|
||||
return pathinfo($this->originalName, \PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +112,7 @@ class UploadedFile extends File
|
||||
* For a trusted mime type, use getMimeType() instead (which guesses the mime
|
||||
* type based on the file content).
|
||||
*
|
||||
* @return string|null The mime type
|
||||
* @return string The mime type
|
||||
*
|
||||
* @see getMimeType()
|
||||
*/
|
||||
@@ -140,10 +140,7 @@ class UploadedFile extends File
|
||||
*/
|
||||
public function guessClientExtension()
|
||||
{
|
||||
$type = $this->getClientMimeType();
|
||||
$guesser = ExtensionGuesser::getInstance();
|
||||
|
||||
return $guesser->guess($type);
|
||||
return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +155,7 @@ class UploadedFile extends File
|
||||
*/
|
||||
public function getClientSize()
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
return $this->getSize();
|
||||
}
|
||||
@@ -183,7 +180,7 @@ class UploadedFile extends File
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
$isOk = UPLOAD_ERR_OK === $this->error;
|
||||
$isOk = \UPLOAD_ERR_OK === $this->error;
|
||||
|
||||
return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
|
||||
}
|
||||
@@ -211,7 +208,7 @@ class UploadedFile extends File
|
||||
$moved = move_uploaded_file($this->getPathname(), $target);
|
||||
restore_error_handler();
|
||||
if (!$moved) {
|
||||
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
|
||||
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s).', $this->getPathname(), $target, strip_tags($error)));
|
||||
}
|
||||
|
||||
@chmod($target, 0666 & ~umask());
|
||||
@@ -220,19 +217,19 @@ class UploadedFile extends File
|
||||
}
|
||||
|
||||
switch ($this->error) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case \UPLOAD_ERR_INI_SIZE:
|
||||
throw new IniSizeFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
case \UPLOAD_ERR_FORM_SIZE:
|
||||
throw new FormSizeFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
case \UPLOAD_ERR_PARTIAL:
|
||||
throw new PartialFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
case \UPLOAD_ERR_NO_FILE:
|
||||
throw new NoFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_CANT_WRITE:
|
||||
case \UPLOAD_ERR_CANT_WRITE:
|
||||
throw new CannotWriteFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
case \UPLOAD_ERR_NO_TMP_DIR:
|
||||
throw new NoTmpDirFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_EXTENSION:
|
||||
case \UPLOAD_ERR_EXTENSION:
|
||||
throw new ExtensionFileException($this->getErrorMessage());
|
||||
}
|
||||
|
||||
@@ -242,26 +239,39 @@ class UploadedFile extends File
|
||||
/**
|
||||
* Returns the maximum size of an uploaded file as configured in php.ini.
|
||||
*
|
||||
* @return int The maximum size of an uploaded file in bytes
|
||||
* @return int|float The maximum size of an uploaded file in bytes (returns float if size > PHP_INT_MAX)
|
||||
*/
|
||||
public static function getMaxFilesize()
|
||||
{
|
||||
$iniMax = strtolower(ini_get('upload_max_filesize'));
|
||||
$sizePostMax = self::parseFilesize(\ini_get('post_max_size'));
|
||||
$sizeUploadMax = self::parseFilesize(\ini_get('upload_max_filesize'));
|
||||
|
||||
if ('' === $iniMax) {
|
||||
return PHP_INT_MAX;
|
||||
return min($sizePostMax ?: \PHP_INT_MAX, $sizeUploadMax ?: \PHP_INT_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given size from an ini value in bytes.
|
||||
*
|
||||
* @return int|float Returns float if size > PHP_INT_MAX
|
||||
*/
|
||||
private static function parseFilesize(string $size)
|
||||
{
|
||||
if ('' === $size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$max = ltrim($iniMax, '+');
|
||||
if (0 === strpos($max, '0x')) {
|
||||
$size = strtolower($size);
|
||||
|
||||
$max = ltrim($size, '+');
|
||||
if (str_starts_with($max, '0x')) {
|
||||
$max = \intval($max, 16);
|
||||
} elseif (0 === strpos($max, '0')) {
|
||||
} elseif (str_starts_with($max, '0')) {
|
||||
$max = \intval($max, 8);
|
||||
} else {
|
||||
$max = (int) $max;
|
||||
}
|
||||
|
||||
switch (substr($iniMax, -1)) {
|
||||
switch (substr($size, -1)) {
|
||||
case 't': $max *= 1024;
|
||||
// no break
|
||||
case 'g': $max *= 1024;
|
||||
@@ -281,19 +291,19 @@ class UploadedFile extends File
|
||||
*/
|
||||
public function getErrorMessage()
|
||||
{
|
||||
static $errors = array(
|
||||
UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
|
||||
UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
|
||||
UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
|
||||
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
|
||||
UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
|
||||
UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
|
||||
UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
|
||||
);
|
||||
static $errors = [
|
||||
\UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
|
||||
\UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
|
||||
\UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
|
||||
\UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
|
||||
\UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
|
||||
\UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
|
||||
\UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
|
||||
];
|
||||
|
||||
$errorCode = $this->error;
|
||||
$maxFilesize = UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
|
||||
$message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.';
|
||||
$maxFilesize = \UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
|
||||
$message = $errors[$errorCode] ?? 'The file "%s" was not uploaded due to an unknown error.';
|
||||
|
||||
return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
|
||||
}
|
||||
|
52
vendor/symfony/http-foundation/FileBag.php
vendored
52
vendor/symfony/http-foundation/FileBag.php
vendored
@@ -21,12 +21,12 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
*/
|
||||
class FileBag extends ParameterBag
|
||||
{
|
||||
private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
|
||||
private const FILE_KEYS = ['error', 'name', 'size', 'tmp_name', 'type'];
|
||||
|
||||
/**
|
||||
* @param array $parameters An array of HTTP files
|
||||
* @param array|UploadedFile[] $parameters An array of HTTP files
|
||||
*/
|
||||
public function __construct(array $parameters = array())
|
||||
public function __construct(array $parameters = [])
|
||||
{
|
||||
$this->replace($parameters);
|
||||
}
|
||||
@@ -34,9 +34,9 @@ class FileBag extends ParameterBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function replace(array $files = array())
|
||||
public function replace(array $files = [])
|
||||
{
|
||||
$this->parameters = array();
|
||||
$this->parameters = [];
|
||||
$this->add($files);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class FileBag extends ParameterBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(array $files = array())
|
||||
public function add(array $files = [])
|
||||
{
|
||||
foreach ($files as $key => $file) {
|
||||
$this->set($key, $file);
|
||||
@@ -76,21 +76,19 @@ class FileBag extends ParameterBag
|
||||
}
|
||||
|
||||
$file = $this->fixPhpFilesArray($file);
|
||||
if (\is_array($file)) {
|
||||
$keys = array_keys($file);
|
||||
sort($keys);
|
||||
$keys = array_keys($file);
|
||||
sort($keys);
|
||||
|
||||
if ($keys == self::$fileKeys) {
|
||||
if (UPLOAD_ERR_NO_FILE == $file['error']) {
|
||||
$file = null;
|
||||
} else {
|
||||
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error']);
|
||||
}
|
||||
if (self::FILE_KEYS == $keys) {
|
||||
if (\UPLOAD_ERR_NO_FILE == $file['error']) {
|
||||
$file = null;
|
||||
} else {
|
||||
$file = array_map(array($this, 'convertFileInformation'), $file);
|
||||
if (array_keys($keys) === $keys) {
|
||||
$file = array_filter($file);
|
||||
}
|
||||
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error'], false);
|
||||
}
|
||||
} else {
|
||||
$file = array_map(function ($v) { return $v instanceof UploadedFile || \is_array($v) ? $this->convertFileInformation($v) : $v; }, $file);
|
||||
if (array_keys($keys) === $keys) {
|
||||
$file = array_filter($file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,34 +107,34 @@ class FileBag extends ParameterBag
|
||||
* It's safe to pass an already converted array, in which case this method
|
||||
* just returns the original array unmodified.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function fixPhpFilesArray($data)
|
||||
{
|
||||
if (!\is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Remove extra key added by PHP 8.1.
|
||||
unset($data['full_path']);
|
||||
$keys = array_keys($data);
|
||||
sort($keys);
|
||||
|
||||
if (self::$fileKeys != $keys || !isset($data['name']) || !\is_array($data['name'])) {
|
||||
if (self::FILE_KEYS != $keys || !isset($data['name']) || !\is_array($data['name'])) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$files = $data;
|
||||
foreach (self::$fileKeys as $k) {
|
||||
foreach (self::FILE_KEYS as $k) {
|
||||
unset($files[$k]);
|
||||
}
|
||||
|
||||
foreach ($data['name'] as $key => $name) {
|
||||
$files[$key] = $this->fixPhpFilesArray(array(
|
||||
$files[$key] = $this->fixPhpFilesArray([
|
||||
'error' => $data['error'][$key],
|
||||
'name' => $name,
|
||||
'type' => $data['type'][$key],
|
||||
'tmp_name' => $data['tmp_name'][$key],
|
||||
'size' => $data['size'][$key],
|
||||
));
|
||||
]);
|
||||
}
|
||||
|
||||
return $files;
|
||||
|
98
vendor/symfony/http-foundation/HeaderBag.php
vendored
98
vendor/symfony/http-foundation/HeaderBag.php
vendored
@@ -18,13 +18,13 @@ namespace Symfony\Component\HttpFoundation;
|
||||
*/
|
||||
class HeaderBag implements \IteratorAggregate, \Countable
|
||||
{
|
||||
protected $headers = array();
|
||||
protected $cacheControl = array();
|
||||
protected const UPPER = '_ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
protected const LOWER = '-abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
/**
|
||||
* @param array $headers An array of HTTP headers
|
||||
*/
|
||||
public function __construct(array $headers = array())
|
||||
protected $headers = [];
|
||||
protected $cacheControl = [];
|
||||
|
||||
public function __construct(array $headers = [])
|
||||
{
|
||||
foreach ($headers as $key => $values) {
|
||||
$this->set($key, $values);
|
||||
@@ -58,10 +58,16 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the headers.
|
||||
*
|
||||
* @param string|null $key The name of the headers to return or null to get them all
|
||||
*
|
||||
* @return array An array of headers
|
||||
*/
|
||||
public function all()
|
||||
public function all(/* string $key = null */)
|
||||
{
|
||||
if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) {
|
||||
return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? [];
|
||||
}
|
||||
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
@@ -77,19 +83,15 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
|
||||
/**
|
||||
* Replaces the current HTTP headers by a new set.
|
||||
*
|
||||
* @param array $headers An array of HTTP headers
|
||||
*/
|
||||
public function replace(array $headers = array())
|
||||
public function replace(array $headers = [])
|
||||
{
|
||||
$this->headers = array();
|
||||
$this->headers = [];
|
||||
$this->add($headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new headers the current HTTP headers set.
|
||||
*
|
||||
* @param array $headers An array of HTTP headers
|
||||
*/
|
||||
public function add(array $headers)
|
||||
{
|
||||
@@ -101,42 +103,43 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns a header value by name.
|
||||
*
|
||||
* @param string $key The header name
|
||||
* @param string|string[]|null $default The default value
|
||||
* @param bool $first Whether to return the first value or all header values
|
||||
* @param string $key The header name
|
||||
* @param string|null $default The default value
|
||||
*
|
||||
* @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise
|
||||
* @return string|null The first header value or default value
|
||||
*/
|
||||
public function get($key, $default = null, $first = true)
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$headers = $this->all();
|
||||
$headers = $this->all((string) $key);
|
||||
if (2 < \func_num_args()) {
|
||||
@trigger_error(sprintf('Passing a third argument to "%s()" is deprecated since Symfony 4.4, use method "all()" instead', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
if (!array_key_exists($key, $headers)) {
|
||||
if (null === $default) {
|
||||
return $first ? null : array();
|
||||
if (!func_get_arg(2)) {
|
||||
return $headers;
|
||||
}
|
||||
|
||||
return $first ? $default : array($default);
|
||||
}
|
||||
|
||||
if ($first) {
|
||||
return \count($headers[$key]) ? $headers[$key][0] : $default;
|
||||
if (!$headers) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $headers[$key];
|
||||
if (null === $headers[0]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) $headers[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a header by name.
|
||||
*
|
||||
* @param string $key The key
|
||||
* @param string|string[] $values The value or an array of values
|
||||
* @param bool $replace Whether to replace the actual value or not (true by default)
|
||||
* @param string $key The key
|
||||
* @param string|string[]|null $values The value or an array of values
|
||||
* @param bool $replace Whether to replace the actual value or not (true by default)
|
||||
*/
|
||||
public function set($key, $values, $replace = true)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$key = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
if (\is_array($values)) {
|
||||
$values = array_values($values);
|
||||
@@ -148,7 +151,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
}
|
||||
} else {
|
||||
if (true === $replace || !isset($this->headers[$key])) {
|
||||
$this->headers[$key] = array($values);
|
||||
$this->headers[$key] = [$values];
|
||||
} else {
|
||||
$this->headers[$key][] = $values;
|
||||
}
|
||||
@@ -168,7 +171,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
|
||||
return \array_key_exists(strtr($key, self::UPPER, self::LOWER), $this->all());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,7 +184,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function contains($key, $value)
|
||||
{
|
||||
return \in_array($value, $this->get($key, null, false));
|
||||
return \in_array($value, $this->all((string) $key));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,22 +194,21 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$key = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
unset($this->headers[$key]);
|
||||
|
||||
if ('cache-control' === $key) {
|
||||
$this->cacheControl = array();
|
||||
$this->cacheControl = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP header value converted to a date.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param \DateTime $default The default value
|
||||
* @param string $key The parameter key
|
||||
*
|
||||
* @return \DateTime|null The parsed DateTime or the default value if the header does not exist
|
||||
* @return \DateTimeInterface|null The parsed DateTime or the default value if the header does not exist
|
||||
*
|
||||
* @throws \RuntimeException When the HTTP header is not parseable
|
||||
*/
|
||||
@@ -216,8 +218,8 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
return $default;
|
||||
}
|
||||
|
||||
if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) {
|
||||
throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
|
||||
if (false === $date = \DateTime::createFromFormat(\DATE_RFC2822, $value)) {
|
||||
throw new \RuntimeException(sprintf('The "%s" HTTP header is not parseable (%s).', $key, $value));
|
||||
}
|
||||
|
||||
return $date;
|
||||
@@ -226,8 +228,8 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Adds a custom Cache-Control directive.
|
||||
*
|
||||
* @param string $key The Cache-Control directive name
|
||||
* @param mixed $value The Cache-Control directive value
|
||||
* @param string $key The Cache-Control directive name
|
||||
* @param bool|string $value The Cache-Control directive value
|
||||
*/
|
||||
public function addCacheControlDirective($key, $value = true)
|
||||
{
|
||||
@@ -245,7 +247,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function hasCacheControlDirective($key)
|
||||
{
|
||||
return array_key_exists($key, $this->cacheControl);
|
||||
return \array_key_exists($key, $this->cacheControl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,11 +255,11 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @param string $key The directive name
|
||||
*
|
||||
* @return mixed|null The directive value if defined, null otherwise
|
||||
* @return bool|string|null The directive value if defined, null otherwise
|
||||
*/
|
||||
public function getCacheControlDirective($key)
|
||||
{
|
||||
return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
|
||||
return $this->cacheControl[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,6 +279,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return \ArrayIterator An \ArrayIterator instance
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->headers);
|
||||
@@ -287,6 +290,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return int The number of headers
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \count($this->headers);
|
||||
|
48
vendor/symfony/http-foundation/HeaderUtils.php
vendored
48
vendor/symfony/http-foundation/HeaderUtils.php
vendored
@@ -34,9 +34,8 @@ class HeaderUtils
|
||||
* Example:
|
||||
*
|
||||
* HeaderUtils::split("da, en-gb;q=0.8", ",;")
|
||||
* // => array(array('da'), array('en-gb', 'q=0.8'))
|
||||
* // => ['da'], ['en-gb', 'q=0.8']]
|
||||
*
|
||||
* @param string $header HTTP header value
|
||||
* @param string $separators List of characters to split on, ordered by
|
||||
* precedence, e.g. ",", ";=", or ",;="
|
||||
*
|
||||
@@ -63,7 +62,7 @@ class HeaderUtils
|
||||
\s*
|
||||
(?<separator>['.$quotedSeparators.'])
|
||||
\s*
|
||||
/x', trim($header), $matches, PREG_SET_ORDER);
|
||||
/x', trim($header), $matches, \PREG_SET_ORDER);
|
||||
|
||||
return self::groupParts($matches, $separators);
|
||||
}
|
||||
@@ -78,12 +77,12 @@ class HeaderUtils
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* HeaderUtils::combine(array(array("foo", "abc"), array("bar")))
|
||||
* // => array("foo" => "abc", "bar" => true)
|
||||
* HeaderUtils::combine([["foo", "abc"], ["bar"]])
|
||||
* // => ["foo" => "abc", "bar" => true]
|
||||
*/
|
||||
public static function combine(array $parts): array
|
||||
{
|
||||
$assoc = array();
|
||||
$assoc = [];
|
||||
foreach ($parts as $part) {
|
||||
$name = strtolower($part[0]);
|
||||
$value = $part[1] ?? true;
|
||||
@@ -102,12 +101,12 @@ class HeaderUtils
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* HeaderUtils::toString(array("foo" => "abc", "bar" => true, "baz" => "a b c"), ",")
|
||||
* HeaderUtils::toString(["foo" => "abc", "bar" => true, "baz" => "a b c"], ",")
|
||||
* // => 'foo=abc, bar, baz="a b c"'
|
||||
*/
|
||||
public static function toString(array $assoc, string $separator): string
|
||||
{
|
||||
$parts = array();
|
||||
$parts = [];
|
||||
foreach ($assoc as $name => $value) {
|
||||
if (true === $value) {
|
||||
$parts[] = $name;
|
||||
@@ -147,7 +146,7 @@ class HeaderUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a HTTP Content-Disposition field-value.
|
||||
* Generates an HTTP Content-Disposition field-value.
|
||||
*
|
||||
* @param string $disposition One of "inline" or "attachment"
|
||||
* @param string $filename A unicode string
|
||||
@@ -163,7 +162,7 @@ class HeaderUtils
|
||||
*/
|
||||
public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = ''): string
|
||||
{
|
||||
if (!\in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
|
||||
if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE])) {
|
||||
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
|
||||
}
|
||||
|
||||
@@ -177,16 +176,16 @@ class HeaderUtils
|
||||
}
|
||||
|
||||
// percent characters aren't safe in fallback.
|
||||
if (false !== strpos($filenameFallback, '%')) {
|
||||
if (str_contains($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, '\\')) {
|
||||
if (str_contains($filename, '/') || str_contains($filename, '\\') || str_contains($filenameFallback, '/') || str_contains($filenameFallback, '\\')) {
|
||||
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
|
||||
}
|
||||
|
||||
$params = array('filename' => $filenameFallback);
|
||||
$params = ['filename' => $filenameFallback];
|
||||
if ($filename !== $filenameFallback) {
|
||||
$params['filename*'] = "utf-8''".rawurlencode($filename);
|
||||
}
|
||||
@@ -194,30 +193,43 @@ class HeaderUtils
|
||||
return $disposition.'; '.self::toString($params, ';');
|
||||
}
|
||||
|
||||
private static function groupParts(array $matches, string $separators): array
|
||||
private static function groupParts(array $matches, string $separators, bool $first = true): array
|
||||
{
|
||||
$separator = $separators[0];
|
||||
$partSeparators = substr($separators, 1);
|
||||
|
||||
$i = 0;
|
||||
$partMatches = array();
|
||||
$partMatches = [];
|
||||
$previousMatchWasSeparator = false;
|
||||
foreach ($matches as $match) {
|
||||
if (isset($match['separator']) && $match['separator'] === $separator) {
|
||||
if (!$first && $previousMatchWasSeparator && isset($match['separator']) && $match['separator'] === $separator) {
|
||||
$previousMatchWasSeparator = true;
|
||||
$partMatches[$i][] = $match;
|
||||
} elseif (isset($match['separator']) && $match['separator'] === $separator) {
|
||||
$previousMatchWasSeparator = true;
|
||||
++$i;
|
||||
} else {
|
||||
$previousMatchWasSeparator = false;
|
||||
$partMatches[$i][] = $match;
|
||||
}
|
||||
}
|
||||
|
||||
$parts = array();
|
||||
$parts = [];
|
||||
if ($partSeparators) {
|
||||
foreach ($partMatches as $matches) {
|
||||
$parts[] = self::groupParts($matches, $partSeparators);
|
||||
$parts[] = self::groupParts($matches, $partSeparators, false);
|
||||
}
|
||||
} else {
|
||||
foreach ($partMatches as $matches) {
|
||||
$parts[] = self::unquote($matches[0][0]);
|
||||
}
|
||||
|
||||
if (!$first && 2 < \count($parts)) {
|
||||
$parts = [
|
||||
$parts[0],
|
||||
implode($separator, \array_slice($parts, 1)),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $parts;
|
||||
|
63
vendor/symfony/http-foundation/IpUtils.php
vendored
63
vendor/symfony/http-foundation/IpUtils.php
vendored
@@ -18,7 +18,7 @@ namespace Symfony\Component\HttpFoundation;
|
||||
*/
|
||||
class IpUtils
|
||||
{
|
||||
private static $checkedIps = array();
|
||||
private static $checkedIps = [];
|
||||
|
||||
/**
|
||||
* This class should not be instantiated.
|
||||
@@ -37,8 +37,12 @@ class IpUtils
|
||||
*/
|
||||
public static function checkIp($requestIp, $ips)
|
||||
{
|
||||
if (null === $requestIp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!\is_array($ips)) {
|
||||
$ips = array($ips);
|
||||
$ips = [$ips];
|
||||
}
|
||||
|
||||
$method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
|
||||
@@ -68,15 +72,15 @@ class IpUtils
|
||||
return self::$checkedIps[$cacheKey];
|
||||
}
|
||||
|
||||
if (!filter_var($requestIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
|
||||
if (false !== strpos($ip, '/')) {
|
||||
list($address, $netmask) = explode('/', $ip, 2);
|
||||
if (str_contains($ip, '/')) {
|
||||
[$address, $netmask] = explode('/', $ip, 2);
|
||||
|
||||
if ('0' === $netmask) {
|
||||
return self::$checkedIps[$cacheKey] = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
|
||||
return self::$checkedIps[$cacheKey] = filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4);
|
||||
}
|
||||
|
||||
if ($netmask < 0 || $netmask > 32) {
|
||||
@@ -120,8 +124,17 @@ class IpUtils
|
||||
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
|
||||
}
|
||||
|
||||
if (false !== strpos($ip, '/')) {
|
||||
list($address, $netmask) = explode('/', $ip, 2);
|
||||
// Check to see if we were given a IP4 $requestIp or $ip by mistake
|
||||
if (str_contains($requestIp, '.') || str_contains($ip, '.')) {
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
|
||||
if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
|
||||
if (str_contains($ip, '/')) {
|
||||
[$address, $netmask] = explode('/', $ip, 2);
|
||||
|
||||
if ('0' === $netmask) {
|
||||
return (bool) unpack('n*', @inet_pton($address));
|
||||
@@ -145,7 +158,7 @@ class IpUtils
|
||||
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
|
||||
$left = $netmask - 16 * ($i - 1);
|
||||
$left = ($left <= 16) ? $left : 16;
|
||||
$mask = ~(0xffff >> $left) & 0xffff;
|
||||
$mask = ~(0xFFFF >> $left) & 0xFFFF;
|
||||
if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
|
||||
return self::$checkedIps[$cacheKey] = false;
|
||||
}
|
||||
@@ -153,4 +166,36 @@ class IpUtils
|
||||
|
||||
return self::$checkedIps[$cacheKey] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymizes an IP/IPv6.
|
||||
*
|
||||
* Removes the last byte for v4 and the last 8 bytes for v6 IPs
|
||||
*/
|
||||
public static function anonymize(string $ip): string
|
||||
{
|
||||
$wrappedIPv6 = false;
|
||||
if ('[' === substr($ip, 0, 1) && ']' === substr($ip, -1, 1)) {
|
||||
$wrappedIPv6 = true;
|
||||
$ip = substr($ip, 1, -1);
|
||||
}
|
||||
|
||||
$packedAddress = inet_pton($ip);
|
||||
if (4 === \strlen($packedAddress)) {
|
||||
$mask = '255.255.255.0';
|
||||
} elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff:ffff'))) {
|
||||
$mask = '::ffff:ffff:ff00';
|
||||
} elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff'))) {
|
||||
$mask = '::ffff:ff00';
|
||||
} else {
|
||||
$mask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000';
|
||||
}
|
||||
$ip = inet_ntop($packedAddress & inet_pton($mask));
|
||||
|
||||
if ($wrappedIPv6) {
|
||||
$ip = '['.$ip.']';
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
|
49
vendor/symfony/http-foundation/JsonResponse.php
vendored
49
vendor/symfony/http-foundation/JsonResponse.php
vendored
@@ -18,7 +18,7 @@ namespace Symfony\Component\HttpFoundation;
|
||||
* object. It is however recommended that you do return an object as it
|
||||
* protects yourself against XSSI and JSON-JavaScript Hijacking.
|
||||
*
|
||||
* @see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside
|
||||
* @see https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/AJAX_Security_Cheat_Sheet.md#always-return-json-with-an-object-on-the-outside
|
||||
*
|
||||
* @author Igor Wiedler <igor@wiedler.ch>
|
||||
*/
|
||||
@@ -29,7 +29,7 @@ class JsonResponse extends Response
|
||||
|
||||
// Encode <, >, ', &, and " characters in the JSON, making it also safe to be embedded into HTML.
|
||||
// 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
|
||||
const DEFAULT_ENCODING_OPTIONS = 15;
|
||||
public const DEFAULT_ENCODING_OPTIONS = 15;
|
||||
|
||||
protected $encodingOptions = self::DEFAULT_ENCODING_OPTIONS;
|
||||
|
||||
@@ -39,10 +39,14 @@ class JsonResponse extends Response
|
||||
* @param array $headers An array of response headers
|
||||
* @param bool $json If the data is already a JSON string
|
||||
*/
|
||||
public function __construct($data = null, int $status = 200, array $headers = array(), bool $json = false)
|
||||
public function __construct($data = null, int $status = 200, array $headers = [], bool $json = false)
|
||||
{
|
||||
parent::__construct('', $status, $headers);
|
||||
|
||||
if ($json && !\is_string($data) && !is_numeric($data) && !\is_callable([$data, '__toString'])) {
|
||||
throw new \TypeError(sprintf('"%s": If $json is set to true, argument $data must be a string or object implementing __toString(), "%s" given.', __METHOD__, get_debug_type($data)));
|
||||
}
|
||||
|
||||
if (null === $data) {
|
||||
$data = new \ArrayObject();
|
||||
}
|
||||
@@ -55,24 +59,35 @@ class JsonResponse extends Response
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* return JsonResponse::create($data, 200)
|
||||
* return JsonResponse::create(['key' => 'value'])
|
||||
* ->setSharedMaxAge(300);
|
||||
*
|
||||
* @param mixed $data The json response data
|
||||
* @param mixed $data The JSON response data
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($data = null, $status = 200, $headers = array())
|
||||
public static function create($data = null, $status = 200, $headers = [])
|
||||
{
|
||||
return new static($data, $status, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make easier the creation of JsonResponse from raw json.
|
||||
* Factory method for chainability.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* return JsonResponse::fromJsonString('{"key": "value"}')
|
||||
* ->setSharedMaxAge(300);
|
||||
*
|
||||
* @param string $data The JSON response string
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function fromJsonString($data = null, $status = 200, $headers = array())
|
||||
public static function fromJsonString($data, $status = 200, $headers = [])
|
||||
{
|
||||
return new static($data, $status, $headers, true);
|
||||
}
|
||||
@@ -89,16 +104,16 @@ class JsonResponse extends Response
|
||||
public function setCallback($callback = null)
|
||||
{
|
||||
if (null !== $callback) {
|
||||
// partially taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/
|
||||
// partially taken from https://geekality.net/2011/08/03/valid-javascript-identifier/
|
||||
// partially taken from https://github.com/willdurand/JsonpCallbackValidator
|
||||
// JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details.
|
||||
// (c) William Durand <william.durand1@gmail.com>
|
||||
$pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u';
|
||||
$reserved = array(
|
||||
$reserved = [
|
||||
'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while',
|
||||
'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export',
|
||||
'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false',
|
||||
);
|
||||
];
|
||||
$parts = explode('.', $callback);
|
||||
foreach ($parts as $part) {
|
||||
if (!preg_match($pattern, $part) || \in_array($part, $reserved, true)) {
|
||||
@@ -118,8 +133,6 @@ class JsonResponse extends Response
|
||||
* @param string $json
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setJson($json)
|
||||
{
|
||||
@@ -137,18 +150,22 @@ class JsonResponse extends Response
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setData($data = array())
|
||||
public function setData($data = [])
|
||||
{
|
||||
try {
|
||||
$data = json_encode($data, $this->encodingOptions);
|
||||
} catch (\Exception $e) {
|
||||
if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
|
||||
if ('Exception' === \get_class($e) && str_starts_with($e->getMessage(), 'Failed calling ')) {
|
||||
throw $e->getPrevious() ?: $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
if (\PHP_VERSION_ID >= 70300 && (\JSON_THROW_ON_ERROR & $this->encodingOptions)) {
|
||||
return $this->setJson($data);
|
||||
}
|
||||
|
||||
if (\JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new \InvalidArgumentException(json_last_error_msg());
|
||||
}
|
||||
|
||||
|
2
vendor/symfony/http-foundation/LICENSE
vendored
2
vendor/symfony/http-foundation/LICENSE
vendored
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2018 Fabien Potencier
|
||||
Copyright (c) 2004-2022 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
31
vendor/symfony/http-foundation/ParameterBag.php
vendored
31
vendor/symfony/http-foundation/ParameterBag.php
vendored
@@ -23,10 +23,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function __construct(array $parameters = array())
|
||||
public function __construct(array $parameters = [])
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
@@ -53,20 +50,16 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
|
||||
/**
|
||||
* Replaces the current parameters by a new set.
|
||||
*
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function replace(array $parameters = array())
|
||||
public function replace(array $parameters = [])
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds parameters.
|
||||
*
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function add(array $parameters = array())
|
||||
public function add(array $parameters = [])
|
||||
{
|
||||
$this->parameters = array_replace($this->parameters, $parameters);
|
||||
}
|
||||
@@ -81,7 +74,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
|
||||
return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +97,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists($key, $this->parameters);
|
||||
return \array_key_exists($key, $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +147,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
public function getDigits($key, $default = '')
|
||||
{
|
||||
// we need to remove - and + because they're allowed in the filter
|
||||
return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
|
||||
return str_replace(['-', '+'], '', $this->filter($key, $default, \FILTER_SANITIZE_NUMBER_INT));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +173,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function getBoolean($key, $default = false)
|
||||
{
|
||||
return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN);
|
||||
return $this->filter($key, $default, \FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,22 +184,22 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
* @param int $filter FILTER_* constant
|
||||
* @param mixed $options Filter options
|
||||
*
|
||||
* @see http://php.net/manual/en/function.filter-var.php
|
||||
* @see https://php.net/filter-var
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array())
|
||||
public function filter($key, $default = null, $filter = \FILTER_DEFAULT, $options = [])
|
||||
{
|
||||
$value = $this->get($key, $default);
|
||||
|
||||
// Always turn $options into an array - this allows filter_var option shortcuts.
|
||||
if (!\is_array($options) && $options) {
|
||||
$options = array('flags' => $options);
|
||||
$options = ['flags' => $options];
|
||||
}
|
||||
|
||||
// Add a convenience check for arrays.
|
||||
if (\is_array($value) && !isset($options['flags'])) {
|
||||
$options['flags'] = FILTER_REQUIRE_ARRAY;
|
||||
$options['flags'] = \FILTER_REQUIRE_ARRAY;
|
||||
}
|
||||
|
||||
return filter_var($value, $filter, $options);
|
||||
@@ -217,6 +210,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return \ArrayIterator An \ArrayIterator instance
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->parameters);
|
||||
@@ -227,6 +221,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return int The number of parameters
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \count($this->parameters);
|
||||
|
10
vendor/symfony/http-foundation/README.md
vendored
10
vendor/symfony/http-foundation/README.md
vendored
@@ -7,8 +7,8 @@ specification.
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/http_foundation/index.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
* [Documentation](https://symfony.com/doc/current/components/http_foundation.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
|
@@ -30,10 +30,15 @@ class RedirectResponse extends Response
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc2616#section-10.3
|
||||
* @see https://tools.ietf.org/html/rfc2616#section-10.3
|
||||
*/
|
||||
public function __construct(?string $url, int $status = 302, array $headers = array())
|
||||
public function __construct(?string $url, int $status = 302, array $headers = [])
|
||||
{
|
||||
if (null === $url) {
|
||||
@trigger_error(sprintf('Passing a null url when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), \E_USER_DEPRECATED);
|
||||
$url = '';
|
||||
}
|
||||
|
||||
parent::__construct('', $status, $headers);
|
||||
|
||||
$this->setTargetUrl($url);
|
||||
@@ -42,7 +47,7 @@ class RedirectResponse extends Response
|
||||
throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
|
||||
}
|
||||
|
||||
if (301 == $status && !array_key_exists('cache-control', $headers)) {
|
||||
if (301 == $status && !\array_key_exists('cache-control', array_change_key_case($headers, \CASE_LOWER))) {
|
||||
$this->headers->remove('cache-control');
|
||||
}
|
||||
}
|
||||
@@ -56,7 +61,7 @@ class RedirectResponse extends Response
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($url = '', $status = 302, $headers = array())
|
||||
public static function create($url = '', $status = 302, $headers = [])
|
||||
{
|
||||
return new static($url, $status, $headers);
|
||||
}
|
||||
@@ -82,7 +87,7 @@ class RedirectResponse extends Response
|
||||
*/
|
||||
public function setTargetUrl($url)
|
||||
{
|
||||
if (empty($url)) {
|
||||
if ('' === ($url ?? '')) {
|
||||
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
|
||||
}
|
||||
|
||||
@@ -93,14 +98,14 @@ class RedirectResponse extends Response
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="refresh" content="0;url=%1$s" />
|
||||
<meta http-equiv="refresh" content="0;url=\'%1$s\'" />
|
||||
|
||||
<title>Redirecting to %1$s</title>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="%1$s">%1$s</a>.
|
||||
</body>
|
||||
</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
|
||||
</html>', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8')));
|
||||
|
||||
$this->headers->set('Location', $url);
|
||||
|
||||
|
424
vendor/symfony/http-foundation/Request.php
vendored
424
vendor/symfony/http-foundation/Request.php
vendored
@@ -15,6 +15,14 @@ use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
|
||||
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.
|
||||
*
|
||||
@@ -30,88 +38,88 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
*/
|
||||
class Request
|
||||
{
|
||||
const HEADER_FORWARDED = 0b00001; // When using RFC 7239
|
||||
const HEADER_X_FORWARDED_FOR = 0b00010;
|
||||
const HEADER_X_FORWARDED_HOST = 0b00100;
|
||||
const HEADER_X_FORWARDED_PROTO = 0b01000;
|
||||
const HEADER_X_FORWARDED_PORT = 0b10000;
|
||||
const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers
|
||||
const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host
|
||||
public const HEADER_FORWARDED = 0b00001; // When using RFC 7239
|
||||
public const HEADER_X_FORWARDED_FOR = 0b00010;
|
||||
public const HEADER_X_FORWARDED_HOST = 0b00100;
|
||||
public const HEADER_X_FORWARDED_PROTO = 0b01000;
|
||||
public const HEADER_X_FORWARDED_PORT = 0b10000;
|
||||
public const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers
|
||||
public const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host
|
||||
|
||||
const METHOD_HEAD = 'HEAD';
|
||||
const METHOD_GET = 'GET';
|
||||
const METHOD_POST = 'POST';
|
||||
const METHOD_PUT = 'PUT';
|
||||
const METHOD_PATCH = 'PATCH';
|
||||
const METHOD_DELETE = 'DELETE';
|
||||
const METHOD_PURGE = 'PURGE';
|
||||
const METHOD_OPTIONS = 'OPTIONS';
|
||||
const METHOD_TRACE = 'TRACE';
|
||||
const METHOD_CONNECT = 'CONNECT';
|
||||
public const METHOD_HEAD = 'HEAD';
|
||||
public const METHOD_GET = 'GET';
|
||||
public const METHOD_POST = 'POST';
|
||||
public const METHOD_PUT = 'PUT';
|
||||
public const METHOD_PATCH = 'PATCH';
|
||||
public const METHOD_DELETE = 'DELETE';
|
||||
public const METHOD_PURGE = 'PURGE';
|
||||
public const METHOD_OPTIONS = 'OPTIONS';
|
||||
public const METHOD_TRACE = 'TRACE';
|
||||
public const METHOD_CONNECT = 'CONNECT';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $trustedProxies = array();
|
||||
protected static $trustedProxies = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $trustedHostPatterns = array();
|
||||
protected static $trustedHostPatterns = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $trustedHosts = array();
|
||||
protected static $trustedHosts = [];
|
||||
|
||||
protected static $httpMethodParameterOverride = false;
|
||||
|
||||
/**
|
||||
* Custom parameters.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\ParameterBag
|
||||
* @var ParameterBag
|
||||
*/
|
||||
public $attributes;
|
||||
|
||||
/**
|
||||
* Request body parameters ($_POST).
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\ParameterBag
|
||||
* @var ParameterBag
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* Query string parameters ($_GET).
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\ParameterBag
|
||||
* @var ParameterBag
|
||||
*/
|
||||
public $query;
|
||||
|
||||
/**
|
||||
* Server and execution environment parameters ($_SERVER).
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\ServerBag
|
||||
* @var ServerBag
|
||||
*/
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* Uploaded files ($_FILES).
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\FileBag
|
||||
* @var FileBag
|
||||
*/
|
||||
public $files;
|
||||
|
||||
/**
|
||||
* Cookies ($_COOKIE).
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\ParameterBag
|
||||
* @var ParameterBag
|
||||
*/
|
||||
public $cookies;
|
||||
|
||||
/**
|
||||
* Headers (taken from the $_SERVER).
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\HeaderBag
|
||||
* @var HeaderBag
|
||||
*/
|
||||
public $headers;
|
||||
|
||||
@@ -171,7 +179,7 @@ class Request
|
||||
protected $format;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
|
||||
* @var SessionInterface|callable
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
@@ -192,17 +200,21 @@ class Request
|
||||
|
||||
protected static $requestFactory;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $preferredFormat;
|
||||
private $isHostValid = true;
|
||||
private $isForwardedValid = true;
|
||||
|
||||
private static $trustedHeaderSet = -1;
|
||||
|
||||
private static $forwardedParams = array(
|
||||
private const FORWARDED_PARAMS = [
|
||||
self::HEADER_X_FORWARDED_FOR => 'for',
|
||||
self::HEADER_X_FORWARDED_HOST => 'host',
|
||||
self::HEADER_X_FORWARDED_PROTO => 'proto',
|
||||
self::HEADER_X_FORWARDED_PORT => 'host',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* Names for headers that can be trusted when
|
||||
@@ -213,13 +225,13 @@ class Request
|
||||
* The other headers are non-standard, but widely used
|
||||
* by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
|
||||
*/
|
||||
private static $trustedHeaders = array(
|
||||
private const TRUSTED_HEADERS = [
|
||||
self::HEADER_FORWARDED => 'FORWARDED',
|
||||
self::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR',
|
||||
self::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST',
|
||||
self::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO',
|
||||
self::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array $query The GET parameters
|
||||
@@ -230,7 +242,7 @@ class Request
|
||||
* @param array $server The SERVER parameters
|
||||
* @param string|resource|null $content The raw body data
|
||||
*/
|
||||
public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
|
||||
public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
|
||||
{
|
||||
$this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
|
||||
}
|
||||
@@ -248,7 +260,7 @@ class Request
|
||||
* @param array $server The SERVER parameters
|
||||
* @param string|resource|null $content The raw body data
|
||||
*/
|
||||
public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
|
||||
public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
|
||||
{
|
||||
$this->request = new ParameterBag($request);
|
||||
$this->query = new ParameterBag($query);
|
||||
@@ -278,10 +290,10 @@ class Request
|
||||
*/
|
||||
public static function createFromGlobals()
|
||||
{
|
||||
$request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);
|
||||
$request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);
|
||||
|
||||
if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
|
||||
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
|
||||
if (str_starts_with($request->headers->get('CONTENT_TYPE', ''), 'application/x-www-form-urlencoded')
|
||||
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
|
||||
) {
|
||||
parse_str($request->getContent(), $data);
|
||||
$request->request = new ParameterBag($data);
|
||||
@@ -306,9 +318,9 @@ class Request
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
|
||||
public static function create($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
|
||||
{
|
||||
$server = array_replace(array(
|
||||
$server = array_replace([
|
||||
'SERVER_NAME' => 'localhost',
|
||||
'SERVER_PORT' => 80,
|
||||
'HTTP_HOST' => 'localhost',
|
||||
@@ -321,7 +333,8 @@ class Request
|
||||
'SCRIPT_FILENAME' => '',
|
||||
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
||||
'REQUEST_TIME' => time(),
|
||||
), $server);
|
||||
'REQUEST_TIME_FLOAT' => microtime(true),
|
||||
], $server);
|
||||
|
||||
$server['PATH_INFO'] = '';
|
||||
$server['REQUEST_METHOD'] = strtoupper($method);
|
||||
@@ -369,10 +382,10 @@ class Request
|
||||
// no break
|
||||
case 'PATCH':
|
||||
$request = $parameters;
|
||||
$query = array();
|
||||
$query = [];
|
||||
break;
|
||||
default:
|
||||
$request = array();
|
||||
$request = [];
|
||||
$query = $parameters;
|
||||
break;
|
||||
}
|
||||
@@ -395,7 +408,7 @@ class Request
|
||||
$server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
|
||||
$server['QUERY_STRING'] = $queryString;
|
||||
|
||||
return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content);
|
||||
return self::createRequestFromFactory($query, $request, [], $cookies, $files, $server, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -492,14 +505,10 @@ class Request
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
$content = $this->getContent();
|
||||
} catch (\LogicException $e) {
|
||||
return trigger_error($e, E_USER_ERROR);
|
||||
}
|
||||
$content = $this->getContent();
|
||||
|
||||
$cookieHeader = '';
|
||||
$cookies = array();
|
||||
$cookies = [];
|
||||
|
||||
foreach ($this->cookies as $k => $v) {
|
||||
$cookies[] = $k.'='.$v;
|
||||
@@ -533,19 +542,19 @@ class Request
|
||||
|
||||
foreach ($this->headers->all() as $key => $value) {
|
||||
$key = strtoupper(str_replace('-', '_', $key));
|
||||
if (\in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
|
||||
if (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) {
|
||||
$_SERVER[$key] = implode(', ', $value);
|
||||
} else {
|
||||
$_SERVER['HTTP_'.$key] = implode(', ', $value);
|
||||
}
|
||||
}
|
||||
|
||||
$request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE);
|
||||
$request = ['g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE];
|
||||
|
||||
$requestOrder = ini_get('request_order') ?: ini_get('variables_order');
|
||||
$requestOrder = \ini_get('request_order') ?: \ini_get('variables_order');
|
||||
$requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
|
||||
|
||||
$_REQUEST = array(array());
|
||||
$_REQUEST = [[]];
|
||||
|
||||
foreach (str_split($requestOrder) as $order) {
|
||||
$_REQUEST[] = $request[$order];
|
||||
@@ -559,14 +568,20 @@ class Request
|
||||
*
|
||||
* You should only list the reverse proxies that you manage directly.
|
||||
*
|
||||
* @param array $proxies A list of trusted proxies
|
||||
* @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR']
|
||||
* @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies
|
||||
*
|
||||
* @throws \InvalidArgumentException When $trustedHeaderSet is invalid
|
||||
*/
|
||||
public static function setTrustedProxies(array $proxies, int $trustedHeaderSet)
|
||||
{
|
||||
self::$trustedProxies = $proxies;
|
||||
self::$trustedProxies = array_reduce($proxies, function ($proxies, $proxy) {
|
||||
if ('REMOTE_ADDR' !== $proxy) {
|
||||
$proxies[] = $proxy;
|
||||
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
$proxies[] = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
return $proxies;
|
||||
}, []);
|
||||
self::$trustedHeaderSet = $trustedHeaderSet;
|
||||
}
|
||||
|
||||
@@ -603,7 +618,7 @@ class Request
|
||||
return sprintf('{%s}i', $hostPattern);
|
||||
}, $hostPatterns);
|
||||
// we need to reset trusted hosts on trusted host patterns change
|
||||
self::$trustedHosts = array();
|
||||
self::$trustedHosts = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,14 +643,14 @@ class Request
|
||||
*/
|
||||
public static function normalizeQueryString($qs)
|
||||
{
|
||||
if ('' == $qs) {
|
||||
if ('' === ($qs ?? '')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
parse_str($qs, $qs);
|
||||
ksort($qs);
|
||||
|
||||
return http_build_query($qs, '', '&', PHP_QUERY_RFC3986);
|
||||
return http_build_query($qs, '', '&', \PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -671,7 +686,7 @@ class Request
|
||||
* flexibility in controllers, it is better to explicitly get request parameters from the appropriate
|
||||
* public property instead (attributes, query, request).
|
||||
*
|
||||
* Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
|
||||
* Order of precedence: PATH (routing placeholders or custom attributes), GET, POST
|
||||
*
|
||||
* @param string $key The key
|
||||
* @param mixed $default The default value if the parameter key does not exist
|
||||
@@ -698,7 +713,7 @@ class Request
|
||||
/**
|
||||
* Gets the Session.
|
||||
*
|
||||
* @return SessionInterface|null The session
|
||||
* @return SessionInterface The session
|
||||
*/
|
||||
public function getSession()
|
||||
{
|
||||
@@ -708,8 +723,8 @@ class Request
|
||||
}
|
||||
|
||||
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');
|
||||
@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.');
|
||||
}
|
||||
|
||||
return $session;
|
||||
@@ -741,11 +756,6 @@ class Request
|
||||
return null !== $this->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Session.
|
||||
*
|
||||
* @param SessionInterface $session The Session
|
||||
*/
|
||||
public function setSession(SessionInterface $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
@@ -777,10 +787,10 @@ class Request
|
||||
$ip = $this->server->get('REMOTE_ADDR');
|
||||
|
||||
if (!$this->isFromTrustedProxy()) {
|
||||
return array($ip);
|
||||
return [$ip];
|
||||
}
|
||||
|
||||
return $this->getTrustedValues(self::HEADER_X_FORWARDED_FOR, $ip) ?: array($ip);
|
||||
return $this->getTrustedValues(self::HEADER_X_FORWARDED_FOR, $ip) ?: [$ip];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -792,10 +802,14 @@ class Request
|
||||
* being the original client, and each successive proxy that passed the request
|
||||
* adding the IP address where it received the request from.
|
||||
*
|
||||
* If your reverse proxy uses a different header name than "X-Forwarded-For",
|
||||
* ("Client-Ip" for instance), configure it via the $trustedHeaderSet
|
||||
* argument of the Request::setTrustedProxies() method instead.
|
||||
*
|
||||
* @return string|null The client IP address
|
||||
*
|
||||
* @see getClientIps()
|
||||
* @see http://en.wikipedia.org/wiki/X-Forwarded-For
|
||||
* @see https://wikipedia.org/wiki/X-Forwarded-For
|
||||
*/
|
||||
public function getClientIp()
|
||||
{
|
||||
@@ -913,8 +927,8 @@ class Request
|
||||
$pos = strrpos($host, ':');
|
||||
}
|
||||
|
||||
if (false !== $pos) {
|
||||
return (int) substr($host, $pos + 1);
|
||||
if (false !== $pos && $port = substr($host, $pos + 1)) {
|
||||
return (int) $port;
|
||||
}
|
||||
|
||||
return 'https' === $this->getScheme() ? 443 : 80;
|
||||
@@ -943,7 +957,7 @@ class Request
|
||||
/**
|
||||
* Gets the user info.
|
||||
*
|
||||
* @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server
|
||||
* @return string|null A user name if any and, optionally, scheme-specific information about how to gain authorization to access the server
|
||||
*/
|
||||
public function getUserInfo()
|
||||
{
|
||||
@@ -1080,7 +1094,7 @@ class Request
|
||||
// A reference to the same base directory or an empty subdirectory must be prefixed with "./".
|
||||
// This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
|
||||
// as the first segment of a relative-path reference, as it would be mistaken for a scheme name
|
||||
// (see http://tools.ietf.org/html/rfc3986#section-4.2).
|
||||
// (see https://tools.ietf.org/html/rfc3986#section-4.2).
|
||||
return !isset($path[0]) || '/' === $path[0]
|
||||
|| false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
|
||||
? "./$path" : $path;
|
||||
@@ -1114,7 +1128,7 @@ class Request
|
||||
public function isSecure()
|
||||
{
|
||||
if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_X_FORWARDED_PROTO)) {
|
||||
return \in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true);
|
||||
return \in_array(strtolower($proto[0]), ['https', 'on', 'ssl', '1'], true);
|
||||
}
|
||||
|
||||
$https = $this->server->get('HTTPS');
|
||||
@@ -1214,22 +1228,37 @@ class Request
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
if (null === $this->method) {
|
||||
$this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
|
||||
|
||||
if ('POST' === $this->method) {
|
||||
if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) {
|
||||
$this->method = strtoupper($method);
|
||||
} elseif (self::$httpMethodParameterOverride) {
|
||||
$method = $this->request->get('_method', $this->query->get('_method', 'POST'));
|
||||
if (\is_string($method)) {
|
||||
$this->method = strtoupper($method);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (null !== $this->method) {
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
return $this->method;
|
||||
$this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
|
||||
|
||||
if ('POST' !== $this->method) {
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
$method = $this->headers->get('X-HTTP-METHOD-OVERRIDE');
|
||||
|
||||
if (!$method && self::$httpMethodParameterOverride) {
|
||||
$method = $this->request->get('_method', $this->query->get('_method', 'POST'));
|
||||
}
|
||||
|
||||
if (!\is_string($method)) {
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
$method = strtoupper($method);
|
||||
|
||||
if (\in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'PATCH', 'PURGE', 'TRACE'], true)) {
|
||||
return $this->method = $method;
|
||||
}
|
||||
|
||||
if (!preg_match('/^[A-Z]++$/D', $method)) {
|
||||
throw new SuspiciousOperationException(sprintf('Invalid method override "%s".', $method));
|
||||
}
|
||||
|
||||
return $this->method = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1273,7 +1302,7 @@ class Request
|
||||
static::initializeFormats();
|
||||
}
|
||||
|
||||
return isset(static::$formats[$format]) ? static::$formats[$format] : array();
|
||||
return static::$formats[$format] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1302,6 +1331,8 @@ class Request
|
||||
return $format;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1316,7 +1347,7 @@ class Request
|
||||
static::initializeFormats();
|
||||
}
|
||||
|
||||
static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
|
||||
static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : [$mimeTypes];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1328,9 +1359,11 @@ class Request
|
||||
* * _format request attribute
|
||||
* * $default
|
||||
*
|
||||
* @see getPreferredFormat
|
||||
*
|
||||
* @param string|null $default The default format
|
||||
*
|
||||
* @return string The request format
|
||||
* @return string|null The request format
|
||||
*/
|
||||
public function getRequestFormat($default = 'html')
|
||||
{
|
||||
@@ -1338,7 +1371,7 @@ class Request
|
||||
$this->format = $this->attributes->get('_format');
|
||||
}
|
||||
|
||||
return null === $this->format ? $default : $this->format;
|
||||
return $this->format ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1358,7 +1391,7 @@ class Request
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->getFormat($this->headers->get('CONTENT_TYPE'));
|
||||
return $this->getFormat($this->headers->get('CONTENT_TYPE', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1422,18 +1455,15 @@ class Request
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc7231#section-4.2.1
|
||||
*
|
||||
* @param bool $andCacheable Adds the additional condition that the method should be cacheable. True by default.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isMethodSafe(/* $andCacheable = true */)
|
||||
public function isMethodSafe()
|
||||
{
|
||||
if (!\func_num_args() || func_get_arg(0)) {
|
||||
// setting $andCacheable to false should be deprecated in 4.1
|
||||
throw new \BadMethodCallException('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is not supported.');
|
||||
if (\func_num_args() > 0) {
|
||||
@trigger_error(sprintf('Passing arguments to "%s()" has been deprecated since Symfony 4.4; use "%s::isMethodCacheable()" to check if the method is cacheable instead.', __METHOD__, __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
return \in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE'));
|
||||
return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1443,7 +1473,7 @@ class Request
|
||||
*/
|
||||
public function isMethodIdempotent()
|
||||
{
|
||||
return \in_array($this->getMethod(), array('HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE'));
|
||||
return \in_array($this->getMethod(), ['HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1455,7 +1485,7 @@ class Request
|
||||
*/
|
||||
public function isMethodCacheable()
|
||||
{
|
||||
return \in_array($this->getMethod(), array('GET', 'HEAD'));
|
||||
return \in_array($this->getMethod(), ['GET', 'HEAD']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1467,12 +1497,12 @@ class Request
|
||||
* if the proxy is trusted (see "setTrustedProxies()"), otherwise it returns
|
||||
* the latter (from the "SERVER_PROTOCOL" server parameter).
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getProtocolVersion()
|
||||
{
|
||||
if ($this->isFromTrustedProxy()) {
|
||||
preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches);
|
||||
preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via') ?? '', $matches);
|
||||
|
||||
if ($matches) {
|
||||
return 'HTTP/'.$matches[2];
|
||||
@@ -1488,8 +1518,6 @@ class Request
|
||||
* @param bool $asResource If true, a resource will be returned
|
||||
*
|
||||
* @return string|resource The request body content or a resource to read the body stream
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function getContent($asResource = false)
|
||||
{
|
||||
@@ -1513,7 +1541,7 @@ class Request
|
||||
|
||||
$this->content = false;
|
||||
|
||||
return fopen('php://input', 'rb');
|
||||
return fopen('php://input', 'r');
|
||||
}
|
||||
|
||||
if ($currentContentIsResource) {
|
||||
@@ -1536,7 +1564,7 @@ class Request
|
||||
*/
|
||||
public function getETags()
|
||||
{
|
||||
return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
|
||||
return preg_split('/\s*,\s*/', $this->headers->get('If-None-Match', ''), -1, \PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1547,10 +1575,33 @@ class Request
|
||||
return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preferred format for the response by inspecting, in the following order:
|
||||
* * the request format set using setRequestFormat
|
||||
* * the values of the Accept HTTP header.
|
||||
*
|
||||
* Note that if you use this method, you should send the "Vary: Accept" header
|
||||
* in the response to prevent any issues with intermediary HTTP caches.
|
||||
*/
|
||||
public function getPreferredFormat(?string $default = 'html'): ?string
|
||||
{
|
||||
if (null !== $this->preferredFormat || null !== $this->preferredFormat = $this->getRequestFormat(null)) {
|
||||
return $this->preferredFormat;
|
||||
}
|
||||
|
||||
foreach ($this->getAcceptableContentTypes() as $mimeType) {
|
||||
if ($this->preferredFormat = $this->getFormat($mimeType)) {
|
||||
return $this->preferredFormat;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred language.
|
||||
*
|
||||
* @param array $locales An array of ordered available locales
|
||||
* @param string[] $locales An array of ordered available locales
|
||||
*
|
||||
* @return string|null The preferred locale
|
||||
*/
|
||||
@@ -1559,14 +1610,14 @@ class Request
|
||||
$preferredLanguages = $this->getLanguages();
|
||||
|
||||
if (empty($locales)) {
|
||||
return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
|
||||
return $preferredLanguages[0] ?? null;
|
||||
}
|
||||
|
||||
if (!$preferredLanguages) {
|
||||
return $locales[0];
|
||||
}
|
||||
|
||||
$extendedPreferredLanguages = array();
|
||||
$extendedPreferredLanguages = [];
|
||||
foreach ($preferredLanguages as $language) {
|
||||
$extendedPreferredLanguages[] = $language;
|
||||
if (false !== $position = strpos($language, '_')) {
|
||||
@@ -1579,7 +1630,7 @@ class Request
|
||||
|
||||
$preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
|
||||
|
||||
return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
|
||||
return $preferredLanguages[0] ?? $locales[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1594,9 +1645,10 @@ class Request
|
||||
}
|
||||
|
||||
$languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
|
||||
$this->languages = array();
|
||||
foreach ($languages as $lang => $acceptHeaderItem) {
|
||||
if (false !== strpos($lang, '-')) {
|
||||
$this->languages = [];
|
||||
foreach ($languages as $acceptHeaderItem) {
|
||||
$lang = $acceptHeaderItem->getValue();
|
||||
if (str_contains($lang, '-')) {
|
||||
$codes = explode('-', $lang);
|
||||
if ('i' === $codes[0]) {
|
||||
// Language not listed in ISO 639 that are not variants
|
||||
@@ -1633,7 +1685,7 @@ class Request
|
||||
return $this->charsets;
|
||||
}
|
||||
|
||||
return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
|
||||
return $this->charsets = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1647,7 +1699,7 @@ class Request
|
||||
return $this->encodings;
|
||||
}
|
||||
|
||||
return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all());
|
||||
return $this->encodings = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1661,16 +1713,16 @@ class Request
|
||||
return $this->acceptableContentTypes;
|
||||
}
|
||||
|
||||
return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
|
||||
return $this->acceptableContentTypes = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request is a XMLHttpRequest.
|
||||
* Returns true if the request is an XMLHttpRequest.
|
||||
*
|
||||
* It works if your JavaScript library sets an X-Requested-With HTTP header.
|
||||
* It is known to work with common JavaScript frameworks:
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
|
||||
* @see https://wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
|
||||
*
|
||||
* @return bool true if the request is an XMLHttpRequest, false otherwise
|
||||
*/
|
||||
@@ -1682,9 +1734,9 @@ class Request
|
||||
/*
|
||||
* The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
|
||||
*
|
||||
* Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
|
||||
* Code subject to the new BSD license (https://framework.zend.com/license).
|
||||
*
|
||||
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (https://www.zend.com/)
|
||||
*/
|
||||
|
||||
protected function prepareRequestUri()
|
||||
@@ -1699,15 +1751,23 @@ class Request
|
||||
} 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
|
||||
$uriComponents = parse_url($requestUri);
|
||||
if ('' !== $requestUri && '/' === $requestUri[0]) {
|
||||
// To only use path and query remove the fragment.
|
||||
if (false !== $pos = strpos($requestUri, '#')) {
|
||||
$requestUri = substr($requestUri, 0, $pos);
|
||||
}
|
||||
} else {
|
||||
// HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path,
|
||||
// only use URL path.
|
||||
$uriComponents = parse_url($requestUri);
|
||||
|
||||
if (isset($uriComponents['path'])) {
|
||||
$requestUri = $uriComponents['path'];
|
||||
}
|
||||
if (isset($uriComponents['path'])) {
|
||||
$requestUri = $uriComponents['path'];
|
||||
}
|
||||
|
||||
if (isset($uriComponents['query'])) {
|
||||
$requestUri .= '?'.$uriComponents['query'];
|
||||
if (isset($uriComponents['query'])) {
|
||||
$requestUri .= '?'.$uriComponents['query'];
|
||||
}
|
||||
}
|
||||
} elseif ($this->server->has('ORIG_PATH_INFO')) {
|
||||
// IIS 5.0, PHP as CGI
|
||||
@@ -1731,13 +1791,13 @@ class Request
|
||||
*/
|
||||
protected function prepareBaseUrl()
|
||||
{
|
||||
$filename = basename($this->server->get('SCRIPT_FILENAME'));
|
||||
$filename = basename($this->server->get('SCRIPT_FILENAME', ''));
|
||||
|
||||
if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
|
||||
if (basename($this->server->get('SCRIPT_NAME', '')) === $filename) {
|
||||
$baseUrl = $this->server->get('SCRIPT_NAME');
|
||||
} elseif (basename($this->server->get('PHP_SELF')) === $filename) {
|
||||
} elseif (basename($this->server->get('PHP_SELF', '')) === $filename) {
|
||||
$baseUrl = $this->server->get('PHP_SELF');
|
||||
} elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
|
||||
} elseif (basename($this->server->get('ORIG_SCRIPT_NAME', '')) === $filename) {
|
||||
$baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
|
||||
} else {
|
||||
// Backtrack up the script_filename to find the portion matching
|
||||
@@ -1762,12 +1822,12 @@ class Request
|
||||
$requestUri = '/'.$requestUri;
|
||||
}
|
||||
|
||||
if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
|
||||
if ($baseUrl && null !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
|
||||
// full $baseUrl matches
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(\dirname($baseUrl), '/'.\DIRECTORY_SEPARATOR).'/')) {
|
||||
if ($baseUrl && null !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(\dirname($baseUrl), '/'.\DIRECTORY_SEPARATOR).'/')) {
|
||||
// directory portion of $baseUrl matches
|
||||
return rtrim($prefix, '/'.\DIRECTORY_SEPARATOR);
|
||||
}
|
||||
@@ -1777,7 +1837,7 @@ class Request
|
||||
$truncatedRequestUri = substr($requestUri, 0, $pos);
|
||||
}
|
||||
|
||||
$basename = basename($baseUrl);
|
||||
$basename = basename($baseUrl ?? '');
|
||||
if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
|
||||
// no match whatsoever; set it blank
|
||||
return '';
|
||||
@@ -1848,7 +1908,7 @@ class Request
|
||||
return '/';
|
||||
}
|
||||
|
||||
return (string) $pathInfo;
|
||||
return $pathInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1856,44 +1916,42 @@ class Request
|
||||
*/
|
||||
protected static function initializeFormats()
|
||||
{
|
||||
static::$formats = array(
|
||||
'html' => array('text/html', 'application/xhtml+xml'),
|
||||
'txt' => array('text/plain'),
|
||||
'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'),
|
||||
'css' => array('text/css'),
|
||||
'json' => array('application/json', 'application/x-json'),
|
||||
'jsonld' => array('application/ld+json'),
|
||||
'xml' => array('text/xml', 'application/xml', 'application/x-xml'),
|
||||
'rdf' => array('application/rdf+xml'),
|
||||
'atom' => array('application/atom+xml'),
|
||||
'rss' => array('application/rss+xml'),
|
||||
'form' => array('application/x-www-form-urlencoded'),
|
||||
);
|
||||
static::$formats = [
|
||||
'html' => ['text/html', 'application/xhtml+xml'],
|
||||
'txt' => ['text/plain'],
|
||||
'js' => ['application/javascript', 'application/x-javascript', 'text/javascript'],
|
||||
'css' => ['text/css'],
|
||||
'json' => ['application/json', 'application/x-json'],
|
||||
'jsonld' => ['application/ld+json'],
|
||||
'xml' => ['text/xml', 'application/xml', 'application/x-xml'],
|
||||
'rdf' => ['application/rdf+xml'],
|
||||
'atom' => ['application/atom+xml'],
|
||||
'rss' => ['application/rss+xml'],
|
||||
'form' => ['application/x-www-form-urlencoded'],
|
||||
];
|
||||
}
|
||||
|
||||
private function setPhpDefaultLocale(string $locale)
|
||||
private function setPhpDefaultLocale(string $locale): void
|
||||
{
|
||||
// if either the class Locale doesn't exist, or an exception is thrown when
|
||||
// setting the default locale, the intl module is not installed, and
|
||||
// the call can be ignored:
|
||||
try {
|
||||
if (class_exists('Locale', false)) {
|
||||
if (class_exists(\Locale::class, false)) {
|
||||
\Locale::setDefault($locale);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Returns the prefix as encoded in the string when the string starts with
|
||||
* the given prefix, false otherwise.
|
||||
*
|
||||
* @return string|false The prefix as it is encoded in $string, or false
|
||||
* the given prefix, null otherwise.
|
||||
*/
|
||||
private function getUrlencodedPrefix(string $string, string $prefix)
|
||||
private function getUrlencodedPrefix(string $string, string $prefix): ?string
|
||||
{
|
||||
if (0 !== strpos(rawurldecode($string), $prefix)) {
|
||||
return false;
|
||||
if (!str_starts_with(rawurldecode($string), $prefix)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$len = \strlen($prefix);
|
||||
@@ -1902,13 +1960,13 @@ class Request
|
||||
return $match[0];
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
|
||||
private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null): self
|
||||
{
|
||||
if (self::$requestFactory) {
|
||||
$request = \call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);
|
||||
$request = (self::$requestFactory)($query, $request, $attributes, $cookies, $files, $server, $content);
|
||||
|
||||
if (!$request instanceof self) {
|
||||
throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
|
||||
@@ -1930,31 +1988,31 @@ class Request
|
||||
*/
|
||||
public function isFromTrustedProxy()
|
||||
{
|
||||
return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
|
||||
return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR', ''), self::$trustedProxies);
|
||||
}
|
||||
|
||||
private function getTrustedValues($type, $ip = null)
|
||||
private function getTrustedValues(int $type, string $ip = null): array
|
||||
{
|
||||
$clientValues = array();
|
||||
$forwardedValues = array();
|
||||
$clientValues = [];
|
||||
$forwardedValues = [];
|
||||
|
||||
if ((self::$trustedHeaderSet & $type) && $this->headers->has(self::$trustedHeaders[$type])) {
|
||||
foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) {
|
||||
if ((self::$trustedHeaderSet & $type) && $this->headers->has(self::TRUSTED_HEADERS[$type])) {
|
||||
foreach (explode(',', $this->headers->get(self::TRUSTED_HEADERS[$type])) as $v) {
|
||||
$clientValues[] = (self::HEADER_X_FORWARDED_PORT === $type ? '0.0.0.0:' : '').trim($v);
|
||||
}
|
||||
}
|
||||
|
||||
if ((self::$trustedHeaderSet & self::HEADER_FORWARDED) && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
|
||||
$forwarded = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
|
||||
if ((self::$trustedHeaderSet & self::HEADER_FORWARDED) && $this->headers->has(self::TRUSTED_HEADERS[self::HEADER_FORWARDED])) {
|
||||
$forwarded = $this->headers->get(self::TRUSTED_HEADERS[self::HEADER_FORWARDED]);
|
||||
$parts = HeaderUtils::split($forwarded, ',;=');
|
||||
$forwardedValues = array();
|
||||
$param = self::$forwardedParams[$type];
|
||||
$forwardedValues = [];
|
||||
$param = self::FORWARDED_PARAMS[$type];
|
||||
foreach ($parts as $subParts) {
|
||||
if (null === $v = HeaderUtils::combine($subParts)[$param] ?? null) {
|
||||
continue;
|
||||
}
|
||||
if (self::HEADER_X_FORWARDED_PORT === $type) {
|
||||
if (']' === substr($v, -1) || false === $v = strrchr($v, ':')) {
|
||||
if (str_ends_with($v, ']') || false === $v = strrchr($v, ':')) {
|
||||
$v = $this->isSecure() ? ':443' : ':80';
|
||||
}
|
||||
$v = '0.0.0.0'.$v;
|
||||
@@ -1977,17 +2035,17 @@ class Request
|
||||
}
|
||||
|
||||
if (!$this->isForwardedValid) {
|
||||
return null !== $ip ? array('0.0.0.0', $ip) : array();
|
||||
return null !== $ip ? ['0.0.0.0', $ip] : [];
|
||||
}
|
||||
$this->isForwardedValid = false;
|
||||
|
||||
throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type]));
|
||||
throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::TRUSTED_HEADERS[self::HEADER_FORWARDED], self::TRUSTED_HEADERS[$type]));
|
||||
}
|
||||
|
||||
private function normalizeAndFilterClientIps(array $clientIps, $ip)
|
||||
private function normalizeAndFilterClientIps(array $clientIps, string $ip): array
|
||||
{
|
||||
if (!$clientIps) {
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
$clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
|
||||
$firstTrustedIp = null;
|
||||
@@ -2000,13 +2058,13 @@ class Request
|
||||
if ($i) {
|
||||
$clientIps[$key] = $clientIp = substr($clientIp, 0, $i);
|
||||
}
|
||||
} elseif (0 === strpos($clientIp, '[')) {
|
||||
} elseif (str_starts_with($clientIp, '[')) {
|
||||
// Strip brackets and :port from IPv6 addresses.
|
||||
$i = strpos($clientIp, ']', 1);
|
||||
$clientIps[$key] = $clientIp = substr($clientIp, 1, $i - 1);
|
||||
}
|
||||
|
||||
if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
|
||||
if (!filter_var($clientIp, \FILTER_VALIDATE_IP)) {
|
||||
unset($clientIps[$key]);
|
||||
|
||||
continue;
|
||||
@@ -2023,6 +2081,6 @@ class Request
|
||||
}
|
||||
|
||||
// Now the IP chain contains only untrusted proxies and the client IP
|
||||
return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
|
||||
return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp];
|
||||
}
|
||||
}
|
||||
|
@@ -36,32 +36,29 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $methods = array();
|
||||
private $methods = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $ips = array();
|
||||
private $ips = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $attributes = array();
|
||||
private $attributes = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $schemes = array();
|
||||
private $schemes = [];
|
||||
|
||||
/**
|
||||
* @param string|null $path
|
||||
* @param string|null $host
|
||||
* @param string|string[]|null $methods
|
||||
* @param string|string[]|null $ips
|
||||
* @param array $attributes
|
||||
* @param string|string[]|null $schemes
|
||||
*/
|
||||
public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null, int $port = null)
|
||||
public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null, int $port = null)
|
||||
{
|
||||
$this->matchPath($path);
|
||||
$this->matchHost($host);
|
||||
@@ -82,7 +79,7 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
*/
|
||||
public function matchScheme($scheme)
|
||||
{
|
||||
$this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : array();
|
||||
$this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +97,7 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
*
|
||||
* @param int|null $port The port number to connect to
|
||||
*/
|
||||
public function matchPort(int $port = null)
|
||||
public function matchPort(?int $port)
|
||||
{
|
||||
$this->port = $port;
|
||||
}
|
||||
@@ -132,7 +129,7 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
*/
|
||||
public function matchIps($ips)
|
||||
{
|
||||
$this->ips = null !== $ips ? (array) $ips : array();
|
||||
$this->ips = null !== $ips ? (array) $ips : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,7 +139,7 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
*/
|
||||
public function matchMethod($method)
|
||||
{
|
||||
$this->methods = null !== $method ? array_map('strtoupper', (array) $method) : array();
|
||||
$this->methods = null !== $method ? array_map('strtoupper', (array) $method) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +167,11 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
}
|
||||
|
||||
foreach ($this->attributes as $key => $pattern) {
|
||||
if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) {
|
||||
$requestAttribute = $request->attributes->get($key);
|
||||
if (!\is_string($requestAttribute)) {
|
||||
return false;
|
||||
}
|
||||
if (!preg_match('{'.$pattern.'}', $requestAttribute)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
12
vendor/symfony/http-foundation/RequestStack.php
vendored
12
vendor/symfony/http-foundation/RequestStack.php
vendored
@@ -21,7 +21,7 @@ class RequestStack
|
||||
/**
|
||||
* @var Request[]
|
||||
*/
|
||||
private $requests = array();
|
||||
private $requests = [];
|
||||
|
||||
/**
|
||||
* Pushes a Request on the stack.
|
||||
@@ -47,7 +47,7 @@ class RequestStack
|
||||
public function pop()
|
||||
{
|
||||
if (!$this->requests) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_pop($this->requests);
|
||||
@@ -73,7 +73,7 @@ class RequestStack
|
||||
public function getMasterRequest()
|
||||
{
|
||||
if (!$this->requests) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->requests[0];
|
||||
@@ -94,10 +94,6 @@ class RequestStack
|
||||
{
|
||||
$pos = \count($this->requests) - 2;
|
||||
|
||||
if (!isset($this->requests[$pos])) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->requests[$pos];
|
||||
return $this->requests[$pos] ?? null;
|
||||
}
|
||||
}
|
||||
|
226
vendor/symfony/http-foundation/Response.php
vendored
226
vendor/symfony/http-foundation/Response.php
vendored
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(ResponseHeaderBag::class);
|
||||
|
||||
/**
|
||||
* Response represents an HTTP response.
|
||||
*
|
||||
@@ -18,77 +21,77 @@ namespace Symfony\Component\HttpFoundation;
|
||||
*/
|
||||
class Response
|
||||
{
|
||||
const HTTP_CONTINUE = 100;
|
||||
const HTTP_SWITCHING_PROTOCOLS = 101;
|
||||
const HTTP_PROCESSING = 102; // RFC2518
|
||||
const HTTP_EARLY_HINTS = 103; // RFC8297
|
||||
const HTTP_OK = 200;
|
||||
const HTTP_CREATED = 201;
|
||||
const HTTP_ACCEPTED = 202;
|
||||
const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
|
||||
const HTTP_NO_CONTENT = 204;
|
||||
const HTTP_RESET_CONTENT = 205;
|
||||
const HTTP_PARTIAL_CONTENT = 206;
|
||||
const HTTP_MULTI_STATUS = 207; // RFC4918
|
||||
const HTTP_ALREADY_REPORTED = 208; // RFC5842
|
||||
const HTTP_IM_USED = 226; // RFC3229
|
||||
const HTTP_MULTIPLE_CHOICES = 300;
|
||||
const HTTP_MOVED_PERMANENTLY = 301;
|
||||
const HTTP_FOUND = 302;
|
||||
const HTTP_SEE_OTHER = 303;
|
||||
const HTTP_NOT_MODIFIED = 304;
|
||||
const HTTP_USE_PROXY = 305;
|
||||
const HTTP_RESERVED = 306;
|
||||
const HTTP_TEMPORARY_REDIRECT = 307;
|
||||
const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
|
||||
const HTTP_BAD_REQUEST = 400;
|
||||
const HTTP_UNAUTHORIZED = 401;
|
||||
const HTTP_PAYMENT_REQUIRED = 402;
|
||||
const HTTP_FORBIDDEN = 403;
|
||||
const HTTP_NOT_FOUND = 404;
|
||||
const HTTP_METHOD_NOT_ALLOWED = 405;
|
||||
const HTTP_NOT_ACCEPTABLE = 406;
|
||||
const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
|
||||
const HTTP_REQUEST_TIMEOUT = 408;
|
||||
const HTTP_CONFLICT = 409;
|
||||
const HTTP_GONE = 410;
|
||||
const HTTP_LENGTH_REQUIRED = 411;
|
||||
const HTTP_PRECONDITION_FAILED = 412;
|
||||
const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
|
||||
const HTTP_REQUEST_URI_TOO_LONG = 414;
|
||||
const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
|
||||
const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
|
||||
const HTTP_EXPECTATION_FAILED = 417;
|
||||
const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
|
||||
const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
|
||||
const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
|
||||
const HTTP_LOCKED = 423; // RFC4918
|
||||
const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
|
||||
public const HTTP_CONTINUE = 100;
|
||||
public const HTTP_SWITCHING_PROTOCOLS = 101;
|
||||
public const HTTP_PROCESSING = 102; // RFC2518
|
||||
public const HTTP_EARLY_HINTS = 103; // RFC8297
|
||||
public const HTTP_OK = 200;
|
||||
public const HTTP_CREATED = 201;
|
||||
public const HTTP_ACCEPTED = 202;
|
||||
public const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
|
||||
public const HTTP_NO_CONTENT = 204;
|
||||
public const HTTP_RESET_CONTENT = 205;
|
||||
public const HTTP_PARTIAL_CONTENT = 206;
|
||||
public const HTTP_MULTI_STATUS = 207; // RFC4918
|
||||
public const HTTP_ALREADY_REPORTED = 208; // RFC5842
|
||||
public const HTTP_IM_USED = 226; // RFC3229
|
||||
public const HTTP_MULTIPLE_CHOICES = 300;
|
||||
public const HTTP_MOVED_PERMANENTLY = 301;
|
||||
public const HTTP_FOUND = 302;
|
||||
public const HTTP_SEE_OTHER = 303;
|
||||
public const HTTP_NOT_MODIFIED = 304;
|
||||
public const HTTP_USE_PROXY = 305;
|
||||
public const HTTP_RESERVED = 306;
|
||||
public const HTTP_TEMPORARY_REDIRECT = 307;
|
||||
public const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
|
||||
public const HTTP_BAD_REQUEST = 400;
|
||||
public const HTTP_UNAUTHORIZED = 401;
|
||||
public const HTTP_PAYMENT_REQUIRED = 402;
|
||||
public const HTTP_FORBIDDEN = 403;
|
||||
public const HTTP_NOT_FOUND = 404;
|
||||
public const HTTP_METHOD_NOT_ALLOWED = 405;
|
||||
public const HTTP_NOT_ACCEPTABLE = 406;
|
||||
public const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
|
||||
public const HTTP_REQUEST_TIMEOUT = 408;
|
||||
public const HTTP_CONFLICT = 409;
|
||||
public const HTTP_GONE = 410;
|
||||
public const HTTP_LENGTH_REQUIRED = 411;
|
||||
public const HTTP_PRECONDITION_FAILED = 412;
|
||||
public const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
|
||||
public const HTTP_REQUEST_URI_TOO_LONG = 414;
|
||||
public const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
|
||||
public const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
|
||||
public const HTTP_EXPECTATION_FAILED = 417;
|
||||
public const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
|
||||
public const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
|
||||
public const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
|
||||
public const HTTP_LOCKED = 423; // RFC4918
|
||||
public const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
|
||||
const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04
|
||||
const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
|
||||
const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
|
||||
const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
|
||||
const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
|
||||
const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
|
||||
const HTTP_INTERNAL_SERVER_ERROR = 500;
|
||||
const HTTP_NOT_IMPLEMENTED = 501;
|
||||
const HTTP_BAD_GATEWAY = 502;
|
||||
const HTTP_SERVICE_UNAVAILABLE = 503;
|
||||
const HTTP_GATEWAY_TIMEOUT = 504;
|
||||
const HTTP_VERSION_NOT_SUPPORTED = 505;
|
||||
const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
|
||||
const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
|
||||
const HTTP_LOOP_DETECTED = 508; // RFC5842
|
||||
const HTTP_NOT_EXTENDED = 510; // RFC2774
|
||||
const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
|
||||
public const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
|
||||
public const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04
|
||||
public const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
|
||||
public const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
|
||||
public const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
|
||||
public const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
|
||||
public const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; // RFC7725
|
||||
public const HTTP_INTERNAL_SERVER_ERROR = 500;
|
||||
public const HTTP_NOT_IMPLEMENTED = 501;
|
||||
public const HTTP_BAD_GATEWAY = 502;
|
||||
public const HTTP_SERVICE_UNAVAILABLE = 503;
|
||||
public const HTTP_GATEWAY_TIMEOUT = 504;
|
||||
public const HTTP_VERSION_NOT_SUPPORTED = 505;
|
||||
public const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
|
||||
public const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
|
||||
public const HTTP_LOOP_DETECTED = 508; // RFC5842
|
||||
public const HTTP_NOT_EXTENDED = 510; // RFC2774
|
||||
public const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
|
||||
* @var ResponseHeaderBag
|
||||
*/
|
||||
public $headers;
|
||||
|
||||
@@ -121,14 +124,14 @@ class Response
|
||||
* Status codes translation table.
|
||||
*
|
||||
* The list of codes is complete according to the
|
||||
* {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry}
|
||||
* (last updated 2016-03-01).
|
||||
* {@link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml Hypertext Transfer Protocol (HTTP) Status Code Registry}
|
||||
* (last updated 2018-09-21).
|
||||
*
|
||||
* Unless otherwise noted, the status code is defined in RFC2616.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $statusTexts = array(
|
||||
public static $statusTexts = [
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
102 => 'Processing', // RFC2518
|
||||
@@ -191,12 +194,12 @@ class Response
|
||||
508 => 'Loop Detected', // RFC5842
|
||||
510 => 'Not Extended', // RFC2774
|
||||
511 => 'Network Authentication Required', // RFC6585
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException When the HTTP status code is not valid
|
||||
*/
|
||||
public function __construct($content = '', int $status = 200, array $headers = array())
|
||||
public function __construct($content = '', int $status = 200, array $headers = [])
|
||||
{
|
||||
$this->headers = new ResponseHeaderBag($headers);
|
||||
$this->setContent($content);
|
||||
@@ -218,7 +221,7 @@ class Response
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($content = '', $status = 200, $headers = array())
|
||||
public static function create($content = '', $status = 200, $headers = [])
|
||||
{
|
||||
return new static($content, $status, $headers);
|
||||
}
|
||||
@@ -267,10 +270,12 @@ class Response
|
||||
$this->setContent(null);
|
||||
$headers->remove('Content-Type');
|
||||
$headers->remove('Content-Length');
|
||||
// prevent PHP from sending the Content-Type header based on default_mimetype
|
||||
ini_set('default_mimetype', '');
|
||||
} else {
|
||||
// Content-type based on the Request
|
||||
if (!$headers->has('Content-Type')) {
|
||||
$format = $request->getRequestFormat();
|
||||
$format = $request->getRequestFormat(null);
|
||||
if (null !== $format && $mimeType = $request->getMimeType($format)) {
|
||||
$headers->set('Content-Type', $mimeType);
|
||||
}
|
||||
@@ -306,9 +311,9 @@ class Response
|
||||
}
|
||||
|
||||
// Check if we need to send extra expire info headers
|
||||
if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) {
|
||||
$this->headers->set('pragma', 'no-cache');
|
||||
$this->headers->set('expires', -1);
|
||||
if ('1.0' == $this->getProtocolVersion() && str_contains($headers->get('Cache-Control', ''), 'no-cache')) {
|
||||
$headers->set('pragma', 'no-cache');
|
||||
$headers->set('expires', -1);
|
||||
}
|
||||
|
||||
$this->ensureIEOverSSLCompatibility($request);
|
||||
@@ -344,7 +349,7 @@ class Response
|
||||
|
||||
// cookies
|
||||
foreach ($this->headers->getCookies() as $cookie) {
|
||||
header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode);
|
||||
header('Set-Cookie: '.$cookie, false, $this->statusCode);
|
||||
}
|
||||
|
||||
// status
|
||||
@@ -377,8 +382,9 @@ class Response
|
||||
|
||||
if (\function_exists('fastcgi_finish_request')) {
|
||||
fastcgi_finish_request();
|
||||
} elseif (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) {
|
||||
} elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
|
||||
static::closeOutputBuffers(0, true);
|
||||
flush();
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -397,7 +403,7 @@ class Response
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable(array($content, '__toString'))) {
|
||||
if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable([$content, '__toString'])) {
|
||||
throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', \gettype($content)));
|
||||
}
|
||||
|
||||
@@ -409,7 +415,7 @@ class Response
|
||||
/**
|
||||
* Gets the current response content.
|
||||
*
|
||||
* @return string Content
|
||||
* @return string|false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
@@ -460,7 +466,7 @@ class Response
|
||||
}
|
||||
|
||||
if (null === $text) {
|
||||
$this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status';
|
||||
$this->statusText = self::$statusTexts[$code] ?? 'unknown status';
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -529,7 +535,7 @@ class Response
|
||||
*/
|
||||
public function isCacheable(): bool
|
||||
{
|
||||
if (!\in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
|
||||
if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -628,7 +634,7 @@ class Response
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the response must be revalidated by caches.
|
||||
* Returns true if the response must be revalidated by shared caches once it has become stale.
|
||||
*
|
||||
* This method indicates that the response must not be served stale by a
|
||||
* cache in any circumstance without first revalidating with the origin.
|
||||
@@ -684,7 +690,7 @@ class Response
|
||||
return (int) $age;
|
||||
}
|
||||
|
||||
return max(time() - $this->getDate()->format('U'), 0);
|
||||
return max(time() - (int) $this->getDate()->format('U'), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -764,7 +770,7 @@ class Response
|
||||
}
|
||||
|
||||
if (null !== $this->getExpires()) {
|
||||
return (int) ($this->getExpires()->format('U') - $this->getDate()->format('U'));
|
||||
return (int) $this->getExpires()->format('U') - (int) $this->getDate()->format('U');
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -916,7 +922,7 @@ class Response
|
||||
if (null === $etag) {
|
||||
$this->headers->remove('Etag');
|
||||
} else {
|
||||
if (0 !== strpos($etag, '"')) {
|
||||
if (!str_starts_with($etag, '"')) {
|
||||
$etag = '"'.$etag.'"';
|
||||
}
|
||||
|
||||
@@ -939,7 +945,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'))) {
|
||||
if ($diff = array_diff(array_keys($options), ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff)));
|
||||
}
|
||||
|
||||
@@ -990,7 +996,7 @@ class Response
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc2616#section-10.3.5
|
||||
* @see https://tools.ietf.org/html/rfc2616#section-10.3.5
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
@@ -1000,7 +1006,7 @@ class Response
|
||||
$this->setContent(null);
|
||||
|
||||
// remove headers that MUST NOT be included with 304 Not Modified responses
|
||||
foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
|
||||
foreach (['Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified'] as $header) {
|
||||
$this->headers->remove($header);
|
||||
}
|
||||
|
||||
@@ -1024,11 +1030,11 @@ class Response
|
||||
*/
|
||||
public function getVary(): array
|
||||
{
|
||||
if (!$vary = $this->headers->get('Vary', null, false)) {
|
||||
return array();
|
||||
if (!$vary = $this->headers->all('Vary')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ret = array();
|
||||
$ret = [];
|
||||
foreach ($vary as $item) {
|
||||
$ret = array_merge($ret, preg_split('/[\s,]+/', $item));
|
||||
}
|
||||
@@ -1074,12 +1080,26 @@ class Response
|
||||
$lastModified = $this->headers->get('Last-Modified');
|
||||
$modifiedSince = $request->headers->get('If-Modified-Since');
|
||||
|
||||
if ($etags = $request->getETags()) {
|
||||
$notModified = \in_array($this->getEtag(), $etags) || \in_array('*', $etags);
|
||||
}
|
||||
if (($ifNoneMatchEtags = $request->getETags()) && (null !== $etag = $this->getEtag())) {
|
||||
if (0 == strncmp($etag, 'W/', 2)) {
|
||||
$etag = substr($etag, 2);
|
||||
}
|
||||
|
||||
if ($modifiedSince && $lastModified) {
|
||||
$notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
|
||||
// Use weak comparison as per https://tools.ietf.org/html/rfc7232#section-3.2.
|
||||
foreach ($ifNoneMatchEtags as $ifNoneMatchEtag) {
|
||||
if (0 == strncmp($ifNoneMatchEtag, 'W/', 2)) {
|
||||
$ifNoneMatchEtag = substr($ifNoneMatchEtag, 2);
|
||||
}
|
||||
|
||||
if ($ifNoneMatchEtag === $etag || '*' === $ifNoneMatchEtag) {
|
||||
$notModified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only do If-Modified-Since date comparison when If-None-Match is not present as per https://tools.ietf.org/html/rfc7232#section-3.3.
|
||||
elseif ($modifiedSince && $lastModified) {
|
||||
$notModified = strtotime($modifiedSince) >= strtotime($lastModified);
|
||||
}
|
||||
|
||||
if ($notModified) {
|
||||
@@ -1092,7 +1112,7 @@ class Response
|
||||
/**
|
||||
* Is response invalid?
|
||||
*
|
||||
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
* @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
@@ -1188,7 +1208,7 @@ class Response
|
||||
*/
|
||||
public function isRedirect(string $location = null): bool
|
||||
{
|
||||
return \in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
|
||||
return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1198,7 +1218,7 @@ class Response
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return \in_array($this->statusCode, array(204, 304));
|
||||
return \in_array($this->statusCode, [204, 304]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1208,11 +1228,11 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public static function closeOutputBuffers(int $targetLevel, bool $flush)
|
||||
public static function closeOutputBuffers(int $targetLevel, bool $flush): void
|
||||
{
|
||||
$status = ob_get_status(true);
|
||||
$level = \count($status);
|
||||
$flags = PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE);
|
||||
$flags = \PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? \PHP_OUTPUT_HANDLER_FLUSHABLE : \PHP_OUTPUT_HANDLER_CLEANABLE);
|
||||
|
||||
while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) {
|
||||
if ($flush) {
|
||||
@@ -1230,9 +1250,9 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
protected function ensureIEOverSSLCompatibility(Request $request)
|
||||
protected function ensureIEOverSSLCompatibility(Request $request): void
|
||||
{
|
||||
if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) && true === $request->isSecure()) {
|
||||
if (false !== stripos($this->headers->get('Content-Disposition') ?? '', 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT') ?? '', $match) && true === $request->isSecure()) {
|
||||
if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) {
|
||||
$this->headers->remove('Cache-Control');
|
||||
}
|
||||
|
@@ -18,17 +18,17 @@ namespace Symfony\Component\HttpFoundation;
|
||||
*/
|
||||
class ResponseHeaderBag extends HeaderBag
|
||||
{
|
||||
const COOKIES_FLAT = 'flat';
|
||||
const COOKIES_ARRAY = 'array';
|
||||
public const COOKIES_FLAT = 'flat';
|
||||
public const COOKIES_ARRAY = 'array';
|
||||
|
||||
const DISPOSITION_ATTACHMENT = 'attachment';
|
||||
const DISPOSITION_INLINE = 'inline';
|
||||
public const DISPOSITION_ATTACHMENT = 'attachment';
|
||||
public const DISPOSITION_INLINE = 'inline';
|
||||
|
||||
protected $computedCacheControl = array();
|
||||
protected $cookies = array();
|
||||
protected $headerNames = array();
|
||||
protected $computedCacheControl = [];
|
||||
protected $cookies = [];
|
||||
protected $headerNames = [];
|
||||
|
||||
public function __construct(array $headers = array())
|
||||
public function __construct(array $headers = [])
|
||||
{
|
||||
parent::__construct($headers);
|
||||
|
||||
@@ -49,9 +49,9 @@ class ResponseHeaderBag extends HeaderBag
|
||||
*/
|
||||
public function allPreserveCase()
|
||||
{
|
||||
$headers = array();
|
||||
$headers = [];
|
||||
foreach ($this->all() as $name => $value) {
|
||||
$headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value;
|
||||
$headers[$this->headerNames[$name] ?? $name] = $value;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
@@ -70,9 +70,9 @@ class ResponseHeaderBag extends HeaderBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function replace(array $headers = array())
|
||||
public function replace(array $headers = [])
|
||||
{
|
||||
$this->headerNames = array();
|
||||
$this->headerNames = [];
|
||||
|
||||
parent::replace($headers);
|
||||
|
||||
@@ -87,10 +87,19 @@ class ResponseHeaderBag extends HeaderBag
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string|null $key The name of the headers to return or null to get them all
|
||||
*/
|
||||
public function all()
|
||||
public function all(/* string $key = null */)
|
||||
{
|
||||
$headers = parent::all();
|
||||
|
||||
if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) {
|
||||
$key = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
return 'set-cookie' !== $key ? $headers[$key] ?? [] : array_map('strval', $this->getCookies());
|
||||
}
|
||||
|
||||
foreach ($this->getCookies() as $cookie) {
|
||||
$headers['set-cookie'][] = (string) $cookie;
|
||||
}
|
||||
@@ -103,11 +112,11 @@ class ResponseHeaderBag extends HeaderBag
|
||||
*/
|
||||
public function set($key, $values, $replace = true)
|
||||
{
|
||||
$uniqueKey = str_replace('_', '-', strtolower($key));
|
||||
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
if ('set-cookie' === $uniqueKey) {
|
||||
if ($replace) {
|
||||
$this->cookies = array();
|
||||
$this->cookies = [];
|
||||
}
|
||||
foreach ((array) $values as $cookie) {
|
||||
$this->setCookie(Cookie::fromString($cookie));
|
||||
@@ -122,9 +131,8 @@ class ResponseHeaderBag extends HeaderBag
|
||||
parent::set($key, $values, $replace);
|
||||
|
||||
// ensure the cache-control header has sensible defaults
|
||||
if (\in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'), true)) {
|
||||
$computed = $this->computeCacheControlValue();
|
||||
$this->headers['cache-control'] = array($computed);
|
||||
if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true) && '' !== $computed = $this->computeCacheControlValue()) {
|
||||
$this->headers['cache-control'] = [$computed];
|
||||
$this->headerNames['cache-control'] = 'Cache-Control';
|
||||
$this->computedCacheControl = $this->parseCacheControl($computed);
|
||||
}
|
||||
@@ -135,11 +143,11 @@ class ResponseHeaderBag extends HeaderBag
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
$uniqueKey = str_replace('_', '-', strtolower($key));
|
||||
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
|
||||
unset($this->headerNames[$uniqueKey]);
|
||||
|
||||
if ('set-cookie' === $uniqueKey) {
|
||||
$this->cookies = array();
|
||||
$this->cookies = [];
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -147,7 +155,7 @@ class ResponseHeaderBag extends HeaderBag
|
||||
parent::remove($key);
|
||||
|
||||
if ('cache-control' === $uniqueKey) {
|
||||
$this->computedCacheControl = array();
|
||||
$this->computedCacheControl = [];
|
||||
}
|
||||
|
||||
if ('date' === $uniqueKey) {
|
||||
@@ -160,7 +168,7 @@ class ResponseHeaderBag extends HeaderBag
|
||||
*/
|
||||
public function hasCacheControlDirective($key)
|
||||
{
|
||||
return array_key_exists($key, $this->computedCacheControl);
|
||||
return \array_key_exists($key, $this->computedCacheControl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +176,7 @@ class ResponseHeaderBag extends HeaderBag
|
||||
*/
|
||||
public function getCacheControlDirective($key)
|
||||
{
|
||||
return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
|
||||
return $this->computedCacheControl[$key] ?? null;
|
||||
}
|
||||
|
||||
public function setCookie(Cookie $cookie)
|
||||
@@ -216,15 +224,15 @@ class ResponseHeaderBag extends HeaderBag
|
||||
*/
|
||||
public function getCookies($format = self::COOKIES_FLAT)
|
||||
{
|
||||
if (!\in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
|
||||
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
|
||||
if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) {
|
||||
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY])));
|
||||
}
|
||||
|
||||
if (self::COOKIES_ARRAY === $format) {
|
||||
return $this->cookies;
|
||||
}
|
||||
|
||||
$flattenedCookies = array();
|
||||
$flattenedCookies = [];
|
||||
foreach ($this->cookies as $path) {
|
||||
foreach ($path as $cookies) {
|
||||
foreach ($cookies as $cookie) {
|
||||
@@ -244,10 +252,13 @@ class ResponseHeaderBag extends HeaderBag
|
||||
* @param string $domain
|
||||
* @param bool $secure
|
||||
* @param bool $httpOnly
|
||||
* @param string $sameSite
|
||||
*/
|
||||
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
|
||||
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true/* , $sameSite = null */)
|
||||
{
|
||||
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
|
||||
$sameSite = \func_num_args() > 5 ? func_get_arg(5) : null;
|
||||
|
||||
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, $sameSite));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,13 +279,13 @@ class ResponseHeaderBag extends HeaderBag
|
||||
*/
|
||||
protected function computeCacheControlValue()
|
||||
{
|
||||
if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
|
||||
return 'no-cache, private';
|
||||
}
|
||||
|
||||
if (!$this->cacheControl) {
|
||||
if ($this->has('Last-Modified') || $this->has('Expires')) {
|
||||
return 'private, must-revalidate'; // allows for heuristic expiration (RFC 7234 Section 4.2.2) in the case of "Last-Modified"
|
||||
}
|
||||
|
||||
// conservative by default
|
||||
return 'private, must-revalidate';
|
||||
return 'no-cache, private';
|
||||
}
|
||||
|
||||
$header = $this->getCacheControlHeader();
|
||||
@@ -290,7 +301,7 @@ class ResponseHeaderBag extends HeaderBag
|
||||
return $header;
|
||||
}
|
||||
|
||||
private function initDate()
|
||||
private function initDate(): void
|
||||
{
|
||||
$now = \DateTime::createFromFormat('U', time());
|
||||
$now->setTimezone(new \DateTimeZone('UTC'));
|
||||
|
25
vendor/symfony/http-foundation/ServerBag.php
vendored
25
vendor/symfony/http-foundation/ServerBag.php
vendored
@@ -27,32 +27,29 @@ class ServerBag extends ParameterBag
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
$headers = array();
|
||||
$contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
|
||||
$headers = [];
|
||||
foreach ($this->parameters as $key => $value) {
|
||||
if (0 === strpos($key, 'HTTP_')) {
|
||||
if (str_starts_with($key, 'HTTP_')) {
|
||||
$headers[substr($key, 5)] = $value;
|
||||
}
|
||||
// CONTENT_* are not prefixed with HTTP_
|
||||
elseif (isset($contentHeaders[$key])) {
|
||||
} elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) {
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->parameters['PHP_AUTH_USER'])) {
|
||||
$headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER'];
|
||||
$headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
|
||||
$headers['PHP_AUTH_PW'] = $this->parameters['PHP_AUTH_PW'] ?? '';
|
||||
} else {
|
||||
/*
|
||||
* php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
|
||||
* For this workaround to work, add these lines to your .htaccess file:
|
||||
* RewriteCond %{HTTP:Authorization} ^(.+)$
|
||||
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
* RewriteCond %{HTTP:Authorization} .+
|
||||
* RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
|
||||
*
|
||||
* A sample .htaccess file:
|
||||
* RewriteEngine On
|
||||
* RewriteCond %{HTTP:Authorization} ^(.+)$
|
||||
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
* RewriteCond %{HTTP:Authorization} .+
|
||||
* RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
|
||||
* RewriteCond %{REQUEST_FILENAME} !-f
|
||||
* RewriteRule ^(.*)$ app.php [QSA,L]
|
||||
*/
|
||||
@@ -69,7 +66,7 @@ class ServerBag extends ParameterBag
|
||||
// Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic
|
||||
$exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2);
|
||||
if (2 == \count($exploded)) {
|
||||
list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
|
||||
[$headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']] = $exploded;
|
||||
}
|
||||
} elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest '))) {
|
||||
// In some circumstances PHP_AUTH_DIGEST needs to be set
|
||||
@@ -79,7 +76,7 @@ class ServerBag extends ParameterBag
|
||||
/*
|
||||
* XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables,
|
||||
* I'll just set $headers['AUTHORIZATION'] here.
|
||||
* http://php.net/manual/en/reserved.variables.server.php
|
||||
* https://php.net/reserved.variables.server
|
||||
*/
|
||||
$headers['AUTHORIZATION'] = $authorizationHeader;
|
||||
}
|
||||
@@ -92,7 +89,7 @@ class ServerBag extends ParameterBag
|
||||
|
||||
// PHP_AUTH_USER/PHP_AUTH_PW
|
||||
if (isset($headers['PHP_AUTH_USER'])) {
|
||||
$headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
|
||||
$headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.($headers['PHP_AUTH_PW'] ?? ''));
|
||||
} elseif (isset($headers['PHP_AUTH_DIGEST'])) {
|
||||
$headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST'];
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
private $name = 'attributes';
|
||||
private $storageKey;
|
||||
|
||||
protected $attributes = array();
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* @param string $storageKey The key used to store attributes in the session
|
||||
@@ -63,7 +63,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return array_key_exists($name, $this->attributes);
|
||||
return \array_key_exists($name, $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
*/
|
||||
public function get($name, $default = null)
|
||||
{
|
||||
return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
|
||||
return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +95,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
*/
|
||||
public function replace(array $attributes)
|
||||
{
|
||||
$this->attributes = array();
|
||||
$this->attributes = [];
|
||||
foreach ($attributes as $key => $value) {
|
||||
$this->set($key, $value);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
public function remove($name)
|
||||
{
|
||||
$retval = null;
|
||||
if (array_key_exists($name, $this->attributes)) {
|
||||
if (\array_key_exists($name, $this->attributes)) {
|
||||
$retval = $this->attributes[$name];
|
||||
unset($this->attributes[$name]);
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
public function clear()
|
||||
{
|
||||
$return = $this->attributes;
|
||||
$this->attributes = array();
|
||||
$this->attributes = [];
|
||||
|
||||
return $return;
|
||||
}
|
||||
@@ -131,6 +131,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
*
|
||||
* @return \ArrayIterator An \ArrayIterator instance
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->attributes);
|
||||
@@ -141,6 +142,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
*
|
||||
* @return int The number of attributes
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \count($this->attributes);
|
||||
|
@@ -50,15 +50,10 @@ interface AttributeBagInterface extends SessionBagInterface
|
||||
/**
|
||||
* Returns attributes.
|
||||
*
|
||||
* @return array Attributes
|
||||
* @return array
|
||||
*/
|
||||
public function all();
|
||||
|
||||
/**
|
||||
* Sets attributes.
|
||||
*
|
||||
* @param array $attributes Attributes
|
||||
*/
|
||||
public function replace(array $attributes);
|
||||
|
||||
/**
|
||||
|
@@ -44,7 +44,7 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_key_exists($name, $attributes);
|
||||
return \array_key_exists($name, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
return $default;
|
||||
}
|
||||
|
||||
return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
|
||||
return \array_key_exists($name, $attributes) ? $attributes[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +81,7 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
$retval = null;
|
||||
$attributes = &$this->resolveAttributePath($name);
|
||||
$name = $this->resolveKey($name);
|
||||
if (null !== $attributes && array_key_exists($name, $attributes)) {
|
||||
if (null !== $attributes && \array_key_exists($name, $attributes)) {
|
||||
$retval = $attributes[$name];
|
||||
unset($attributes[$name]);
|
||||
}
|
||||
@@ -97,12 +97,12 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
* @param string $name Key name
|
||||
* @param bool $writeContext Write context, default false
|
||||
*
|
||||
* @return array
|
||||
* @return array|null
|
||||
*/
|
||||
protected function &resolveAttributePath($name, $writeContext = false)
|
||||
{
|
||||
$array = &$this->attributes;
|
||||
$name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name;
|
||||
$name = (str_starts_with($name, $this->namespaceCharacter)) ? substr($name, 1) : $name;
|
||||
|
||||
// Check if there is anything to do, else return
|
||||
if (!$name) {
|
||||
@@ -115,7 +115,7 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
return $array;
|
||||
}
|
||||
|
||||
$array[$parts[0]] = array();
|
||||
$array[$parts[0]] = [];
|
||||
|
||||
return $array;
|
||||
}
|
||||
@@ -123,14 +123,14 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
unset($parts[\count($parts) - 1]);
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (null !== $array && !array_key_exists($part, $array)) {
|
||||
if (null !== $array && !\array_key_exists($part, $array)) {
|
||||
if (!$writeContext) {
|
||||
$null = null;
|
||||
|
||||
return $null;
|
||||
}
|
||||
|
||||
$array[$part] = array();
|
||||
$array[$part] = [];
|
||||
}
|
||||
|
||||
$array = &$array[$part];
|
||||
|
@@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Flash;
|
||||
class AutoExpireFlashBag implements FlashBagInterface
|
||||
{
|
||||
private $name = 'flashes';
|
||||
private $flashes = array('display' => array(), 'new' => array());
|
||||
private $flashes = ['display' => [], 'new' => []];
|
||||
private $storageKey;
|
||||
|
||||
/**
|
||||
@@ -53,8 +53,8 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
// The logic: messages from the last request will be stored in new, so we move them to previous
|
||||
// This request we will show what is in 'display'. What is placed into 'new' this time round will
|
||||
// be moved to display next time round.
|
||||
$this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array();
|
||||
$this->flashes['new'] = array();
|
||||
$this->flashes['display'] = \array_key_exists('new', $this->flashes) ? $this->flashes['new'] : [];
|
||||
$this->flashes['new'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +68,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function peek($type, array $default = array())
|
||||
public function peek($type, array $default = [])
|
||||
{
|
||||
return $this->has($type) ? $this->flashes['display'][$type] : $default;
|
||||
}
|
||||
@@ -78,13 +78,13 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
*/
|
||||
public function peekAll()
|
||||
{
|
||||
return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array();
|
||||
return \array_key_exists('display', $this->flashes) ? $this->flashes['display'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($type, array $default = array())
|
||||
public function get($type, array $default = [])
|
||||
{
|
||||
$return = $default;
|
||||
|
||||
@@ -106,7 +106,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
public function all()
|
||||
{
|
||||
$return = $this->flashes['display'];
|
||||
$this->flashes['display'] = array();
|
||||
$this->flashes['display'] = [];
|
||||
|
||||
return $return;
|
||||
}
|
||||
@@ -132,7 +132,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
*/
|
||||
public function has($type)
|
||||
{
|
||||
return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
|
||||
return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Flash;
|
||||
class FlashBag implements FlashBagInterface
|
||||
{
|
||||
private $name = 'flashes';
|
||||
private $flashes = array();
|
||||
private $flashes = [];
|
||||
private $storageKey;
|
||||
|
||||
/**
|
||||
@@ -62,7 +62,7 @@ class FlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function peek($type, array $default = array())
|
||||
public function peek($type, array $default = [])
|
||||
{
|
||||
return $this->has($type) ? $this->flashes[$type] : $default;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class FlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($type, array $default = array())
|
||||
public function get($type, array $default = [])
|
||||
{
|
||||
if (!$this->has($type)) {
|
||||
return $default;
|
||||
@@ -97,7 +97,7 @@ class FlashBag implements FlashBagInterface
|
||||
public function all()
|
||||
{
|
||||
$return = $this->peekAll();
|
||||
$this->flashes = array();
|
||||
$this->flashes = [];
|
||||
|
||||
return $return;
|
||||
}
|
||||
@@ -123,7 +123,7 @@ class FlashBag implements FlashBagInterface
|
||||
*/
|
||||
public function has($type)
|
||||
{
|
||||
return array_key_exists($type, $this->flashes) && $this->flashes[$type];
|
||||
return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
|
||||
interface FlashBagInterface extends SessionBagInterface
|
||||
{
|
||||
/**
|
||||
* Adds a flash message for type.
|
||||
* Adds a flash message for the given type.
|
||||
*
|
||||
* @param string $type
|
||||
* @param mixed $message
|
||||
@@ -29,12 +29,12 @@ interface FlashBagInterface extends SessionBagInterface
|
||||
public function add($type, $message);
|
||||
|
||||
/**
|
||||
* Registers a message for a given type.
|
||||
* Registers one or more messages for a given type.
|
||||
*
|
||||
* @param string $type
|
||||
* @param string|array $message
|
||||
* @param string|array $messages
|
||||
*/
|
||||
public function set($type, $message);
|
||||
public function set($type, $messages);
|
||||
|
||||
/**
|
||||
* Gets flash messages for a given type.
|
||||
@@ -44,7 +44,7 @@ interface FlashBagInterface extends SessionBagInterface
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function peek($type, array $default = array());
|
||||
public function peek($type, array $default = []);
|
||||
|
||||
/**
|
||||
* Gets all flash messages.
|
||||
@@ -61,7 +61,7 @@ interface FlashBagInterface extends SessionBagInterface
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get($type, array $default = array());
|
||||
public function get($type, array $default = []);
|
||||
|
||||
/**
|
||||
* Gets and clears flashes from the stack.
|
||||
|
@@ -18,6 +18,11 @@ use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
|
||||
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 <fabien@symfony.com>
|
||||
* @author Drak <drak@zikula.org>
|
||||
@@ -28,23 +33,18 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
|
||||
private $flashName;
|
||||
private $attributeName;
|
||||
private $data = array();
|
||||
private $data = [];
|
||||
private $usageIndex = 0;
|
||||
|
||||
/**
|
||||
* @param SessionStorageInterface $storage A SessionStorageInterface instance
|
||||
* @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
|
||||
* @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
|
||||
*/
|
||||
public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
|
||||
{
|
||||
$this->storage = $storage ?: new NativeSessionStorage();
|
||||
$this->storage = $storage ?? new NativeSessionStorage();
|
||||
|
||||
$attributes = $attributes ?: new AttributeBag();
|
||||
$attributes = $attributes ?? new AttributeBag();
|
||||
$this->attributeName = $attributes->getName();
|
||||
$this->registerBag($attributes);
|
||||
|
||||
$flashes = $flashes ?: new FlashBag();
|
||||
$flashes = $flashes ?? new FlashBag();
|
||||
$this->flashName = $flashes->getName();
|
||||
$this->registerBag($flashes);
|
||||
}
|
||||
@@ -126,6 +126,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
*
|
||||
* @return \ArrayIterator An \ArrayIterator instance
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->getAttributeBag()->all());
|
||||
@@ -134,29 +135,23 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the number of attributes.
|
||||
*
|
||||
* @return int The number of attributes
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \count($this->getAttributeBag()->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getUsageIndex()
|
||||
public function &getUsageIndex(): int
|
||||
{
|
||||
return $this->usageIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function isEmpty()
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
if ($this->isStarted()) {
|
||||
++$this->usageIndex;
|
||||
@@ -253,7 +248,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function getBag($name)
|
||||
{
|
||||
return $this->storage->getBag($name)->getBag();
|
||||
$bag = $this->storage->getBag($name);
|
||||
|
||||
return method_exists($bag, 'getBag') ? $bag->getBag() : $bag;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,10 +267,8 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
* Gets the attributebag interface.
|
||||
*
|
||||
* Note that this method was added to help with IDE autocompletion.
|
||||
*
|
||||
* @return AttributeBagInterface
|
||||
*/
|
||||
private function getAttributeBag()
|
||||
private function getAttributeBag(): AttributeBagInterface
|
||||
{
|
||||
return $this->getBag($this->attributeName);
|
||||
}
|
||||
|
@@ -22,27 +22,21 @@ final class SessionBagProxy implements SessionBagInterface
|
||||
private $data;
|
||||
private $usageIndex;
|
||||
|
||||
public function __construct(SessionBagInterface $bag, array &$data, &$usageIndex)
|
||||
public function __construct(SessionBagInterface $bag, array &$data, ?int &$usageIndex)
|
||||
{
|
||||
$this->bag = $bag;
|
||||
$this->data = &$data;
|
||||
$this->usageIndex = &$usageIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SessionBagInterface
|
||||
*/
|
||||
public function getBag()
|
||||
public function getBag(): SessionBagInterface
|
||||
{
|
||||
++$this->usageIndex;
|
||||
|
||||
return $this->bag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
if (!isset($this->data[$this->bag->getStorageKey()])) {
|
||||
return true;
|
||||
@@ -55,7 +49,7 @@ final class SessionBagProxy implements SessionBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->bag->getName();
|
||||
}
|
||||
@@ -63,7 +57,7 @@ final class SessionBagProxy implements SessionBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initialize(array &$array)
|
||||
public function initialize(array &$array): void
|
||||
{
|
||||
++$this->usageIndex;
|
||||
$this->data[$this->bag->getStorageKey()] = &$array;
|
||||
@@ -74,7 +68,7 @@ final class SessionBagProxy implements SessionBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStorageKey()
|
||||
public function getStorageKey(): string
|
||||
{
|
||||
return $this->bag->getStorageKey();
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ interface SessionInterface
|
||||
/**
|
||||
* Starts the session storage.
|
||||
*
|
||||
* @return bool True if session started
|
||||
* @return bool
|
||||
*
|
||||
* @throws \RuntimeException if session fails to start
|
||||
*/
|
||||
@@ -32,7 +32,7 @@ interface SessionInterface
|
||||
/**
|
||||
* Returns the session ID.
|
||||
*
|
||||
* @return string The session ID
|
||||
* @return string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
@@ -46,7 +46,7 @@ interface SessionInterface
|
||||
/**
|
||||
* Returns the session name.
|
||||
*
|
||||
* @return mixed The session name
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
@@ -68,7 +68,7 @@ interface SessionInterface
|
||||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
*
|
||||
* @return bool True if session invalidated, false if error
|
||||
* @return bool
|
||||
*/
|
||||
public function invalidate($lifetime = null);
|
||||
|
||||
@@ -82,7 +82,7 @@ interface SessionInterface
|
||||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
*
|
||||
* @return bool True if session migrated, false if error
|
||||
* @return bool
|
||||
*/
|
||||
public function migrate($destroy = false, $lifetime = null);
|
||||
|
||||
@@ -100,7 +100,7 @@ interface SessionInterface
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return bool true if the attribute is defined, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name);
|
||||
|
||||
@@ -125,14 +125,12 @@ interface SessionInterface
|
||||
/**
|
||||
* Returns attributes.
|
||||
*
|
||||
* @return array Attributes
|
||||
* @return array
|
||||
*/
|
||||
public function all();
|
||||
|
||||
/**
|
||||
* Sets attributes.
|
||||
*
|
||||
* @param array $attributes Attributes
|
||||
*/
|
||||
public function replace(array $attributes);
|
||||
|
||||
|
@@ -30,7 +30,7 @@ final class SessionUtils
|
||||
$sessionCookie = null;
|
||||
$sessionCookiePrefix = sprintf(' %s=', urlencode($sessionName));
|
||||
$sessionCookieWithId = sprintf('%s%s;', $sessionCookiePrefix, urlencode($sessionId));
|
||||
$otherCookies = array();
|
||||
$otherCookies = [];
|
||||
foreach (headers_list() as $h) {
|
||||
if (0 !== stripos($h, 'Set-Cookie:')) {
|
||||
continue;
|
||||
|
@@ -29,13 +29,14 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess
|
||||
private $igbinaryEmptyData;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$this->sessionName = $sessionName;
|
||||
if (!headers_sent() && !ini_get('session.cache_limiter') && '0' !== ini_get('session.cache_limiter')) {
|
||||
header(sprintf('Cache-Control: max-age=%d, private, must-revalidate', 60 * (int) ini_get('session.cache_expire')));
|
||||
if (!headers_sent() && !\ini_get('session.cache_limiter') && '0' !== \ini_get('session.cache_limiter')) {
|
||||
header(sprintf('Cache-Control: max-age=%d, private, must-revalidate', 60 * (int) \ini_get('session.cache_expire')));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -64,19 +65,30 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess
|
||||
abstract protected function doDestroy($sessionId);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
$this->prefetchData = $this->read($sessionId);
|
||||
$this->prefetchId = $sessionId;
|
||||
|
||||
if (\PHP_VERSION_ID < 70317 || (70400 <= \PHP_VERSION_ID && \PHP_VERSION_ID < 70405)) {
|
||||
// work around https://bugs.php.net/79413
|
||||
foreach (debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) {
|
||||
if (!isset($frame['class']) && isset($frame['function']) && \in_array($frame['function'], ['session_regenerate_id', 'session_create_id'], true)) {
|
||||
return '' === $this->prefetchData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '' !== $this->prefetchData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($sessionId)
|
||||
{
|
||||
if (null !== $this->prefetchId) {
|
||||
@@ -98,13 +110,14 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
if (null === $this->igbinaryEmptyData) {
|
||||
// see https://github.com/igbinary/igbinary/issues/146
|
||||
$this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize(array()) : '';
|
||||
$this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize([]) : '';
|
||||
}
|
||||
if ('' === $data || $this->igbinaryEmptyData === $data) {
|
||||
return $this->destroy($sessionId);
|
||||
@@ -115,18 +128,27 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
if (!headers_sent() && filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
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)));
|
||||
throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', static::class));
|
||||
}
|
||||
$cookie = SessionUtils::popSessionCookie($this->sessionName, $sessionId);
|
||||
if (null === $cookie) {
|
||||
|
||||
/*
|
||||
* 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));
|
||||
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 {
|
||||
$params = session_get_cookie_params();
|
||||
unset($params['lifetime']);
|
||||
|
@@ -15,7 +15,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
* Memcached based session storage handler based on the Memcached class
|
||||
* provided by the PHP memcached extension.
|
||||
*
|
||||
* @see http://php.net/memcached
|
||||
* @see https://php.net/memcached
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
@@ -40,29 +40,27 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
||||
* * prefix: The prefix to use for the memcached keys in order to avoid collision
|
||||
* * expiretime: The time to live in seconds.
|
||||
*
|
||||
* @param \Memcached $memcached A \Memcached instance
|
||||
* @param array $options An associative array of Memcached options
|
||||
*
|
||||
* @throws \InvalidArgumentException When unsupported options are passed
|
||||
*/
|
||||
public function __construct(\Memcached $memcached, array $options = array())
|
||||
public function __construct(\Memcached $memcached, array $options = [])
|
||||
{
|
||||
$this->memcached = $memcached;
|
||||
|
||||
if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) {
|
||||
throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
|
||||
if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The following options are not supported "%s".', implode(', ', $diff)));
|
||||
}
|
||||
|
||||
$this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
|
||||
$this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
|
||||
$this->prefix = $options['prefix'] ?? 'sf2s';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
return $this->memcached->quit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,8 +72,9 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
$this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl);
|
||||
@@ -102,12 +101,13 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
// not required here because memcached will auto expire the records anyhow.
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -39,8 +39,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
$result = $this->currentHandler->close();
|
||||
@@ -50,8 +51,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
$result = $this->currentHandler->destroy($sessionId);
|
||||
@@ -61,8 +63,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$result = $this->currentHandler->gc($maxlifetime);
|
||||
@@ -72,8 +75,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$result = $this->currentHandler->open($savePath, $sessionName);
|
||||
@@ -83,8 +87,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($sessionId)
|
||||
{
|
||||
// No reading from new handler until switch-over
|
||||
@@ -92,8 +97,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function write($sessionId, $sessionData)
|
||||
{
|
||||
$result = $this->currentHandler->write($sessionId, $sessionData);
|
||||
@@ -103,8 +109,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
// No reading from new handler until switch-over
|
||||
@@ -112,8 +119,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $sessionData)
|
||||
{
|
||||
$result = $this->currentHandler->updateTimestamp($sessionId, $sessionData);
|
||||
|
@@ -17,7 +17,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||
*
|
||||
* @see https://packagist.org/packages/mongodb/mongodb
|
||||
* @see http://php.net/manual/en/set.mongodb.php
|
||||
* @see https://php.net/mongodb
|
||||
*/
|
||||
class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
@@ -51,40 +51,38 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
* A TTL collections can be used on MongoDB 2.2+ to cleanup expired sessions
|
||||
* automatically. Such an index can for example look like this:
|
||||
*
|
||||
* db.<session-collection>.ensureIndex(
|
||||
* db.<session-collection>.createIndex(
|
||||
* { "<expiry-field>": 1 },
|
||||
* { "expireAfterSeconds": 0 }
|
||||
* )
|
||||
*
|
||||
* More details on: http://docs.mongodb.org/manual/tutorial/expire-data/
|
||||
* More details on: https://docs.mongodb.org/manual/tutorial/expire-data/
|
||||
*
|
||||
* If you use such an index, you can drop `gc_probability` to 0 since
|
||||
* no garbage-collection is required.
|
||||
*
|
||||
* @param \MongoDB\Client $mongo A MongoDB\Client instance
|
||||
* @param array $options An associative array of field options
|
||||
*
|
||||
* @throws \InvalidArgumentException When "database" or "collection" not provided
|
||||
*/
|
||||
public function __construct(\MongoDB\Client $mongo, array $options)
|
||||
{
|
||||
if (!isset($options['database']) || !isset($options['collection'])) {
|
||||
throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler');
|
||||
throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler.');
|
||||
}
|
||||
|
||||
$this->mongo = $mongo;
|
||||
|
||||
$this->options = array_merge(array(
|
||||
$this->options = array_merge([
|
||||
'id_field' => '_id',
|
||||
'data_field' => 'data',
|
||||
'time_field' => 'time',
|
||||
'expiry_field' => 'expires_at',
|
||||
), $options);
|
||||
], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
@@ -95,23 +93,22 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
{
|
||||
$this->getCollection()->deleteOne(array(
|
||||
$this->getCollection()->deleteOne([
|
||||
$this->options['id_field'] => $sessionId,
|
||||
));
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$this->getCollection()->deleteMany(array(
|
||||
$this->options['expiry_field'] => array('$lt' => new \MongoDB\BSON\UTCDateTime()),
|
||||
));
|
||||
|
||||
return true;
|
||||
return $this->getCollection()->deleteMany([
|
||||
$this->options['expiry_field'] => ['$lt' => new \MongoDB\BSON\UTCDateTime()],
|
||||
])->getDeletedCount();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,36 +116,37 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) \ini_get('session.gc_maxlifetime')) * 1000);
|
||||
|
||||
$fields = array(
|
||||
$fields = [
|
||||
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
$this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY),
|
||||
);
|
||||
];
|
||||
|
||||
$this->getCollection()->updateOne(
|
||||
array($this->options['id_field'] => $sessionId),
|
||||
array('$set' => $fields),
|
||||
array('upsert' => true)
|
||||
[$this->options['id_field'] => $sessionId],
|
||||
['$set' => $fields],
|
||||
['upsert' => true]
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) \ini_get('session.gc_maxlifetime')) * 1000);
|
||||
|
||||
$this->getCollection()->updateOne(
|
||||
array($this->options['id_field'] => $sessionId),
|
||||
array('$set' => array(
|
||||
[$this->options['id_field'] => $sessionId],
|
||||
['$set' => [
|
||||
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
))
|
||||
]]
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -159,10 +157,10 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
*/
|
||||
protected function doRead($sessionId)
|
||||
{
|
||||
$dbData = $this->getCollection()->findOne(array(
|
||||
$dbData = $this->getCollection()->findOne([
|
||||
$this->options['id_field'] => $sessionId,
|
||||
$this->options['expiry_field'] => array('$gte' => new \MongoDB\BSON\UTCDateTime()),
|
||||
));
|
||||
$this->options['expiry_field'] => ['$gte' => new \MongoDB\BSON\UTCDateTime()],
|
||||
]);
|
||||
|
||||
if (null === $dbData) {
|
||||
return '';
|
||||
@@ -171,10 +169,7 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
return $dbData[$this->options['data_field']]->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \MongoDB\Collection
|
||||
*/
|
||||
private function getCollection()
|
||||
private function getCollection(): \MongoDB\Collection
|
||||
{
|
||||
if (null === $this->collection) {
|
||||
$this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
|
||||
|
@@ -23,7 +23,7 @@ class NativeFileSessionHandler extends \SessionHandler
|
||||
* Default null will leave setting as defined by PHP.
|
||||
* '/path', 'N;/path', or 'N;octal-mode;/path
|
||||
*
|
||||
* @see http://php.net/session.configuration.php#ini.session.save-path for further details.
|
||||
* @see https://php.net/session.configuration#ini.session.save-path for further details.
|
||||
*
|
||||
* @throws \InvalidArgumentException On invalid $savePath
|
||||
* @throws \RuntimeException When failing to create the save directory
|
||||
@@ -31,14 +31,14 @@ class NativeFileSessionHandler extends \SessionHandler
|
||||
public function __construct(string $savePath = null)
|
||||
{
|
||||
if (null === $savePath) {
|
||||
$savePath = ini_get('session.save_path');
|
||||
$savePath = \ini_get('session.save_path');
|
||||
}
|
||||
|
||||
$baseDir = $savePath;
|
||||
|
||||
if ($count = substr_count($savePath, ';')) {
|
||||
if ($count > 2) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath));
|
||||
throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'.', $savePath));
|
||||
}
|
||||
|
||||
// characters after last ';' are the path
|
||||
@@ -46,7 +46,7 @@ class NativeFileSessionHandler extends \SessionHandler
|
||||
}
|
||||
|
||||
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);
|
||||
|
@@ -19,16 +19,18 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
class NullSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
return true;
|
||||
@@ -43,8 +45,9 @@ class NullSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return true;
|
||||
@@ -67,10 +70,11 @@ class NullSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
* Saving it in a character column could corrupt the data. You can use createTable()
|
||||
* to initialize a correctly defined table.
|
||||
*
|
||||
* @see http://php.net/sessionhandlerinterface
|
||||
* @see https://php.net/sessionhandlerinterface
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Michael Williams <michael.williams@funsational.com>
|
||||
@@ -46,7 +46,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
* write will win in this case. It might be useful when you implement your own
|
||||
* logic to deal with this like an optimistic approach.
|
||||
*/
|
||||
const LOCK_NONE = 0;
|
||||
public const LOCK_NONE = 0;
|
||||
|
||||
/**
|
||||
* Creates an application-level lock on a session. The disadvantage is that the
|
||||
@@ -55,7 +55,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
* does not require a transaction.
|
||||
* This mode is not available for SQLite and not yet implemented for oci and sqlsrv.
|
||||
*/
|
||||
const LOCK_ADVISORY = 1;
|
||||
public const LOCK_ADVISORY = 1;
|
||||
|
||||
/**
|
||||
* Issues a real row lock. Since it uses a transaction between opening and
|
||||
@@ -63,7 +63,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
* that you also use for your application logic. This mode is the default because
|
||||
* it's the only reliable solution across DBMSs.
|
||||
*/
|
||||
const LOCK_TRANSACTIONAL = 2;
|
||||
public const LOCK_TRANSACTIONAL = 2;
|
||||
|
||||
private const MAX_LIFETIME = 315576000;
|
||||
|
||||
/**
|
||||
* @var \PDO|null PDO instance or null when not connected yet
|
||||
@@ -118,7 +120,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* @var array Connection options when lazy-connect
|
||||
*/
|
||||
private $connectionOptions = array();
|
||||
private $connectionOptions = [];
|
||||
|
||||
/**
|
||||
* @var int The strategy for locking, see constants
|
||||
@@ -130,7 +132,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
*
|
||||
* @var \PDOStatement[] An array of statements to release advisory locks
|
||||
*/
|
||||
private $unlockStatements = array();
|
||||
private $unlockStatements = [];
|
||||
|
||||
/**
|
||||
* @var bool True when the current session exists but expired according to session.gc_maxlifetime
|
||||
@@ -161,38 +163,37 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
* * db_time_col: The column where to store the timestamp [default: sess_time]
|
||||
* * db_username: The username when lazy-connect [default: '']
|
||||
* * db_password: The password when lazy-connect [default: '']
|
||||
* * db_connection_options: An array of driver-specific connection options [default: array()]
|
||||
* * db_connection_options: An array of driver-specific connection options [default: []]
|
||||
* * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL]
|
||||
*
|
||||
* @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null
|
||||
* @param array $options An associative array of options
|
||||
*
|
||||
* @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
|
||||
*/
|
||||
public function __construct($pdoOrDsn = null, array $options = array())
|
||||
public function __construct($pdoOrDsn = null, array $options = [])
|
||||
{
|
||||
if ($pdoOrDsn instanceof \PDO) {
|
||||
if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
|
||||
throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __CLASS__));
|
||||
}
|
||||
|
||||
$this->pdo = $pdoOrDsn;
|
||||
$this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
} elseif (\is_string($pdoOrDsn) && false !== strpos($pdoOrDsn, '://')) {
|
||||
} elseif (\is_string($pdoOrDsn) && str_contains($pdoOrDsn, '://')) {
|
||||
$this->dsn = $this->buildDsnFromUrl($pdoOrDsn);
|
||||
} else {
|
||||
$this->dsn = $pdoOrDsn;
|
||||
}
|
||||
|
||||
$this->table = isset($options['db_table']) ? $options['db_table'] : $this->table;
|
||||
$this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol;
|
||||
$this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol;
|
||||
$this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol;
|
||||
$this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol;
|
||||
$this->username = isset($options['db_username']) ? $options['db_username'] : $this->username;
|
||||
$this->password = isset($options['db_password']) ? $options['db_password'] : $this->password;
|
||||
$this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions;
|
||||
$this->lockMode = isset($options['lock_mode']) ? $options['lock_mode'] : $this->lockMode;
|
||||
$this->table = $options['db_table'] ?? $this->table;
|
||||
$this->idCol = $options['db_id_col'] ?? $this->idCol;
|
||||
$this->dataCol = $options['db_data_col'] ?? $this->dataCol;
|
||||
$this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol;
|
||||
$this->timeCol = $options['db_time_col'] ?? $this->timeCol;
|
||||
$this->username = $options['db_username'] ?? $this->username;
|
||||
$this->password = $options['db_password'] ?? $this->password;
|
||||
$this->connectionOptions = $options['db_connection_options'] ?? $this->connectionOptions;
|
||||
$this->lockMode = $options['lock_mode'] ?? $this->lockMode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +219,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
// - trailing space removal
|
||||
// - case-insensitivity
|
||||
// - language processing like é == e
|
||||
$sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB";
|
||||
$sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_bin, ENGINE = InnoDB";
|
||||
break;
|
||||
case 'sqlite':
|
||||
$sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
|
||||
@@ -238,6 +239,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
|
||||
try {
|
||||
$this->pdo->exec($sql);
|
||||
$this->pdo->exec("CREATE INDEX EXPIRY ON $this->table ($this->lifetimeCol)");
|
||||
} catch (\PDOException $e) {
|
||||
$this->rollback();
|
||||
|
||||
@@ -258,8 +260,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$this->sessionExpired = false;
|
||||
@@ -272,8 +275,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($sessionId)
|
||||
{
|
||||
try {
|
||||
@@ -286,15 +290,16 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
// We delay gc() to close() so that it is executed outside the transactional and blocking read-write process.
|
||||
// This way, pruning expired sessions does not block them from being started while the current session is used.
|
||||
$this->gcCalled = true;
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,7 +328,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
|
||||
$maxlifetime = (int) \ini_get('session.gc_maxlifetime');
|
||||
|
||||
try {
|
||||
// We use a single MERGE SQL query when supported by the database.
|
||||
@@ -348,7 +353,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
$insertStmt->execute();
|
||||
} catch (\PDOException $e) {
|
||||
// Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
|
||||
if (0 === strpos($e->getCode(), '23')) {
|
||||
if (str_starts_with($e->getCode(), '23')) {
|
||||
$updateStmt->execute();
|
||||
} else {
|
||||
throw $e;
|
||||
@@ -365,18 +370,19 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
|
||||
$expiry = time() + (int) \ini_get('session.gc_maxlifetime');
|
||||
|
||||
try {
|
||||
$updateStmt = $this->pdo->prepare(
|
||||
"UPDATE $this->table SET $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"
|
||||
"UPDATE $this->table SET $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id"
|
||||
);
|
||||
$updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
|
||||
$updateStmt->bindParam(':expiry', $expiry, \PDO::PARAM_INT);
|
||||
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
$updateStmt->execute();
|
||||
} catch (\PDOException $e) {
|
||||
@@ -389,8 +395,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
$this->commit();
|
||||
@@ -403,14 +410,21 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
$this->gcCalled = false;
|
||||
|
||||
// delete the session records that have expired
|
||||
if ('mysql' === $this->driver) {
|
||||
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time";
|
||||
} else {
|
||||
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time - $this->timeCol";
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time AND $this->lifetimeCol > :min";
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
// to be removed in 6.0
|
||||
if ('mysql' === $this->driver) {
|
||||
$legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol + $this->timeCol < :time";
|
||||
} else {
|
||||
$legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol < :time - $this->timeCol";
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare($legacySql);
|
||||
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
@@ -423,10 +437,8 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
|
||||
/**
|
||||
* Lazy-connects to the database.
|
||||
*
|
||||
* @param string $dsn DSN string
|
||||
*/
|
||||
private function connect($dsn)
|
||||
private function connect(string $dsn): void
|
||||
{
|
||||
$this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
@@ -436,13 +448,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* Builds a PDO DSN from a URL-like connection string.
|
||||
*
|
||||
* @param string $dsnOrUrl
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @todo implement missing support for oci DSN (which look totally different from other PDO ones)
|
||||
*/
|
||||
private function buildDsnFromUrl($dsnOrUrl)
|
||||
private function buildDsnFromUrl(string $dsnOrUrl): string
|
||||
{
|
||||
// (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
|
||||
$url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl);
|
||||
@@ -465,21 +473,21 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
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 = array(
|
||||
$driverAliasMap = [
|
||||
'mssql' => 'sqlsrv',
|
||||
'mysql2' => 'mysql', // Amazon RDS, for some weird reason
|
||||
'postgres' => 'pgsql',
|
||||
'postgresql' => 'pgsql',
|
||||
'sqlite3' => 'sqlite',
|
||||
);
|
||||
];
|
||||
|
||||
$driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme'];
|
||||
$driver = $driverAliasMap[$params['scheme']] ?? $params['scheme'];
|
||||
|
||||
// Doctrine DBAL supports passing its internal pdo_* driver names directly too (allowing both dashes and underscores). This allows supporting the same here.
|
||||
if (0 === strpos($driver, 'pdo_') || 0 === strpos($driver, 'pdo-')) {
|
||||
if (str_starts_with($driver, 'pdo_') || str_starts_with($driver, 'pdo-')) {
|
||||
$driver = substr($driver, 4);
|
||||
}
|
||||
|
||||
@@ -538,10 +546,10 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
* PDO::rollback or PDO::inTransaction for SQLite.
|
||||
*
|
||||
* Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions
|
||||
* due to http://www.mysqlperformanceblog.com/2013/12/12/one-more-innodb-gap-lock-to-avoid/ .
|
||||
* due to https://percona.com/blog/2013/12/12/one-more-innodb-gap-lock-to-avoid/ .
|
||||
* So we change it to READ COMMITTED.
|
||||
*/
|
||||
private function beginTransaction()
|
||||
private function beginTransaction(): void
|
||||
{
|
||||
if (!$this->inTransaction) {
|
||||
if ('sqlite' === $this->driver) {
|
||||
@@ -559,7 +567,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* Helper method to commit a transaction.
|
||||
*/
|
||||
private function commit()
|
||||
private function commit(): void
|
||||
{
|
||||
if ($this->inTransaction) {
|
||||
try {
|
||||
@@ -581,7 +589,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* Helper method to rollback a transaction.
|
||||
*/
|
||||
private function rollback()
|
||||
private function rollback(): void
|
||||
{
|
||||
// We only need to rollback if we are in a transaction. Otherwise the resulting
|
||||
// error would hide the real problem why rollback was called. We might not be
|
||||
@@ -618,12 +626,17 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
$selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$insertStmt = null;
|
||||
|
||||
do {
|
||||
while (true) {
|
||||
$selectStmt->execute();
|
||||
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
|
||||
|
||||
if ($sessionRows) {
|
||||
if ($sessionRows[0][1] + $sessionRows[0][2] < time()) {
|
||||
$expiry = (int) $sessionRows[0][1];
|
||||
if ($expiry <= self::MAX_LIFETIME) {
|
||||
$expiry += $sessionRows[0][2];
|
||||
}
|
||||
|
||||
if ($expiry < time()) {
|
||||
$this->sessionExpired = true;
|
||||
|
||||
return '';
|
||||
@@ -637,7 +650,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 (!filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN) && 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
|
||||
@@ -648,7 +661,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
} catch (\PDOException $e) {
|
||||
// Catch duplicate key error because other connection created the session already.
|
||||
// It would only not be the case when the other connection destroyed the session.
|
||||
if (0 === strpos($e->getCode(), '23')) {
|
||||
if (str_starts_with($e->getCode(), '23')) {
|
||||
// Retrieve finished session data written by concurrent connection by restarting the loop.
|
||||
// We have to start a new transaction as a failed query will mark the current transaction as
|
||||
// aborted in PostgreSQL and disallow further queries within it.
|
||||
@@ -662,7 +675,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
return '';
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -676,12 +689,12 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
* - for oci using DBMS_LOCK.REQUEST
|
||||
* - for sqlsrv using sp_getapplock with LockOwner = Session
|
||||
*/
|
||||
private function doAdvisoryLock(string $sessionId)
|
||||
private function doAdvisoryLock(string $sessionId): \PDOStatement
|
||||
{
|
||||
switch ($this->driver) {
|
||||
case 'mysql':
|
||||
// MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced.
|
||||
$lockId = \substr($sessionId, 0, 64);
|
||||
$lockId = substr($sessionId, 0, 64);
|
||||
// should we handle the return value? 0 on timeout, null on error
|
||||
// we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout
|
||||
$stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)');
|
||||
@@ -754,6 +767,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
if (self::LOCK_TRANSACTIONAL === $this->lockMode) {
|
||||
$this->beginTransaction();
|
||||
|
||||
// selecting the time column should be removed in 6.0
|
||||
switch ($this->driver) {
|
||||
case 'mysql':
|
||||
case 'oci':
|
||||
@@ -774,32 +788,26 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
|
||||
/**
|
||||
* Returns an insert statement supported by the database for writing session data.
|
||||
*
|
||||
* @param string $sessionId Session ID
|
||||
* @param string $sessionData Encoded session data
|
||||
* @param int $maxlifetime session.gc_maxlifetime
|
||||
*
|
||||
* @return \PDOStatement The insert statement
|
||||
*/
|
||||
private function getInsertStatement($sessionId, $sessionData, $maxlifetime)
|
||||
private function getInsertStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement
|
||||
{
|
||||
switch ($this->driver) {
|
||||
case 'oci':
|
||||
$data = fopen('php://memory', 'r+');
|
||||
fwrite($data, $sessionData);
|
||||
rewind($data);
|
||||
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :lifetime, :time) RETURNING $this->dataCol into :data";
|
||||
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :expiry, :time) RETURNING $this->dataCol into :data";
|
||||
break;
|
||||
default:
|
||||
$data = $sessionData;
|
||||
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
|
||||
$sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)";
|
||||
break;
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
|
||||
$stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
|
||||
return $stmt;
|
||||
@@ -807,32 +815,26 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
|
||||
/**
|
||||
* Returns an update statement supported by the database for writing session data.
|
||||
*
|
||||
* @param string $sessionId Session ID
|
||||
* @param string $sessionData Encoded session data
|
||||
* @param int $maxlifetime session.gc_maxlifetime
|
||||
*
|
||||
* @return \PDOStatement The update statement
|
||||
*/
|
||||
private function getUpdateStatement($sessionId, $sessionData, $maxlifetime)
|
||||
private function getUpdateStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement
|
||||
{
|
||||
switch ($this->driver) {
|
||||
case 'oci':
|
||||
$data = fopen('php://memory', 'r+');
|
||||
fwrite($data, $sessionData);
|
||||
rewind($data);
|
||||
$sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
|
||||
$sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
|
||||
break;
|
||||
default:
|
||||
$data = $sessionData;
|
||||
$sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id";
|
||||
$sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id";
|
||||
break;
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
|
||||
$stmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
|
||||
return $stmt;
|
||||
@@ -845,25 +847,25 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
switch (true) {
|
||||
case 'mysql' === $this->driver:
|
||||
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ".
|
||||
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
|
||||
break;
|
||||
case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='):
|
||||
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
|
||||
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
|
||||
// It also requires HOLDLOCK according to https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/
|
||||
$mergeSql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
|
||||
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
|
||||
"WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
|
||||
break;
|
||||
case 'sqlite' === $this->driver:
|
||||
$mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)";
|
||||
$mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)";
|
||||
break;
|
||||
case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
|
||||
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ".
|
||||
$mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ".
|
||||
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
|
||||
break;
|
||||
default:
|
||||
// MERGE is not supported with LOBs: http://www.oracle.com/technetwork/articles/fuecks-lobs-095315.html
|
||||
// MERGE is not supported with LOBs: https://oracle.com/technetwork/articles/fuecks-lobs-095315.html
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -873,15 +875,15 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
$mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
|
||||
$mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
|
||||
$mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
|
||||
$mergeStmt->bindParam(4, $maxlifetime, \PDO::PARAM_INT);
|
||||
$mergeStmt->bindValue(4, time() + $maxlifetime, \PDO::PARAM_INT);
|
||||
$mergeStmt->bindValue(5, time(), \PDO::PARAM_INT);
|
||||
$mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB);
|
||||
$mergeStmt->bindParam(7, $maxlifetime, \PDO::PARAM_INT);
|
||||
$mergeStmt->bindValue(7, time() + $maxlifetime, \PDO::PARAM_INT);
|
||||
$mergeStmt->bindValue(8, time(), \PDO::PARAM_INT);
|
||||
} else {
|
||||
$mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
|
||||
$mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT);
|
||||
$mergeStmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
|
||||
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
@@ -896,7 +898,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
protected function getConnection()
|
||||
{
|
||||
if (null === $this->pdo) {
|
||||
$this->connect($this->dsn ?: ini_get('session.save_path'));
|
||||
$this->connect($this->dsn ?: \ini_get('session.save_path'));
|
||||
}
|
||||
|
||||
return $this->pdo;
|
||||
|
@@ -30,34 +30,40 @@ class RedisSessionHandler extends AbstractSessionHandler
|
||||
*/
|
||||
private $prefix;
|
||||
|
||||
/**
|
||||
* @var int Time to live in seconds
|
||||
*/
|
||||
private $ttl;
|
||||
|
||||
/**
|
||||
* List of available options:
|
||||
* * prefix: The prefix to use for the keys in order to avoid collision on the Redis server.
|
||||
* * prefix: The prefix to use for the keys in order to avoid collision on the Redis server
|
||||
* * ttl: The time to live in seconds.
|
||||
*
|
||||
* @param \Redis|\RedisArray|\RedisCluster|\Predis\Client|RedisProxy $redis
|
||||
* @param array $options An associative array of options
|
||||
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis
|
||||
*
|
||||
* @throws \InvalidArgumentException When unsupported client or options are passed
|
||||
*/
|
||||
public function __construct($redis, array $options = array())
|
||||
public function __construct($redis, array $options = [])
|
||||
{
|
||||
if (
|
||||
!$redis instanceof \Redis &&
|
||||
!$redis instanceof \RedisArray &&
|
||||
!$redis instanceof \RedisCluster &&
|
||||
!$redis instanceof \Predis\Client &&
|
||||
!$redis instanceof \Predis\ClientInterface &&
|
||||
!$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)));
|
||||
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), array('prefix'))) {
|
||||
throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
|
||||
if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The following options are not supported "%s".', implode(', ', $diff)));
|
||||
}
|
||||
|
||||
$this->redis = $redis;
|
||||
$this->prefix = $options['prefix'] ?? 'sf_s';
|
||||
$this->ttl = $options['ttl'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +79,7 @@ class RedisSessionHandler extends AbstractSessionHandler
|
||||
*/
|
||||
protected function doWrite($sessionId, $data): bool
|
||||
{
|
||||
$result = $this->redis->setEx($this->prefix.$sessionId, (int) ini_get('session.gc_maxlifetime'), $data);
|
||||
$result = $this->redis->setEx($this->prefix.$sessionId, (int) ($this->ttl ?? \ini_get('session.gc_maxlifetime')), $data);
|
||||
|
||||
return $result && !$result instanceof ErrorInterface;
|
||||
}
|
||||
@@ -91,6 +97,7 @@ class RedisSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close(): bool
|
||||
{
|
||||
return true;
|
||||
@@ -98,17 +105,21 @@ class RedisSessionHandler extends AbstractSessionHandler
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public function gc($maxlifetime): bool
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return (bool) $this->redis->expire($this->prefix.$sessionId, (int) ini_get('session.gc_maxlifetime'));
|
||||
return (bool) $this->redis->expire($this->prefix.$sessionId, (int) ($this->ttl ?? \ini_get('session.gc_maxlifetime')));
|
||||
}
|
||||
}
|
||||
|
87
vendor/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php
vendored
Normal file
87
vendor/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class SessionHandlerFactory
|
||||
{
|
||||
/**
|
||||
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy|\Memcached|\PDO|string $connection Connection or DSN
|
||||
*/
|
||||
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)));
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $connection instanceof \Redis:
|
||||
case $connection instanceof \RedisArray:
|
||||
case $connection instanceof \RedisCluster:
|
||||
case $connection instanceof \Predis\ClientInterface:
|
||||
case $connection instanceof RedisProxy:
|
||||
case $connection instanceof RedisClusterProxy:
|
||||
return new RedisSessionHandler($connection);
|
||||
|
||||
case $connection instanceof \Memcached:
|
||||
return new MemcachedSessionHandler($connection);
|
||||
|
||||
case $connection instanceof \PDO:
|
||||
return new PdoSessionHandler($connection);
|
||||
|
||||
case !\is_string($connection):
|
||||
throw new \InvalidArgumentException(sprintf('Unsupported Connection: "%s".', \get_class($connection)));
|
||||
case str_starts_with($connection, 'file://'):
|
||||
$savePath = substr($connection, 7);
|
||||
|
||||
return new StrictSessionHandler(new NativeFileSessionHandler('' === $savePath ? null : $savePath));
|
||||
|
||||
case str_starts_with($connection, 'redis:'):
|
||||
case str_starts_with($connection, 'rediss:'):
|
||||
case str_starts_with($connection, 'memcached:'):
|
||||
if (!class_exists(AbstractAdapter::class)) {
|
||||
throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require symfony/cache".', $connection));
|
||||
}
|
||||
$handlerClass = str_starts_with($connection, 'memcached:') ? MemcachedSessionHandler::class : RedisSessionHandler::class;
|
||||
$connection = AbstractAdapter::createConnection($connection, ['lazy' => true]);
|
||||
|
||||
return new $handlerClass($connection);
|
||||
|
||||
case str_starts_with($connection, 'pdo_oci://'):
|
||||
if (!class_exists(DriverManager::class)) {
|
||||
throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require doctrine/dbal".', $connection));
|
||||
}
|
||||
$connection = DriverManager::getConnection(['url' => $connection])->getWrappedConnection();
|
||||
// no break;
|
||||
|
||||
case str_starts_with($connection, 'mssql://'):
|
||||
case str_starts_with($connection, 'mysql://'):
|
||||
case str_starts_with($connection, 'mysql2://'):
|
||||
case str_starts_with($connection, 'pgsql://'):
|
||||
case str_starts_with($connection, 'postgres://'):
|
||||
case str_starts_with($connection, 'postgresql://'):
|
||||
case str_starts_with($connection, 'sqlsrv://'):
|
||||
case str_starts_with($connection, 'sqlite://'):
|
||||
case str_starts_with($connection, 'sqlite3://'):
|
||||
return new PdoSessionHandler($connection);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Unsupported Connection: "%s".', $connection));
|
||||
}
|
||||
}
|
@@ -31,8 +31,19 @@ class StrictSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function isWrapper(): bool
|
||||
{
|
||||
return $this->handler instanceof \SessionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
parent::open($savePath, $sessionName);
|
||||
@@ -49,8 +60,9 @@ class StrictSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return $this->write($sessionId, $data);
|
||||
@@ -65,8 +77,9 @@ class StrictSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
$this->doDestroy = true;
|
||||
@@ -86,16 +99,18 @@ class StrictSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
return $this->handler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return $this->handler->gc($maxlifetime);
|
||||
|
@@ -22,9 +22,9 @@ use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
|
||||
*/
|
||||
class MetadataBag implements SessionBagInterface
|
||||
{
|
||||
const CREATED = 'c';
|
||||
const UPDATED = 'u';
|
||||
const LIFETIME = 'l';
|
||||
public const CREATED = 'c';
|
||||
public const UPDATED = 'u';
|
||||
public const LIFETIME = 'l';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@@ -39,7 +39,7 @@ class MetadataBag implements SessionBagInterface
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0);
|
||||
protected $meta = [self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0];
|
||||
|
||||
/**
|
||||
* Unix timestamp.
|
||||
@@ -159,10 +159,10 @@ class MetadataBag implements SessionBagInterface
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
private function stampCreated($lifetime = null)
|
||||
private function stampCreated(int $lifetime = null): void
|
||||
{
|
||||
$timeStamp = time();
|
||||
$this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp;
|
||||
$this->meta[self::LIFETIME] = (null === $lifetime) ? ini_get('session.cookie_lifetime') : $lifetime;
|
||||
$this->meta[self::LIFETIME] = $lifetime ?? (int) \ini_get('session.cookie_lifetime');
|
||||
}
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* @var MetadataBag
|
||||
@@ -60,7 +60,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* @var array|SessionBagInterface[]
|
||||
*/
|
||||
protected $bags = array();
|
||||
protected $bags = [];
|
||||
|
||||
public function __construct(string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
|
||||
{
|
||||
@@ -148,7 +148,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
public function save()
|
||||
{
|
||||
if (!$this->started || $this->closed) {
|
||||
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
|
||||
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed.');
|
||||
}
|
||||
// nothing to do since we don't persist the session data
|
||||
$this->closed = false;
|
||||
@@ -166,7 +166,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
}
|
||||
|
||||
// clear out the session
|
||||
$this->data = array();
|
||||
$this->data = [];
|
||||
|
||||
// reconnect the bags to the session
|
||||
$this->loadSession();
|
||||
@@ -186,7 +186,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
public function getBag($name)
|
||||
{
|
||||
if (!isset($this->bags[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
|
||||
throw new \InvalidArgumentException(sprintf('The SessionBagInterface "%s" is not registered.', $name));
|
||||
}
|
||||
|
||||
if (!$this->started) {
|
||||
@@ -238,11 +238,11 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
|
||||
protected function loadSession()
|
||||
{
|
||||
$bags = array_merge($this->bags, array($this->metadataBag));
|
||||
$bags = array_merge($this->bags, [$this->metadataBag]);
|
||||
|
||||
foreach ($bags as $bag) {
|
||||
$key = $bag->getStorageKey();
|
||||
$this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array();
|
||||
$this->data[$key] = $this->data[$key] ?? [];
|
||||
$bag->initialize($this->data[$key]);
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,8 @@ namespace Symfony\Component\HttpFoundation\Session\Storage;
|
||||
|
||||
/**
|
||||
* MockFileSessionStorage is used to mock sessions for
|
||||
* functional testing when done in a single PHP process.
|
||||
* functional testing where you may need to persist session data
|
||||
* across separate PHP processes.
|
||||
*
|
||||
* No PHP session is actually started since a session can be initialized
|
||||
* and shutdown only once per PHP execution cycle and this class does
|
||||
@@ -27,9 +28,8 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
private $savePath;
|
||||
|
||||
/**
|
||||
* @param string $savePath Path of directory to save session files
|
||||
* @param string $name Session name
|
||||
* @param MetadataBag $metaBag MetadataBag instance
|
||||
* @param string $savePath Path of directory to save session files
|
||||
* @param string $name Session name
|
||||
*/
|
||||
public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
|
||||
{
|
||||
@@ -38,7 +38,7 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
}
|
||||
|
||||
if (!is_dir($savePath) && !@mkdir($savePath, 0777, true) && !is_dir($savePath)) {
|
||||
throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $savePath));
|
||||
throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s".', $savePath));
|
||||
}
|
||||
|
||||
$this->savePath = $savePath;
|
||||
@@ -88,7 +88,7 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
public function save()
|
||||
{
|
||||
if (!$this->started) {
|
||||
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
|
||||
throw new \RuntimeException('Trying to save a session that was not started yet or was already closed.');
|
||||
}
|
||||
|
||||
$data = $this->data;
|
||||
@@ -98,13 +98,16 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($data)) {
|
||||
if ([$key = $this->metadataBag->getStorageKey()] === array_keys($data)) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
|
||||
try {
|
||||
if ($data) {
|
||||
file_put_contents($this->getFilePath(), serialize($data));
|
||||
$path = $this->getFilePath();
|
||||
$tmp = $path.bin2hex(random_bytes(6));
|
||||
file_put_contents($tmp, serialize($data));
|
||||
rename($tmp, $path);
|
||||
} else {
|
||||
$this->destroy();
|
||||
}
|
||||
@@ -112,9 +115,8 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
// this is needed for Silex, where the session object is re-used across requests
|
||||
// in functional tests. In Symfony, the container is rebooted, so we don't have
|
||||
// this issue
|
||||
// this is needed when the session object is re-used across multiple requests
|
||||
// in functional tests.
|
||||
$this->started = false;
|
||||
}
|
||||
|
||||
@@ -122,19 +124,20 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
* Deletes a session from persistent storage.
|
||||
* Deliberately leaves session data in memory intact.
|
||||
*/
|
||||
private function destroy()
|
||||
private function destroy(): void
|
||||
{
|
||||
if (is_file($this->getFilePath())) {
|
||||
set_error_handler(static function () {});
|
||||
try {
|
||||
unlink($this->getFilePath());
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate path to file.
|
||||
*
|
||||
* @return string File path
|
||||
*/
|
||||
private function getFilePath()
|
||||
private function getFilePath(): string
|
||||
{
|
||||
return $this->savePath.'/'.$this->id.'.mocksess';
|
||||
}
|
||||
@@ -142,10 +145,16 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
/**
|
||||
* Reads session from storage and loads session.
|
||||
*/
|
||||
private function read()
|
||||
private function read(): void
|
||||
{
|
||||
$filePath = $this->getFilePath();
|
||||
$this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array();
|
||||
set_error_handler(static function () {});
|
||||
try {
|
||||
$data = file_get_contents($this->getFilePath());
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
$this->data = $data ? unserialize($data) : [];
|
||||
|
||||
$this->loadSession();
|
||||
}
|
||||
|
@@ -17,6 +17,11 @@ use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandle
|
||||
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.
|
||||
*
|
||||
@@ -27,7 +32,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* @var SessionBagInterface[]
|
||||
*/
|
||||
protected $bags = array();
|
||||
protected $bags = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
@@ -60,7 +65,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
*
|
||||
* List of options for $options array with their defaults.
|
||||
*
|
||||
* @see http://php.net/session.configuration for options
|
||||
* @see https://php.net/session.configuration for options
|
||||
* but we omit 'session.' from the beginning of the keys for convenience.
|
||||
*
|
||||
* ("auto_start", is not supported as it tells PHP to start a session before
|
||||
@@ -81,7 +86,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
* name, "PHPSESSID"
|
||||
* referer_check, ""
|
||||
* serialize_handler, "php"
|
||||
* use_strict_mode, "0"
|
||||
* use_strict_mode, "1"
|
||||
* use_cookies, "1"
|
||||
* use_only_cookies, "1"
|
||||
* use_trans_sid, "0"
|
||||
@@ -97,19 +102,21 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
* trans_sid_hosts, $_SERVER['HTTP_HOST']
|
||||
* trans_sid_tags, "a=href,area=href,frame=src,form="
|
||||
*
|
||||
* @param array $options Session configuration options
|
||||
* @param \SessionHandlerInterface|null $handler
|
||||
* @param MetadataBag $metaBag MetadataBag
|
||||
* @param AbstractProxy|\SessionHandlerInterface|null $handler
|
||||
*/
|
||||
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
|
||||
public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null)
|
||||
{
|
||||
$options += array(
|
||||
if (!\extension_loaded('session')) {
|
||||
throw new \LogicException('PHP extension "session" is required.');
|
||||
}
|
||||
|
||||
$options += [
|
||||
'cache_limiter' => '',
|
||||
'cache_expire' => 0,
|
||||
'use_cookies' => 1,
|
||||
'lazy_write' => 1,
|
||||
'use_strict_mode' => 1,
|
||||
);
|
||||
];
|
||||
|
||||
session_register_shutdown();
|
||||
|
||||
@@ -141,19 +148,55 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
throw new \RuntimeException('Failed to start the session: already started by PHP.');
|
||||
}
|
||||
|
||||
if (filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN) && 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));
|
||||
}
|
||||
|
||||
$sessionId = $_COOKIE[session_name()] ?? null;
|
||||
/*
|
||||
* Explanation of the session ID regular expression: `/^[a-zA-Z0-9,-]{22,250}$/`.
|
||||
*
|
||||
* ---------- Part 1
|
||||
*
|
||||
* The part `[a-zA-Z0-9,-]` is related to the PHP ini directive `session.sid_bits_per_character` defined as 6.
|
||||
* See https://www.php.net/manual/en/session.configuration.php#ini.session.sid-bits-per-character.
|
||||
* Allowed values are integers such as:
|
||||
* - 4 for range `a-f0-9`
|
||||
* - 5 for range `a-v0-9`
|
||||
* - 6 for range `a-zA-Z0-9,-`
|
||||
*
|
||||
* ---------- Part 2
|
||||
*
|
||||
* The part `{22,250}` is related to the PHP ini directive `session.sid_length`.
|
||||
* See https://www.php.net/manual/en/session.configuration.php#ini.session.sid-length.
|
||||
* Allowed values are integers between 22 and 256, but we use 250 for the max.
|
||||
*
|
||||
* Where does the 250 come from?
|
||||
* - The length of Windows and Linux filenames is limited to 255 bytes. Then the max must not exceed 255.
|
||||
* - The session filename prefix is `sess_`, a 5 bytes string. Then the max must not exceed 255 - 5 = 250.
|
||||
*
|
||||
* ---------- Conclusion
|
||||
*
|
||||
* The parts 1 and 2 prevent the warning below:
|
||||
* `PHP Warning: SessionHandler::read(): Session ID is too long or contains illegal characters. Only the A-Z, a-z, 0-9, "-", and "," characters are allowed.`
|
||||
*
|
||||
* The part 2 prevents the warning below:
|
||||
* `PHP Warning: SessionHandler::read(): open(filepath, O_RDWR) failed: No such file or directory (2).`
|
||||
*/
|
||||
if ($sessionId && $this->saveHandler instanceof AbstractProxy && 'files' === $this->saveHandler->getSaveHandlerName() && !preg_match('/^[a-zA-Z0-9,-]{22,250}$/', $sessionId)) {
|
||||
// the session ID in the header is invalid, create a new one
|
||||
session_id(session_create_id());
|
||||
}
|
||||
|
||||
// ok to try and start the session
|
||||
if (!session_start()) {
|
||||
throw new \RuntimeException('Failed to start the session');
|
||||
throw new \RuntimeException('Failed to start the session.');
|
||||
}
|
||||
|
||||
if (null !== $this->emulateSameSite) {
|
||||
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
|
||||
if (null !== $originalCookie) {
|
||||
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite));
|
||||
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,8 +251,10 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null !== $lifetime) {
|
||||
if (null !== $lifetime && $lifetime != \ini_get('session.cookie_lifetime')) {
|
||||
$this->save();
|
||||
ini_set('session.cookie_lifetime', $lifetime);
|
||||
$this->start();
|
||||
}
|
||||
|
||||
if ($destroy) {
|
||||
@@ -218,14 +263,10 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
|
||||
$isRegenerated = session_regenerate_id($destroy);
|
||||
|
||||
// The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
|
||||
// @see https://bugs.php.net/bug.php?id=70013
|
||||
$this->loadSession();
|
||||
|
||||
if (null !== $this->emulateSameSite) {
|
||||
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
|
||||
if (null !== $originalCookie) {
|
||||
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite));
|
||||
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +278,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
// Store a copy so we can restore the bags in case the session was not left empty
|
||||
$session = $_SESSION;
|
||||
|
||||
foreach ($this->bags as $bag) {
|
||||
@@ -244,13 +286,13 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
}
|
||||
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) {
|
||||
if ($_SESSION && [$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) {
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
|
||||
// Register error handler to add information about the current save handler
|
||||
$previousHandler = set_error_handler(function ($type, $msg, $file, $line) use (&$previousHandler) {
|
||||
if (E_WARNING === $type && 0 === strpos($msg, 'session_write_close():')) {
|
||||
if (\E_WARNING === $type && str_starts_with($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));
|
||||
}
|
||||
@@ -262,7 +304,11 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
session_write_close();
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
$_SESSION = $session;
|
||||
|
||||
// Restore only if not empty
|
||||
if ($_SESSION) {
|
||||
$_SESSION = $session;
|
||||
}
|
||||
}
|
||||
|
||||
$this->closed = true;
|
||||
@@ -280,7 +326,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
}
|
||||
|
||||
// clear out the session
|
||||
$_SESSION = array();
|
||||
$_SESSION = [];
|
||||
|
||||
// reconnect the bags to the session
|
||||
$this->loadSession();
|
||||
@@ -304,7 +350,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
public function getBag($name)
|
||||
{
|
||||
if (!isset($this->bags[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
|
||||
throw new \InvalidArgumentException(sprintf('The SessionBagInterface "%s" is not registered.', $name));
|
||||
}
|
||||
|
||||
if (!$this->started && $this->saveHandler->isActive()) {
|
||||
@@ -349,9 +395,9 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
* For convenience we omit 'session.' from the beginning of the keys.
|
||||
* Explicitly ignores other ini keys.
|
||||
*
|
||||
* @param array $options Session ini directives array(key => value)
|
||||
* @param array $options Session ini directives [key => value]
|
||||
*
|
||||
* @see http://php.net/session.configuration
|
||||
* @see https://php.net/session.configuration
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
@@ -359,7 +405,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
return;
|
||||
}
|
||||
|
||||
$validOptions = array_flip(array(
|
||||
$validOptions = array_flip([
|
||||
'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
|
||||
'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite',
|
||||
'gc_divisor', 'gc_maxlifetime', 'gc_probability',
|
||||
@@ -369,7 +415,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
|
||||
'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags',
|
||||
'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
|
||||
));
|
||||
]);
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
if (isset($validOptions[$key])) {
|
||||
@@ -379,6 +425,9 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
$this->emulateSameSite = $value;
|
||||
continue;
|
||||
}
|
||||
if ('cookie_secure' === $key && 'auto' === $value) {
|
||||
continue;
|
||||
}
|
||||
ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
|
||||
}
|
||||
}
|
||||
@@ -394,15 +443,13 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
* ini_set('session.save_path', '/tmp');
|
||||
*
|
||||
* or pass in a \SessionHandler instance which configures session.save_handler in the
|
||||
* constructor, for a template see NativeFileSessionHandler or use handlers in
|
||||
* composer package drak/native-session
|
||||
* constructor, for a template see NativeFileSessionHandler.
|
||||
*
|
||||
* @see http://php.net/session-set-save-handler
|
||||
* @see http://php.net/sessionhandlerinterface
|
||||
* @see http://php.net/sessionhandler
|
||||
* @see http://github.com/drak/NativeSession
|
||||
* @see https://php.net/session-set-save-handler
|
||||
* @see https://php.net/sessionhandlerinterface
|
||||
* @see https://php.net/sessionhandler
|
||||
*
|
||||
* @param \SessionHandlerInterface|null $saveHandler
|
||||
* @param AbstractProxy|\SessionHandlerInterface|null $saveHandler
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
@@ -445,11 +492,11 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
$session = &$_SESSION;
|
||||
}
|
||||
|
||||
$bags = array_merge($this->bags, array($this->metadataBag));
|
||||
$bags = array_merge($this->bags, [$this->metadataBag]);
|
||||
|
||||
foreach ($bags as $bag) {
|
||||
$key = $bag->getStorageKey();
|
||||
$session[$key] = isset($session[$key]) ? $session[$key] : array();
|
||||
$session[$key] = isset($session[$key]) && \is_array($session[$key]) ? $session[$key] : [];
|
||||
$bag->initialize($session[$key]);
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
|
||||
|
||||
/**
|
||||
* Allows session to be started by PHP and managed by Symfony.
|
||||
*
|
||||
@@ -19,11 +21,14 @@ namespace Symfony\Component\HttpFoundation\Session\Storage;
|
||||
class PhpBridgeSessionStorage extends NativeSessionStorage
|
||||
{
|
||||
/**
|
||||
* @param \SessionHandlerInterface|null $handler
|
||||
* @param MetadataBag $metaBag MetadataBag
|
||||
* @param AbstractProxy|\SessionHandlerInterface|null $handler
|
||||
*/
|
||||
public function __construct($handler = null, MetadataBag $metaBag = null)
|
||||
{
|
||||
if (!\extension_loaded('session')) {
|
||||
throw new \LogicException('PHP extension "session" is required.');
|
||||
}
|
||||
|
||||
$this->setMetadataBag($metaBag);
|
||||
$this->setSaveHandler($handler);
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ abstract class AbstractProxy
|
||||
/**
|
||||
* Gets the session.save_handler name.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSaveHandlerName()
|
||||
{
|
||||
@@ -88,7 +88,7 @@ abstract class AbstractProxy
|
||||
public function setId($id)
|
||||
{
|
||||
if ($this->isActive()) {
|
||||
throw new \LogicException('Cannot change the ID of an active session');
|
||||
throw new \LogicException('Cannot change the ID of an active session.');
|
||||
}
|
||||
|
||||
session_id($id);
|
||||
@@ -114,7 +114,7 @@ abstract class AbstractProxy
|
||||
public function setName($name)
|
||||
{
|
||||
if ($this->isActive()) {
|
||||
throw new \LogicException('Cannot change the name of an active session');
|
||||
throw new \LogicException('Cannot change the name of an active session.');
|
||||
}
|
||||
|
||||
session_name($name);
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
|
||||
|
||||
/**
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
@@ -21,8 +23,8 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
||||
public function __construct(\SessionHandlerInterface $handler)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
$this->wrapper = ($handler instanceof \SessionHandler);
|
||||
$this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
|
||||
$this->wrapper = $handler instanceof \SessionHandler;
|
||||
$this->saveHandlerName = $this->wrapper || ($handler instanceof StrictSessionHandler && $handler->isWrapper()) ? \ini_get('session.save_handler') : 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,64 +38,72 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
||||
// \SessionHandlerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return (bool) $this->handler->open($savePath, $sessionName);
|
||||
return $this->handler->open($savePath, $sessionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
return (bool) $this->handler->close();
|
||||
return $this->handler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($sessionId)
|
||||
{
|
||||
return (string) $this->handler->read($sessionId);
|
||||
return $this->handler->read($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
return (bool) $this->handler->write($sessionId, $data);
|
||||
return $this->handler->write($sessionId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return (bool) $this->handler->destroy($sessionId);
|
||||
return $this->handler->destroy($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return (bool) $this->handler->gc($maxlifetime);
|
||||
return $this->handler->gc($maxlifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
return !$this->handler instanceof \SessionUpdateTimestampHandlerInterface || $this->handler->validateId($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return $this->handler instanceof \SessionUpdateTimestampHandlerInterface ? $this->handler->updateTimestamp($sessionId, $data) : $this->write($sessionId, $data);
|
||||
|
@@ -77,7 +77,7 @@ interface SessionStorageInterface
|
||||
* only delete the session data from persistent storage.
|
||||
*
|
||||
* Care: When regenerating the session ID no locking is involved in PHP's
|
||||
* session design. See https://bugs.php.net/bug.php?id=61470 for a discussion.
|
||||
* session design. See https://bugs.php.net/61470 for a discussion.
|
||||
* So you must make sure the regenerated session is saved BEFORE sending the
|
||||
* headers with the new ID. Symfony's HttpKernel offers a listener for this.
|
||||
* See Symfony\Component\HttpKernel\EventListener\SaveSessionListener.
|
||||
|
@@ -35,7 +35,7 @@ class StreamedResponse extends Response
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*/
|
||||
public function __construct(callable $callback = null, int $status = 200, array $headers = array())
|
||||
public function __construct(callable $callback = null, int $status = 200, array $headers = [])
|
||||
{
|
||||
parent::__construct(null, $status, $headers);
|
||||
|
||||
@@ -55,7 +55,7 @@ class StreamedResponse extends Response
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($callback = null, $status = 200, $headers = array())
|
||||
public static function create($callback = null, $status = 200, $headers = [])
|
||||
{
|
||||
return new static($callback, $status, $headers);
|
||||
}
|
||||
@@ -63,8 +63,6 @@ class StreamedResponse extends Response
|
||||
/**
|
||||
* Sets the PHP callback associated with this Response.
|
||||
*
|
||||
* @param callable $callback A valid PHP callback
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCallback(callable $callback)
|
||||
@@ -111,7 +109,7 @@ class StreamedResponse extends Response
|
||||
throw new \LogicException('The Response callback must not be null.');
|
||||
}
|
||||
|
||||
\call_user_func($this->callback);
|
||||
($this->callback)();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -136,8 +134,6 @@ class StreamedResponse extends Response
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
|
55
vendor/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php
vendored
Normal file
55
vendor/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class RequestAttributeValueSame extends Constraint
|
||||
{
|
||||
private $name;
|
||||
private $value;
|
||||
|
||||
public function __construct(string $name, string $value)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return sprintf('has attribute "%s" with value "%s"', $this->name, $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($request): bool
|
||||
{
|
||||
return $this->value === $request->attributes->get($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($request): string
|
||||
{
|
||||
return 'the Request '.$this->toString();
|
||||
}
|
||||
}
|
85
vendor/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php
vendored
Normal file
85
vendor/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class ResponseCookieValueSame extends Constraint
|
||||
{
|
||||
private $name;
|
||||
private $value;
|
||||
private $path;
|
||||
private $domain;
|
||||
|
||||
public function __construct(string $name, string $value, string $path = '/', string $domain = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
$this->path = $path;
|
||||
$this->domain = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
$str = sprintf('has cookie "%s"', $this->name);
|
||||
if ('/' !== $this->path) {
|
||||
$str .= sprintf(' with path "%s"', $this->path);
|
||||
}
|
||||
if ($this->domain) {
|
||||
$str .= sprintf(' for domain "%s"', $this->domain);
|
||||
}
|
||||
$str .= sprintf(' with value "%s"', $this->value);
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
$cookie = $this->getCookie($response);
|
||||
if (!$cookie) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->value === (string) $cookie->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($response): string
|
||||
{
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
|
||||
protected function getCookie(Response $response): ?Cookie
|
||||
{
|
||||
$cookies = $response->headers->getCookies();
|
||||
|
||||
$filteredCookies = array_filter($cookies, function (Cookie $cookie) {
|
||||
return $cookie->getName() === $this->name && $cookie->getPath() === $this->path && $cookie->getDomain() === $this->domain;
|
||||
});
|
||||
|
||||
return reset($filteredCookies) ?: null;
|
||||
}
|
||||
}
|
77
vendor/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php
vendored
Normal file
77
vendor/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class ResponseHasCookie extends Constraint
|
||||
{
|
||||
private $name;
|
||||
private $path;
|
||||
private $domain;
|
||||
|
||||
public function __construct(string $name, string $path = '/', string $domain = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->path = $path;
|
||||
$this->domain = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
$str = sprintf('has cookie "%s"', $this->name);
|
||||
if ('/' !== $this->path) {
|
||||
$str .= sprintf(' with path "%s"', $this->path);
|
||||
}
|
||||
if ($this->domain) {
|
||||
$str .= sprintf(' for domain "%s"', $this->domain);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
return null !== $this->getCookie($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($response): string
|
||||
{
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
|
||||
private function getCookie(Response $response): ?Cookie
|
||||
{
|
||||
$cookies = $response->headers->getCookies();
|
||||
|
||||
$filteredCookies = array_filter($cookies, function (Cookie $cookie) {
|
||||
return $cookie->getName() === $this->name && $cookie->getPath() === $this->path && $cookie->getDomain() === $this->domain;
|
||||
});
|
||||
|
||||
return reset($filteredCookies) ?: null;
|
||||
}
|
||||
}
|
53
vendor/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php
vendored
Normal file
53
vendor/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class ResponseHasHeader extends Constraint
|
||||
{
|
||||
private $headerName;
|
||||
|
||||
public function __construct(string $headerName)
|
||||
{
|
||||
$this->headerName = $headerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return sprintf('has header "%s"', $this->headerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
return $response->headers->has($this->headerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($response): string
|
||||
{
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
}
|
55
vendor/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php
vendored
Normal file
55
vendor/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class ResponseHeaderSame extends Constraint
|
||||
{
|
||||
private $headerName;
|
||||
private $expectedValue;
|
||||
|
||||
public function __construct(string $headerName, string $expectedValue)
|
||||
{
|
||||
$this->headerName = $headerName;
|
||||
$this->expectedValue = $expectedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return sprintf('has header "%s" with value "%s"', $this->headerName, $this->expectedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
return $this->expectedValue === $response->headers->get($this->headerName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($response): string
|
||||
{
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
}
|
56
vendor/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php
vendored
Normal file
56
vendor/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class ResponseIsRedirected extends Constraint
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return 'is redirected';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
return $response->isRedirect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($response): string
|
||||
{
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function additionalFailureDescription($response): string
|
||||
{
|
||||
return (string) $response;
|
||||
}
|
||||
}
|
56
vendor/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php
vendored
Normal file
56
vendor/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class ResponseIsSuccessful extends Constraint
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return 'is successful';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
return $response->isSuccessful();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($response): string
|
||||
{
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function additionalFailureDescription($response): string
|
||||
{
|
||||
return (string) $response;
|
||||
}
|
||||
}
|
63
vendor/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php
vendored
Normal file
63
vendor/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class ResponseStatusCodeSame extends Constraint
|
||||
{
|
||||
private $statusCode;
|
||||
|
||||
public function __construct(int $statusCode)
|
||||
{
|
||||
$this->statusCode = $statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return 'status code is '.$this->statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
return $this->statusCode === $response->getStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($response): string
|
||||
{
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function additionalFailureDescription($response): string
|
||||
{
|
||||
return (string) $response;
|
||||
}
|
||||
}
|
@@ -1,113 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
|
||||
|
||||
class AcceptHeaderItemTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideFromStringData
|
||||
*/
|
||||
public function testFromString($string, $value, array $attributes)
|
||||
{
|
||||
$item = AcceptHeaderItem::fromString($string);
|
||||
$this->assertEquals($value, $item->getValue());
|
||||
$this->assertEquals($attributes, $item->getAttributes());
|
||||
}
|
||||
|
||||
public function provideFromStringData()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'text/html',
|
||||
'text/html', array(),
|
||||
),
|
||||
array(
|
||||
'"this;should,not=matter"',
|
||||
'this;should,not=matter', array(),
|
||||
),
|
||||
array(
|
||||
"text/plain; charset=utf-8;param=\"this;should,not=matter\";\tfootnotes=true",
|
||||
'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'),
|
||||
),
|
||||
array(
|
||||
'"this;should,not=matter";charset=utf-8',
|
||||
'this;should,not=matter', array('charset' => 'utf-8'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideToStringData
|
||||
*/
|
||||
public function testToString($value, array $attributes, $string)
|
||||
{
|
||||
$item = new AcceptHeaderItem($value, $attributes);
|
||||
$this->assertEquals($string, (string) $item);
|
||||
}
|
||||
|
||||
public function provideToStringData()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'text/html', array(),
|
||||
'text/html',
|
||||
),
|
||||
array(
|
||||
'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'),
|
||||
'text/plain; charset=utf-8; param="this;should,not=matter"; footnotes=true',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testValue()
|
||||
{
|
||||
$item = new AcceptHeaderItem('value', array());
|
||||
$this->assertEquals('value', $item->getValue());
|
||||
|
||||
$item->setValue('new value');
|
||||
$this->assertEquals('new value', $item->getValue());
|
||||
|
||||
$item->setValue(1);
|
||||
$this->assertEquals('1', $item->getValue());
|
||||
}
|
||||
|
||||
public function testQuality()
|
||||
{
|
||||
$item = new AcceptHeaderItem('value', array());
|
||||
$this->assertEquals(1.0, $item->getQuality());
|
||||
|
||||
$item->setQuality(0.5);
|
||||
$this->assertEquals(0.5, $item->getQuality());
|
||||
|
||||
$item->setAttribute('q', 0.75);
|
||||
$this->assertEquals(0.75, $item->getQuality());
|
||||
$this->assertFalse($item->hasAttribute('q'));
|
||||
}
|
||||
|
||||
public function testAttribute()
|
||||
{
|
||||
$item = new AcceptHeaderItem('value', array());
|
||||
$this->assertEquals(array(), $item->getAttributes());
|
||||
$this->assertFalse($item->hasAttribute('test'));
|
||||
$this->assertNull($item->getAttribute('test'));
|
||||
$this->assertEquals('default', $item->getAttribute('test', 'default'));
|
||||
|
||||
$item->setAttribute('test', 'value');
|
||||
$this->assertEquals(array('test' => 'value'), $item->getAttributes());
|
||||
$this->assertTrue($item->hasAttribute('test'));
|
||||
$this->assertEquals('value', $item->getAttribute('test'));
|
||||
$this->assertEquals('value', $item->getAttribute('test', 'default'));
|
||||
}
|
||||
}
|
@@ -1,130 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\AcceptHeader;
|
||||
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
|
||||
|
||||
class AcceptHeaderTest extends TestCase
|
||||
{
|
||||
public function testFirst()
|
||||
{
|
||||
$header = AcceptHeader::fromString('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c');
|
||||
$this->assertSame('text/html', $header->first()->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFromStringData
|
||||
*/
|
||||
public function testFromString($string, array $items)
|
||||
{
|
||||
$header = AcceptHeader::fromString($string);
|
||||
$parsed = array_values($header->all());
|
||||
// reset index since the fixtures don't have them set
|
||||
foreach ($parsed as $item) {
|
||||
$item->setIndex(0);
|
||||
}
|
||||
$this->assertEquals($items, $parsed);
|
||||
}
|
||||
|
||||
public function provideFromStringData()
|
||||
{
|
||||
return array(
|
||||
array('', array()),
|
||||
array('gzip', array(new AcceptHeaderItem('gzip'))),
|
||||
array('gzip,deflate,sdch', array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))),
|
||||
array("gzip, deflate\t,sdch", array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))),
|
||||
array('"this;should,not=matter"', array(new AcceptHeaderItem('this;should,not=matter'))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideToStringData
|
||||
*/
|
||||
public function testToString(array $items, $string)
|
||||
{
|
||||
$header = new AcceptHeader($items);
|
||||
$this->assertEquals($string, (string) $header);
|
||||
}
|
||||
|
||||
public function provideToStringData()
|
||||
{
|
||||
return array(
|
||||
array(array(), ''),
|
||||
array(array(new AcceptHeaderItem('gzip')), 'gzip'),
|
||||
array(array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')), 'gzip,deflate,sdch'),
|
||||
array(array(new AcceptHeaderItem('this;should,not=matter')), 'this;should,not=matter'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFilterData
|
||||
*/
|
||||
public function testFilter($string, $filter, array $values)
|
||||
{
|
||||
$header = AcceptHeader::fromString($string)->filter($filter);
|
||||
$this->assertEquals($values, array_keys($header->all()));
|
||||
}
|
||||
|
||||
public function provideFilterData()
|
||||
{
|
||||
return array(
|
||||
array('fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', array('fr-FR', 'fr')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideSortingData
|
||||
*/
|
||||
public function testSorting($string, array $values)
|
||||
{
|
||||
$header = AcceptHeader::fromString($string);
|
||||
$this->assertEquals($values, array_keys($header->all()));
|
||||
}
|
||||
|
||||
public function provideSortingData()
|
||||
{
|
||||
return array(
|
||||
'quality has priority' => array('*;q=0.3,ISO-8859-1,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')),
|
||||
'order matters when q is equal' => array('*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')),
|
||||
'order matters when q is equal2' => array('*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', array('utf-8', 'ISO-8859-1', '*')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDefaultValueData
|
||||
*/
|
||||
public function testDefaultValue($acceptHeader, $value, $expectedQuality)
|
||||
{
|
||||
$header = AcceptHeader::fromString($acceptHeader);
|
||||
$this->assertSame($expectedQuality, $header->get($value)->getQuality());
|
||||
}
|
||||
|
||||
public function provideDefaultValueData()
|
||||
{
|
||||
yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, *;q=0.3', 'text/xml', 0.3);
|
||||
yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/xml', 0.3);
|
||||
yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/html', 1.0);
|
||||
yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/plain', 0.5);
|
||||
yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', '*', 0.3);
|
||||
yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', '*', 1.0);
|
||||
yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', 'text/xml', 1.0);
|
||||
yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', 'text/*', 1.0);
|
||||
yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/*', 0.8);
|
||||
yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/html', 1.0);
|
||||
yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/x-dvi', 0.8);
|
||||
yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', '*', 0.3);
|
||||
yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'utf-8', 0.7);
|
||||
yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'SHIFT_JIS', 0.3);
|
||||
}
|
||||
}
|
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\ApacheRequest;
|
||||
|
||||
class ApacheRequestTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideServerVars
|
||||
*/
|
||||
public function testUriMethods($server, $expectedRequestUri, $expectedBaseUrl, $expectedPathInfo)
|
||||
{
|
||||
$request = new ApacheRequest();
|
||||
$request->server->replace($server);
|
||||
|
||||
$this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
|
||||
$this->assertEquals($expectedBaseUrl, $request->getBaseUrl(), '->getBaseUrl() is correct');
|
||||
$this->assertEquals($expectedPathInfo, $request->getPathInfo(), '->getPathInfo() is correct');
|
||||
}
|
||||
|
||||
public function provideServerVars()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/foo/app_dev.php/bar',
|
||||
'SCRIPT_NAME' => '/foo/app_dev.php',
|
||||
'PATH_INFO' => '/bar',
|
||||
),
|
||||
'/foo/app_dev.php/bar',
|
||||
'/foo/app_dev.php',
|
||||
'/bar',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/foo/bar',
|
||||
'SCRIPT_NAME' => '/foo/app_dev.php',
|
||||
),
|
||||
'/foo/bar',
|
||||
'/foo',
|
||||
'/bar',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/app_dev.php/foo/bar',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
'PATH_INFO' => '/foo/bar',
|
||||
),
|
||||
'/app_dev.php/foo/bar',
|
||||
'/app_dev.php',
|
||||
'/foo/bar',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/foo/bar',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
),
|
||||
'/foo/bar',
|
||||
'',
|
||||
'/foo/bar',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/app_dev.php',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
),
|
||||
'/app_dev.php',
|
||||
'/app_dev.php',
|
||||
'/',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
),
|
||||
'/',
|
||||
'',
|
||||
'/',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,366 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\File\Stream;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\HttpFoundation\Tests\File\FakeFile;
|
||||
|
||||
class BinaryFileResponseTest extends ResponseTestCase
|
||||
{
|
||||
public function testConstruction()
|
||||
{
|
||||
$file = __DIR__.'/../README.md';
|
||||
$response = new BinaryFileResponse($file, 404, array('X-Header' => 'Foo'), true, null, true, true);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$this->assertEquals('Foo', $response->headers->get('X-Header'));
|
||||
$this->assertTrue($response->headers->has('ETag'));
|
||||
$this->assertTrue($response->headers->has('Last-Modified'));
|
||||
$this->assertFalse($response->headers->has('Content-Disposition'));
|
||||
|
||||
$response = BinaryFileResponse::create($file, 404, array(), true, ResponseHeaderBag::DISPOSITION_INLINE);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$this->assertFalse($response->headers->has('ETag'));
|
||||
$this->assertEquals('inline; filename=README.md', $response->headers->get('Content-Disposition'));
|
||||
}
|
||||
|
||||
public function testConstructWithNonAsciiFilename()
|
||||
{
|
||||
touch(sys_get_temp_dir().'/fööö.html');
|
||||
|
||||
$response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, array(), true, 'attachment');
|
||||
|
||||
@unlink(sys_get_temp_dir().'/fööö.html');
|
||||
|
||||
$this->assertSame('fööö.html', $response->getFile()->getFilename());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testSetContent()
|
||||
{
|
||||
$response = new BinaryFileResponse(__FILE__);
|
||||
$response->setContent('foo');
|
||||
}
|
||||
|
||||
public function testGetContent()
|
||||
{
|
||||
$response = new BinaryFileResponse(__FILE__);
|
||||
$this->assertFalse($response->getContent());
|
||||
}
|
||||
|
||||
public function testSetContentDispositionGeneratesSafeFallbackFilename()
|
||||
{
|
||||
$response = new BinaryFileResponse(__FILE__);
|
||||
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'föö.html');
|
||||
|
||||
$this->assertSame('attachment; filename=f__.html; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition'));
|
||||
}
|
||||
|
||||
public function testSetContentDispositionGeneratesSafeFallbackFilenameForWronglyEncodedFilename()
|
||||
{
|
||||
$response = new BinaryFileResponse(__FILE__);
|
||||
|
||||
$iso88591EncodedFilename = utf8_decode('föö.html');
|
||||
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $iso88591EncodedFilename);
|
||||
|
||||
// the parameter filename* is invalid in this case (rawurldecode('f%F6%F6') does not provide a UTF-8 string but an ISO-8859-1 encoded one)
|
||||
$this->assertSame('attachment; filename=f__.html; filename*=utf-8\'\'f%F6%F6.html', $response->headers->get('Content-Disposition'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideRanges
|
||||
*/
|
||||
public function testRequests($requestRange, $offset, $length, $responseRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
||||
|
||||
// do a request to get the ETag
|
||||
$request = Request::create('/');
|
||||
$response->prepare($request);
|
||||
$etag = $response->headers->get('ETag');
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('If-Range', $etag);
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
||||
fseek($file, $offset);
|
||||
$data = fread($file, $length);
|
||||
fclose($file);
|
||||
|
||||
$this->expectOutputString($data);
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(206, $response->getStatusCode());
|
||||
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
|
||||
$this->assertSame($length, $response->headers->get('Content-Length'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideRanges
|
||||
*/
|
||||
public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
|
||||
// do a request to get the LastModified
|
||||
$request = Request::create('/');
|
||||
$response->prepare($request);
|
||||
$lastModified = $response->headers->get('Last-Modified');
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('If-Range', $lastModified);
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
||||
fseek($file, $offset);
|
||||
$data = fread($file, $length);
|
||||
fclose($file);
|
||||
|
||||
$this->expectOutputString($data);
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(206, $response->getStatusCode());
|
||||
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
public function provideRanges()
|
||||
{
|
||||
return array(
|
||||
array('bytes=1-4', 1, 4, 'bytes 1-4/35'),
|
||||
array('bytes=-5', 30, 5, 'bytes 30-34/35'),
|
||||
array('bytes=30-', 30, 5, 'bytes 30-34/35'),
|
||||
array('bytes=30-30', 30, 1, 'bytes 30-30/35'),
|
||||
array('bytes=30-34', 30, 5, 'bytes 30-34/35'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testRangeRequestsWithoutLastModifiedDate()
|
||||
{
|
||||
// prevent auto last modified
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'), true, null, false, false);
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT');
|
||||
$request->headers->set('Range', 'bytes=1-4');
|
||||
|
||||
$this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif'));
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertNull($response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFullFileRanges
|
||||
*/
|
||||
public function testFullFileRequests($requestRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
||||
$data = fread($file, 35);
|
||||
fclose($file);
|
||||
|
||||
$this->expectOutputString($data);
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function provideFullFileRanges()
|
||||
{
|
||||
return array(
|
||||
array('bytes=0-'),
|
||||
array('bytes=0-34'),
|
||||
array('bytes=-35'),
|
||||
// Syntactical invalid range-request should also return the full resource
|
||||
array('bytes=20-10'),
|
||||
array('bytes=50-40'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testUnpreparedResponseSendsFullFile()
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200);
|
||||
|
||||
$data = file_get_contents(__DIR__.'/File/Fixtures/test.gif');
|
||||
|
||||
$this->expectOutputString($data);
|
||||
$response = clone $response;
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidRanges
|
||||
*/
|
||||
public function testInvalidRequests($requestRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(416, $response->getStatusCode());
|
||||
$this->assertEquals('bytes */35', $response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
public function provideInvalidRanges()
|
||||
{
|
||||
return array(
|
||||
array('bytes=-40'),
|
||||
array('bytes=30-40'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideXSendfileFiles
|
||||
*/
|
||||
public function testXSendfile($file)
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('X-Sendfile-Type', 'X-Sendfile');
|
||||
|
||||
BinaryFileResponse::trustXSendfileTypeHeader();
|
||||
$response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$response->prepare($request);
|
||||
|
||||
$this->expectOutputString('');
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertContains('README.md', $response->headers->get('X-Sendfile'));
|
||||
}
|
||||
|
||||
public function provideXSendfileFiles()
|
||||
{
|
||||
return array(
|
||||
array(__DIR__.'/../README.md'),
|
||||
array('file://'.__DIR__.'/../README.md'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getSampleXAccelMappings
|
||||
*/
|
||||
public function testXAccelMapping($realpath, $mapping, $virtual)
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect');
|
||||
$request->headers->set('X-Accel-Mapping', $mapping);
|
||||
|
||||
$file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test');
|
||||
|
||||
BinaryFileResponse::trustXSendfileTypeHeader();
|
||||
$response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$reflection = new \ReflectionObject($response);
|
||||
$property = $reflection->getProperty('file');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($response, $file);
|
||||
|
||||
$response->prepare($request);
|
||||
$this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect'));
|
||||
}
|
||||
|
||||
public function testDeleteFileAfterSend()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
|
||||
$path = __DIR__.'/File/Fixtures/to_delete';
|
||||
touch($path);
|
||||
$realPath = realpath($path);
|
||||
$this->assertFileExists($realPath);
|
||||
|
||||
$response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$response->deleteFileAfterSend(true);
|
||||
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertFileNotExists($path);
|
||||
}
|
||||
|
||||
public function testAcceptRangeOnUnsafeMethods()
|
||||
{
|
||||
$request = Request::create('/', 'POST');
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertEquals('none', $response->headers->get('Accept-Ranges'));
|
||||
}
|
||||
|
||||
public function testAcceptRangeNotOverriden()
|
||||
{
|
||||
$request = Request::create('/', 'POST');
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$response->headers->set('Accept-Ranges', 'foo');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertEquals('foo', $response->headers->get('Accept-Ranges'));
|
||||
}
|
||||
|
||||
public function getSampleXAccelMappings()
|
||||
{
|
||||
return array(
|
||||
array('/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'),
|
||||
array('/home/Foo/bar.txt', '/var/www/=/files/,/home/Foo/=/baz/', '/baz/bar.txt'),
|
||||
array('/home/Foo/bar.txt', '"/var/www/"="/files/", "/home/Foo/"="/baz/"', '/baz/bar.txt'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testStream()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, array('Content-Type' => 'text/plain'));
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertNull($response->headers->get('Content-Length'));
|
||||
}
|
||||
|
||||
protected function provideResponse()
|
||||
{
|
||||
return new BinaryFileResponse(__DIR__.'/../README.md', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
$path = __DIR__.'/../Fixtures/to_delete';
|
||||
if (file_exists($path)) {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
}
|
253
vendor/symfony/http-foundation/Tests/CookieTest.php
vendored
253
vendor/symfony/http-foundation/Tests/CookieTest.php
vendored
@@ -1,253 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
/**
|
||||
* CookieTest.
|
||||
*
|
||||
* @author John Kary <john@johnkary.net>
|
||||
* @author Hugo Hamon <hugo.hamon@sensio.com>
|
||||
*
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class CookieTest extends TestCase
|
||||
{
|
||||
public function invalidNames()
|
||||
{
|
||||
return array(
|
||||
array(''),
|
||||
array(',MyName'),
|
||||
array(';MyName'),
|
||||
array(' MyName'),
|
||||
array("\tMyName"),
|
||||
array("\rMyName"),
|
||||
array("\nMyName"),
|
||||
array("\013MyName"),
|
||||
array("\014MyName"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidNames
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
|
||||
{
|
||||
Cookie::create($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidExpiration()
|
||||
{
|
||||
Cookie::create('MyCookie', 'foo', 'bar');
|
||||
}
|
||||
|
||||
public function testNegativeExpirationIsNotPossible()
|
||||
{
|
||||
$cookie = Cookie::create('foo', 'bar', -100);
|
||||
|
||||
$this->assertSame(0, $cookie->getExpiresTime());
|
||||
}
|
||||
|
||||
public function testGetValue()
|
||||
{
|
||||
$value = 'MyValue';
|
||||
$cookie = Cookie::create('MyCookie', $value);
|
||||
|
||||
$this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value');
|
||||
}
|
||||
|
||||
public function testGetPath()
|
||||
{
|
||||
$cookie = Cookie::create('foo', 'bar');
|
||||
|
||||
$this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path');
|
||||
}
|
||||
|
||||
public function testGetExpiresTime()
|
||||
{
|
||||
$cookie = Cookie::create('foo', 'bar');
|
||||
|
||||
$this->assertEquals(0, $cookie->getExpiresTime(), '->getExpiresTime() returns the default expire date');
|
||||
|
||||
$cookie = Cookie::create('foo', 'bar', $expire = time() + 3600);
|
||||
|
||||
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
|
||||
}
|
||||
|
||||
public function testGetExpiresTimeIsCastToInt()
|
||||
{
|
||||
$cookie = Cookie::create('foo', 'bar', 3600.9);
|
||||
|
||||
$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
|
||||
}
|
||||
|
||||
public function testConstructorWithDateTime()
|
||||
{
|
||||
$expire = new \DateTime();
|
||||
$cookie = Cookie::create('foo', 'bar', $expire);
|
||||
|
||||
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
|
||||
}
|
||||
|
||||
public function testConstructorWithDateTimeImmutable()
|
||||
{
|
||||
$expire = new \DateTimeImmutable();
|
||||
$cookie = Cookie::create('foo', 'bar', $expire);
|
||||
|
||||
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
|
||||
}
|
||||
|
||||
public function testGetExpiresTimeWithStringValue()
|
||||
{
|
||||
$value = '+1 day';
|
||||
$cookie = Cookie::create('foo', 'bar', $value);
|
||||
$expire = strtotime($value);
|
||||
|
||||
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1);
|
||||
}
|
||||
|
||||
public function testGetDomain()
|
||||
{
|
||||
$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 = 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 = 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 = 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 = Cookie::create('foo', 'bar', time() - 20);
|
||||
|
||||
$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
|
||||
|
||||
$cookie = Cookie::create('foo', 'bar');
|
||||
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
|
||||
$cookie = Cookie::create('foo', 'bar');
|
||||
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
|
||||
$cookie = Cookie::create('foo', 'bar', -1);
|
||||
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
}
|
||||
|
||||
public function testToString()
|
||||
{
|
||||
$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 = 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 = 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 = Cookie::create('foo', 'bar');
|
||||
$this->assertEquals('foo=bar; path=/; httponly; samesite=lax', (string) $cookie);
|
||||
}
|
||||
|
||||
public function testRawCookie()
|
||||
{
|
||||
$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 = 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 = Cookie::create('foo', 'bar');
|
||||
$this->assertEquals(0, $cookie->getMaxAge());
|
||||
|
||||
$cookie = Cookie::create('foo', 'bar', $expire = time() + 100);
|
||||
$this->assertEquals($expire - time(), $cookie->getMaxAge());
|
||||
|
||||
$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(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(Cookie::create('foo', 'bar', 0, '/', null, false, false, false, null), $cookie);
|
||||
|
||||
$cookie = Cookie::fromString('foo', true);
|
||||
$this->assertEquals(Cookie::create('foo', null, 0, '/', null, false, false, false, null), $cookie);
|
||||
}
|
||||
|
||||
public function testFromStringWithHttpOnly()
|
||||
{
|
||||
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
|
||||
$this->assertTrue($cookie->isHttpOnly());
|
||||
|
||||
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure');
|
||||
$this->assertFalse($cookie->isHttpOnly());
|
||||
}
|
||||
|
||||
public function 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());
|
||||
}
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\HttpFoundation\ExpressionRequestMatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class ExpressionRequestMatcherTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testWhenNoExpressionIsSet()
|
||||
{
|
||||
$expressionRequestMatcher = new ExpressionRequestMatcher();
|
||||
$expressionRequestMatcher->matches(new Request());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideExpressions
|
||||
*/
|
||||
public function testMatchesWhenParentMatchesIsTrue($expression, $expected)
|
||||
{
|
||||
$request = Request::create('/foo');
|
||||
$expressionRequestMatcher = new ExpressionRequestMatcher();
|
||||
|
||||
$expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
|
||||
$this->assertSame($expected, $expressionRequestMatcher->matches($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideExpressions
|
||||
*/
|
||||
public function testMatchesWhenParentMatchesIsFalse($expression)
|
||||
{
|
||||
$request = Request::create('/foo');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$expressionRequestMatcher = new ExpressionRequestMatcher();
|
||||
$expressionRequestMatcher->matchAttribute('foo', 'bar');
|
||||
|
||||
$expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
|
||||
$this->assertFalse($expressionRequestMatcher->matches($request));
|
||||
}
|
||||
|
||||
public function provideExpressions()
|
||||
{
|
||||
return array(
|
||||
array('request.getMethod() == method', true),
|
||||
array('request.getPathInfo() == path', true),
|
||||
array('request.getHost() == host', true),
|
||||
array('request.getClientIp() == ip', true),
|
||||
array('request.attributes.all() == attributes', true),
|
||||
array('request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', true),
|
||||
array('request.getMethod() != method', false),
|
||||
array('request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', false),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\File;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\File as OrigFile;
|
||||
|
||||
class FakeFile extends OrigFile
|
||||
{
|
||||
private $realpath;
|
||||
|
||||
public function __construct($realpath, $path)
|
||||
{
|
||||
$this->realpath = $realpath;
|
||||
parent::__construct($path, false);
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRealpath()
|
||||
{
|
||||
return $this->realpath;
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
public function getMTime()
|
||||
{
|
||||
return time();
|
||||
}
|
||||
}
|
@@ -1,180 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\File;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
|
||||
class FileTest extends TestCase
|
||||
{
|
||||
protected $file;
|
||||
|
||||
public function testGetMimeTypeUsesMimeTypeGuessers()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/test.gif');
|
||||
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
|
||||
|
||||
MimeTypeGuesser::getInstance()->register($guesser);
|
||||
|
||||
$this->assertEquals('image/gif', $file->getMimeType());
|
||||
}
|
||||
|
||||
public function testGuessExtensionWithoutGuesser()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/directory/.empty');
|
||||
|
||||
$this->assertNull($file->guessExtension());
|
||||
}
|
||||
|
||||
public function testGuessExtensionIsBasedOnMimeType()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/test');
|
||||
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
|
||||
|
||||
MimeTypeGuesser::getInstance()->register($guesser);
|
||||
|
||||
$this->assertEquals('gif', $file->guessExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension fileinfo
|
||||
*/
|
||||
public function testGuessExtensionWithReset()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/other-file.example');
|
||||
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
|
||||
MimeTypeGuesser::getInstance()->register($guesser);
|
||||
|
||||
$this->assertEquals('gif', $file->guessExtension());
|
||||
|
||||
MimeTypeGuesser::reset();
|
||||
|
||||
$this->assertNull($file->guessExtension());
|
||||
}
|
||||
|
||||
public function testConstructWhenFileNotExists()
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
|
||||
|
||||
new File(__DIR__.'/Fixtures/not_here');
|
||||
}
|
||||
|
||||
public function testMove()
|
||||
{
|
||||
$path = __DIR__.'/Fixtures/test.copy.gif';
|
||||
$targetDir = __DIR__.'/Fixtures/directory';
|
||||
$targetPath = $targetDir.'/test.copy.gif';
|
||||
@unlink($path);
|
||||
@unlink($targetPath);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $path);
|
||||
|
||||
$file = new File($path);
|
||||
$movedFile = $file->move($targetDir);
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($path);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($targetPath);
|
||||
}
|
||||
|
||||
public function testMoveWithNewName()
|
||||
{
|
||||
$path = __DIR__.'/Fixtures/test.copy.gif';
|
||||
$targetDir = __DIR__.'/Fixtures/directory';
|
||||
$targetPath = $targetDir.'/test.newname.gif';
|
||||
@unlink($path);
|
||||
@unlink($targetPath);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $path);
|
||||
|
||||
$file = new File($path);
|
||||
$movedFile = $file->move($targetDir, 'test.newname.gif');
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($path);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($targetPath);
|
||||
}
|
||||
|
||||
public function getFilenameFixtures()
|
||||
{
|
||||
return array(
|
||||
array('original.gif', 'original.gif'),
|
||||
array('..\\..\\original.gif', 'original.gif'),
|
||||
array('../../original.gif', 'original.gif'),
|
||||
array('файлfile.gif', 'файлfile.gif'),
|
||||
array('..\\..\\файлfile.gif', 'файлfile.gif'),
|
||||
array('../../файлfile.gif', 'файлfile.gif'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getFilenameFixtures
|
||||
*/
|
||||
public function testMoveWithNonLatinName($filename, $sanitizedFilename)
|
||||
{
|
||||
$path = __DIR__.'/Fixtures/'.$sanitizedFilename;
|
||||
$targetDir = __DIR__.'/Fixtures/directory/';
|
||||
$targetPath = $targetDir.$sanitizedFilename;
|
||||
@unlink($path);
|
||||
@unlink($targetPath);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $path);
|
||||
|
||||
$file = new File($path);
|
||||
$movedFile = $file->move($targetDir, $filename);
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($path);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($targetPath);
|
||||
}
|
||||
|
||||
public function testMoveToAnUnexistentDirectory()
|
||||
{
|
||||
$sourcePath = __DIR__.'/Fixtures/test.copy.gif';
|
||||
$targetDir = __DIR__.'/Fixtures/directory/sub';
|
||||
$targetPath = $targetDir.'/test.copy.gif';
|
||||
@unlink($sourcePath);
|
||||
@unlink($targetPath);
|
||||
@rmdir($targetDir);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $sourcePath);
|
||||
|
||||
$file = new File($sourcePath);
|
||||
$movedFile = $file->move($targetDir);
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($sourcePath);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($sourcePath);
|
||||
@unlink($targetPath);
|
||||
@rmdir($targetDir);
|
||||
}
|
||||
|
||||
protected function createMockGuesser($path, $mimeType)
|
||||
{
|
||||
$guesser = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface')->getMock();
|
||||
$guesser
|
||||
->expects($this->once())
|
||||
->method('guess')
|
||||
->with($this->equalTo($path))
|
||||
->will($this->returnValue($mimeType))
|
||||
;
|
||||
|
||||
return $guesser;
|
||||
}
|
||||
}
|
@@ -1 +0,0 @@
|
||||
f
|
Binary file not shown.
Before Width: | Height: | Size: 35 B |
Binary file not shown.
Before Width: | Height: | Size: 35 B |
@@ -1,90 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\File\MimeType;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
|
||||
/**
|
||||
* @requires extension fileinfo
|
||||
*/
|
||||
class MimeTypeTest extends TestCase
|
||||
{
|
||||
protected $path;
|
||||
|
||||
public function testGuessImageWithoutExtension()
|
||||
{
|
||||
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
|
||||
}
|
||||
|
||||
public function testGuessImageWithDirectory()
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
|
||||
|
||||
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory');
|
||||
}
|
||||
|
||||
public function testGuessImageWithFileBinaryMimeTypeGuesser()
|
||||
{
|
||||
$guesser = MimeTypeGuesser::getInstance();
|
||||
$guesser->register(new FileBinaryMimeTypeGuesser());
|
||||
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
|
||||
}
|
||||
|
||||
public function testGuessImageWithKnownExtension()
|
||||
{
|
||||
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif'));
|
||||
}
|
||||
|
||||
public function testGuessFileWithUnknownExtension()
|
||||
{
|
||||
$this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
|
||||
}
|
||||
|
||||
public function testGuessWithIncorrectPath()
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
|
||||
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here');
|
||||
}
|
||||
|
||||
public function testGuessWithNonReadablePath()
|
||||
{
|
||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||
$this->markTestSkipped('Can not verify chmod operations on Windows');
|
||||
}
|
||||
|
||||
if (!getenv('USER') || 'root' === getenv('USER')) {
|
||||
$this->markTestSkipped('This test will fail if run under superuser');
|
||||
}
|
||||
|
||||
$path = __DIR__.'/../Fixtures/to_delete';
|
||||
touch($path);
|
||||
@chmod($path, 0333);
|
||||
|
||||
if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) {
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException');
|
||||
MimeTypeGuesser::getInstance()->guess($path);
|
||||
} else {
|
||||
$this->markTestSkipped('Can not verify chmod operations, change of file permissions failed');
|
||||
}
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
$path = __DIR__.'/../Fixtures/to_delete';
|
||||
if (file_exists($path)) {
|
||||
@chmod($path, 0666);
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,346 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\File;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\CannotWriteFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\ExtensionFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FormSizeFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\NoFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\PartialFileException;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
||||
class UploadedFileTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
if (!ini_get('file_uploads')) {
|
||||
$this->markTestSkipped('file_uploads is disabled in php.ini');
|
||||
}
|
||||
}
|
||||
|
||||
public function testConstructWhenFileNotExists()
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
|
||||
|
||||
new UploadedFile(
|
||||
__DIR__.'/Fixtures/not_here',
|
||||
'original.gif',
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public function testFileUploadsWithNoMimeType()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
UPLOAD_ERR_OK
|
||||
);
|
||||
|
||||
$this->assertEquals('application/octet-stream', $file->getClientMimeType());
|
||||
|
||||
if (\extension_loaded('fileinfo')) {
|
||||
$this->assertEquals('image/gif', $file->getMimeType());
|
||||
}
|
||||
}
|
||||
|
||||
public function testFileUploadsWithUnknownMimeType()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/.unknownextension',
|
||||
'original.gif',
|
||||
null,
|
||||
UPLOAD_ERR_OK
|
||||
);
|
||||
|
||||
$this->assertEquals('application/octet-stream', $file->getClientMimeType());
|
||||
}
|
||||
|
||||
public function testGuessClientExtension()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('gif', $file->guessClientExtension());
|
||||
}
|
||||
|
||||
public function testGuessClientExtensionWithIncorrectMimeType()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/jpeg',
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('jpeg', $file->guessClientExtension());
|
||||
}
|
||||
|
||||
public function testErrorIsOkByDefault()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals(UPLOAD_ERR_OK, $file->getError());
|
||||
}
|
||||
|
||||
public function testGetClientOriginalName()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('original.gif', $file->getClientOriginalName());
|
||||
}
|
||||
|
||||
public function testGetClientOriginalExtension()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('gif', $file->getClientOriginalExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException
|
||||
*/
|
||||
public function testMoveLocalFileIsNotAllowed()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
UPLOAD_ERR_OK
|
||||
);
|
||||
|
||||
$movedFile = $file->move(__DIR__.'/Fixtures/directory');
|
||||
}
|
||||
|
||||
public function failedUploadedFile()
|
||||
{
|
||||
foreach (array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_EXTENSION, -1) as $error) {
|
||||
yield array(new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
$error
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider failedUploadedFile
|
||||
*/
|
||||
public function testMoveFailed(UploadedFile $file)
|
||||
{
|
||||
switch ($file->getError()) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
$exceptionClass = IniSizeFileException::class;
|
||||
break;
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
$exceptionClass = FormSizeFileException::class;
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
$exceptionClass = PartialFileException::class;
|
||||
break;
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$exceptionClass = NoFileException::class;
|
||||
break;
|
||||
case UPLOAD_ERR_CANT_WRITE:
|
||||
$exceptionClass = CannotWriteFileException::class;
|
||||
break;
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
$exceptionClass = NoTmpDirFileException::class;
|
||||
break;
|
||||
case UPLOAD_ERR_EXTENSION:
|
||||
$exceptionClass = ExtensionFileException::class;
|
||||
break;
|
||||
default:
|
||||
$exceptionClass = FileException::class;
|
||||
}
|
||||
|
||||
$this->expectException($exceptionClass);
|
||||
|
||||
$file->move(__DIR__.'/Fixtures/directory');
|
||||
}
|
||||
|
||||
public function testMoveLocalFileIsAllowedInTestMode()
|
||||
{
|
||||
$path = __DIR__.'/Fixtures/test.copy.gif';
|
||||
$targetDir = __DIR__.'/Fixtures/directory';
|
||||
$targetPath = $targetDir.'/test.copy.gif';
|
||||
@unlink($path);
|
||||
@unlink($targetPath);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $path);
|
||||
|
||||
$file = new UploadedFile(
|
||||
$path,
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
UPLOAD_ERR_OK,
|
||||
true
|
||||
);
|
||||
|
||||
$movedFile = $file->move(__DIR__.'/Fixtures/directory');
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($path);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($targetPath);
|
||||
}
|
||||
|
||||
public function testGetClientOriginalNameSanitizeFilename()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'../../original.gif',
|
||||
'image/gif'
|
||||
);
|
||||
|
||||
$this->assertEquals('original.gif', $file->getClientOriginalName());
|
||||
}
|
||||
|
||||
public function testGetSize()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif'
|
||||
);
|
||||
|
||||
$this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize());
|
||||
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test',
|
||||
'original.gif',
|
||||
'image/gif'
|
||||
);
|
||||
|
||||
$this->assertEquals(filesize(__DIR__.'/Fixtures/test'), $file->getSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
* @expectedDeprecation Passing a size as 4th argument to the constructor of "Symfony\Component\HttpFoundation\File\UploadedFile" is deprecated since Symfony 4.1.
|
||||
*/
|
||||
public function testConstructDeprecatedSize()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
UPLOAD_ERR_OK,
|
||||
false
|
||||
);
|
||||
|
||||
$this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
* @expectedDeprecation Passing a size as 4th argument to the constructor of "Symfony\Component\HttpFoundation\File\UploadedFile" is deprecated since Symfony 4.1.
|
||||
*/
|
||||
public function testConstructDeprecatedSizeWhenPassingOnlyThe4Needed()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
filesize(__DIR__.'/Fixtures/test.gif')
|
||||
);
|
||||
|
||||
$this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize());
|
||||
}
|
||||
|
||||
public function testGetExtension()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif'
|
||||
);
|
||||
|
||||
$this->assertEquals('gif', $file->getExtension());
|
||||
}
|
||||
|
||||
public function testIsValid()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
UPLOAD_ERR_OK,
|
||||
true
|
||||
);
|
||||
|
||||
$this->assertTrue($file->isValid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider uploadedFileErrorProvider
|
||||
*/
|
||||
public function testIsInvalidOnUploadError($error)
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
$error
|
||||
);
|
||||
|
||||
$this->assertFalse($file->isValid());
|
||||
}
|
||||
|
||||
public function uploadedFileErrorProvider()
|
||||
{
|
||||
return array(
|
||||
array(UPLOAD_ERR_INI_SIZE),
|
||||
array(UPLOAD_ERR_FORM_SIZE),
|
||||
array(UPLOAD_ERR_PARTIAL),
|
||||
array(UPLOAD_ERR_NO_TMP_DIR),
|
||||
array(UPLOAD_ERR_EXTENSION),
|
||||
);
|
||||
}
|
||||
|
||||
public function testIsInvalidIfNotHttpUpload()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
UPLOAD_ERR_OK
|
||||
);
|
||||
|
||||
$this->assertFalse($file->isValid());
|
||||
}
|
||||
}
|
178
vendor/symfony/http-foundation/Tests/FileBagTest.php
vendored
178
vendor/symfony/http-foundation/Tests/FileBagTest.php
vendored
@@ -1,178 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\FileBag;
|
||||
|
||||
/**
|
||||
* FileBagTest.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
|
||||
*/
|
||||
class FileBagTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testFileMustBeAnArrayOrUploadedFile()
|
||||
{
|
||||
new FileBag(array('file' => 'foo'));
|
||||
}
|
||||
|
||||
public function testShouldConvertsUploadedFiles()
|
||||
{
|
||||
$tmpFile = $this->createTempFile();
|
||||
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain');
|
||||
|
||||
$bag = new FileBag(array('file' => array(
|
||||
'name' => basename($tmpFile),
|
||||
'type' => 'text/plain',
|
||||
'tmp_name' => $tmpFile,
|
||||
'error' => 0,
|
||||
'size' => null,
|
||||
)));
|
||||
|
||||
$this->assertEquals($file, $bag->get('file'));
|
||||
}
|
||||
|
||||
public function testShouldSetEmptyUploadedFilesToNull()
|
||||
{
|
||||
$bag = new FileBag(array('file' => array(
|
||||
'name' => '',
|
||||
'type' => '',
|
||||
'tmp_name' => '',
|
||||
'error' => UPLOAD_ERR_NO_FILE,
|
||||
'size' => 0,
|
||||
)));
|
||||
|
||||
$this->assertNull($bag->get('file'));
|
||||
}
|
||||
|
||||
public function testShouldRemoveEmptyUploadedFilesForMultiUpload()
|
||||
{
|
||||
$bag = new FileBag(array('files' => array(
|
||||
'name' => array(''),
|
||||
'type' => array(''),
|
||||
'tmp_name' => array(''),
|
||||
'error' => array(UPLOAD_ERR_NO_FILE),
|
||||
'size' => array(0),
|
||||
)));
|
||||
|
||||
$this->assertSame(array(), $bag->get('files'));
|
||||
}
|
||||
|
||||
public function testShouldNotRemoveEmptyUploadedFilesForAssociativeArray()
|
||||
{
|
||||
$bag = new FileBag(array('files' => array(
|
||||
'name' => array('file1' => ''),
|
||||
'type' => array('file1' => ''),
|
||||
'tmp_name' => array('file1' => ''),
|
||||
'error' => array('file1' => UPLOAD_ERR_NO_FILE),
|
||||
'size' => array('file1' => 0),
|
||||
)));
|
||||
|
||||
$this->assertSame(array('file1' => null), $bag->get('files'));
|
||||
}
|
||||
|
||||
public function testShouldConvertUploadedFilesWithPhpBug()
|
||||
{
|
||||
$tmpFile = $this->createTempFile();
|
||||
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain');
|
||||
|
||||
$bag = new FileBag(array(
|
||||
'child' => array(
|
||||
'name' => array(
|
||||
'file' => basename($tmpFile),
|
||||
),
|
||||
'type' => array(
|
||||
'file' => 'text/plain',
|
||||
),
|
||||
'tmp_name' => array(
|
||||
'file' => $tmpFile,
|
||||
),
|
||||
'error' => array(
|
||||
'file' => 0,
|
||||
),
|
||||
'size' => array(
|
||||
'file' => null,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$files = $bag->all();
|
||||
$this->assertEquals($file, $files['child']['file']);
|
||||
}
|
||||
|
||||
public function testShouldConvertNestedUploadedFilesWithPhpBug()
|
||||
{
|
||||
$tmpFile = $this->createTempFile();
|
||||
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain');
|
||||
|
||||
$bag = new FileBag(array(
|
||||
'child' => array(
|
||||
'name' => array(
|
||||
'sub' => array('file' => basename($tmpFile)),
|
||||
),
|
||||
'type' => array(
|
||||
'sub' => array('file' => 'text/plain'),
|
||||
),
|
||||
'tmp_name' => array(
|
||||
'sub' => array('file' => $tmpFile),
|
||||
),
|
||||
'error' => array(
|
||||
'sub' => array('file' => 0),
|
||||
),
|
||||
'size' => array(
|
||||
'sub' => array('file' => null),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$files = $bag->all();
|
||||
$this->assertEquals($file, $files['child']['sub']['file']);
|
||||
}
|
||||
|
||||
public function testShouldNotConvertNestedUploadedFiles()
|
||||
{
|
||||
$tmpFile = $this->createTempFile();
|
||||
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain');
|
||||
$bag = new FileBag(array('image' => array('file' => $file)));
|
||||
|
||||
$files = $bag->all();
|
||||
$this->assertEquals($file, $files['image']['file']);
|
||||
}
|
||||
|
||||
protected function createTempFile()
|
||||
{
|
||||
$tempFile = tempnam(sys_get_temp_dir().'/form_test', 'FormTest');
|
||||
file_put_contents($tempFile, '1');
|
||||
|
||||
return $tempFile;
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
mkdir(sys_get_temp_dir().'/form_test', 0777, true);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
rmdir(sys_get_temp_dir().'/form_test');
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
$parent = __DIR__;
|
||||
while (!@file_exists($parent.'/vendor/autoload.php')) {
|
||||
if (!@file_exists($parent)) {
|
||||
// open_basedir restriction in effect
|
||||
break;
|
||||
}
|
||||
if ($parent === dirname($parent)) {
|
||||
echo "vendor/autoload.php not found\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$parent = dirname($parent);
|
||||
}
|
||||
|
||||
require $parent.'/vendor/autoload.php';
|
||||
|
||||
error_reporting(-1);
|
||||
ini_set('html_errors', 0);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
if (filter_var(ini_get('xdebug.default_enable'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
xdebug_disable();
|
||||
}
|
||||
|
||||
header_remove('X-Powered-By');
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
register_shutdown_function(function () {
|
||||
echo "\n";
|
||||
session_write_close();
|
||||
print_r(headers_list());
|
||||
echo "shutdown\n";
|
||||
});
|
||||
ob_start();
|
||||
|
||||
$r = new Response();
|
||||
$r->headers->set('Date', 'Sat, 12 Nov 1955 20:04:00 GMT');
|
||||
|
||||
return $r;
|
@@ -1,11 +0,0 @@
|
||||
|
||||
Warning: Expiry date cannot have a year greater than 9999 in %scookie_max_age.php on line 10
|
||||
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: no-cache, private
|
||||
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
|
||||
[3] => Set-Cookie: foo=bar; expires=Sat, 01-Jan-10000 02:46:40 GMT; Max-Age=%d; path=/
|
||||
)
|
||||
shutdown
|
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
$r = require __DIR__.'/common.inc';
|
||||
|
||||
$r->headers->setCookie(new Cookie('foo', 'bar', 253402310800, '', null, false, false, false, null));
|
||||
$r->sendHeaders();
|
||||
|
||||
setcookie('foo2', 'bar', 253402310800, '/');
|
@@ -1,10 +0,0 @@
|
||||
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: no-cache, private
|
||||
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
|
||||
[3] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/
|
||||
[4] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/
|
||||
)
|
||||
shutdown
|
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
$r = require __DIR__.'/common.inc';
|
||||
|
||||
$str = '?*():@&+$/%#[]';
|
||||
|
||||
$r->headers->setCookie(new Cookie($str, $str, 0, '/', null, false, false, true, null));
|
||||
$r->sendHeaders();
|
||||
|
||||
setrawcookie($str, $str, 0, '/', null, false, false);
|
@@ -1,9 +0,0 @@
|
||||
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: no-cache, private
|
||||
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
|
||||
[3] => Set-Cookie: CookieSamesiteLaxTest=LaxValue; path=/; httponly; samesite=lax
|
||||
)
|
||||
shutdown
|
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
$r = require __DIR__.'/common.inc';
|
||||
|
||||
$r->headers->setCookie(new Cookie('CookieSamesiteLaxTest', 'LaxValue', 0, '/', null, false, true, false, Cookie::SAMESITE_LAX));
|
||||
$r->sendHeaders();
|
@@ -1,9 +0,0 @@
|
||||
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: no-cache, private
|
||||
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
|
||||
[3] => Set-Cookie: CookieSamesiteStrictTest=StrictValue; path=/; httponly; samesite=strict
|
||||
)
|
||||
shutdown
|
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
$r = require __DIR__.'/common.inc';
|
||||
|
||||
$r->headers->setCookie(new Cookie('CookieSamesiteStrictTest', 'StrictValue', 0, '/', null, false, true, false, Cookie::SAMESITE_STRICT));
|
||||
$r->sendHeaders();
|
@@ -1,10 +0,0 @@
|
||||
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
[1] => Cache-Control: no-cache, private
|
||||
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
|
||||
[3] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
|
||||
[4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
|
||||
)
|
||||
shutdown
|
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
$r = require __DIR__.'/common.inc';
|
||||
|
||||
$str = '?*():@&+$/%#[]';
|
||||
|
||||
$r->headers->setCookie(new Cookie($str, $str, 0, '', null, false, false, false, null));
|
||||
$r->sendHeaders();
|
||||
|
||||
setcookie($str, $str, 0, '/');
|
@@ -1,6 +0,0 @@
|
||||
The cookie name "Hello + world" contains invalid characters.
|
||||
Array
|
||||
(
|
||||
[0] => Content-Type: text/plain; charset=utf-8
|
||||
)
|
||||
shutdown
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user