mirror of
https://github.com/linuxserver/Heimdall.git
synced 2025-12-02 13:09:53 +09:00
Update to laravel 7
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
|
||||
23
vendor/symfony/http-foundation/AcceptHeader.php
vendored
23
vendor/symfony/http-foundation/AcceptHeader.php
vendored
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(AcceptHeaderItem::class);
|
||||
|
||||
/**
|
||||
* Represents an Accept-* header.
|
||||
*
|
||||
@@ -44,15 +47,13 @@ class AcceptHeader
|
||||
/**
|
||||
* Builds an AcceptHeader instance from a string.
|
||||
*
|
||||
* @param string $headerValue
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function fromString($headerValue)
|
||||
public static function fromString(?string $headerValue)
|
||||
{
|
||||
$index = 0;
|
||||
|
||||
$parts = HeaderUtils::split((string) $headerValue, ',;=');
|
||||
$parts = HeaderUtils::split($headerValue ?? '', ',;=');
|
||||
|
||||
return new self(array_map(function ($subParts) use (&$index) {
|
||||
$part = array_shift($subParts);
|
||||
@@ -78,11 +79,9 @@ class AcceptHeader
|
||||
/**
|
||||
* Tests if header has given value.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($value)
|
||||
public function has(string $value)
|
||||
{
|
||||
return isset($this->items[$value]);
|
||||
}
|
||||
@@ -90,11 +89,9 @@ class AcceptHeader
|
||||
/**
|
||||
* Returns given value's item, if exists.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return AcceptHeaderItem|null
|
||||
*/
|
||||
public function get($value)
|
||||
public function get(string $value)
|
||||
{
|
||||
return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null;
|
||||
}
|
||||
@@ -127,11 +124,9 @@ class AcceptHeader
|
||||
/**
|
||||
* Filters items on their value using given regex.
|
||||
*
|
||||
* @param string $pattern
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function filter($pattern)
|
||||
public function filter(string $pattern)
|
||||
{
|
||||
return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) {
|
||||
return preg_match($pattern, $item->getValue());
|
||||
@@ -153,7 +148,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) {
|
||||
|
||||
@@ -34,13 +34,11 @@ class AcceptHeaderItem
|
||||
/**
|
||||
* Builds an AcceptHeaderInstance instance from a string.
|
||||
*
|
||||
* @param string $itemValue
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function fromString($itemValue)
|
||||
public static function fromString(?string $itemValue)
|
||||
{
|
||||
$parts = HeaderUtils::split($itemValue, ';=');
|
||||
$parts = HeaderUtils::split($itemValue ?? '', ';=');
|
||||
|
||||
$part = array_shift($parts);
|
||||
$attributes = HeaderUtils::combine($parts);
|
||||
@@ -66,11 +64,9 @@ class AcceptHeaderItem
|
||||
/**
|
||||
* Set the item value.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($value)
|
||||
public function setValue(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
@@ -90,11 +86,9 @@ class AcceptHeaderItem
|
||||
/**
|
||||
* Set the item quality.
|
||||
*
|
||||
* @param float $quality
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setQuality($quality)
|
||||
public function setQuality(float $quality)
|
||||
{
|
||||
$this->quality = $quality;
|
||||
|
||||
@@ -114,11 +108,9 @@ class AcceptHeaderItem
|
||||
/**
|
||||
* Set the item index.
|
||||
*
|
||||
* @param int $index
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIndex($index)
|
||||
public function setIndex(int $index)
|
||||
{
|
||||
$this->index = $index;
|
||||
|
||||
@@ -138,11 +130,9 @@ class AcceptHeaderItem
|
||||
/**
|
||||
* Tests if an attribute exists.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAttribute($name)
|
||||
public function hasAttribute(string $name)
|
||||
{
|
||||
return isset($this->attributes[$name]);
|
||||
}
|
||||
@@ -150,14 +140,13 @@ class AcceptHeaderItem
|
||||
/**
|
||||
* Returns an attribute by its name.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $default
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute($name, $default = null)
|
||||
public function getAttribute(string $name, $default = null)
|
||||
{
|
||||
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
|
||||
return $this->attributes[$name] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,17 +162,14 @@ class AcceptHeaderItem
|
||||
/**
|
||||
* Set an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($name, $value)
|
||||
public function setAttribute(string $name, string $value)
|
||||
{
|
||||
if ('q' === $name) {
|
||||
$this->quality = (float) $value;
|
||||
} else {
|
||||
$this->attributes[$name] = (string) $value;
|
||||
$this->attributes[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
43
vendor/symfony/http-foundation/ApacheRequest.php
vendored
43
vendor/symfony/http-foundation/ApacheRequest.php
vendored
@@ -1,43 +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;
|
||||
|
||||
/**
|
||||
* Request represents an HTTP request from an Apache server.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ApacheRequest extends Request
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareRequestUri()
|
||||
{
|
||||
return $this->server->get('REQUEST_URI');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareBaseUrl()
|
||||
{
|
||||
$baseUrl = $this->server->get('SCRIPT_NAME');
|
||||
|
||||
if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) {
|
||||
// assume mod_rewrite
|
||||
return rtrim(\dirname($baseUrl), '/\\');
|
||||
}
|
||||
|
||||
return $baseUrl;
|
||||
}
|
||||
}
|
||||
@@ -65,25 +65,26 @@ class BinaryFileResponse extends Response
|
||||
* @param bool $autoLastModified Whether the Last-Modified header should be automatically set
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @deprecated since Symfony 5.2, use __construct() instead.
|
||||
*/
|
||||
public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
|
||||
public static function create($file = null, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
|
||||
{
|
||||
trigger_deprecation('symfony/http-foundation', '5.2', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, static::class);
|
||||
|
||||
return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file to stream.
|
||||
*
|
||||
* @param \SplFileInfo|string $file The file to stream
|
||||
* @param string $contentDisposition
|
||||
* @param bool $autoEtag
|
||||
* @param bool $autoLastModified
|
||||
* @param \SplFileInfo|string $file The file to stream
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws FileException
|
||||
*/
|
||||
public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
|
||||
public function setFile($file, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
|
||||
{
|
||||
if (!$file instanceof File) {
|
||||
if ($file instanceof \SplFileInfo) {
|
||||
@@ -117,7 +118,7 @@ class BinaryFileResponse extends Response
|
||||
/**
|
||||
* Gets the file.
|
||||
*
|
||||
* @return File The file to stream
|
||||
* @return File
|
||||
*/
|
||||
public function getFile()
|
||||
{
|
||||
@@ -126,6 +127,8 @@ class BinaryFileResponse extends Response
|
||||
|
||||
/**
|
||||
* Automatically sets the Last-Modified header according the file modification date.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAutoLastModified()
|
||||
{
|
||||
@@ -136,6 +139,8 @@ class BinaryFileResponse extends Response
|
||||
|
||||
/**
|
||||
* Automatically sets the ETag header according to the checksum of the file.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAutoEtag()
|
||||
{
|
||||
@@ -153,13 +158,13 @@ class BinaryFileResponse extends Response
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContentDisposition($disposition, $filename = '', $filenameFallback = '')
|
||||
public function setContentDisposition(string $disposition, string $filename = '', string $filenameFallback = '')
|
||||
{
|
||||
if ('' === $filename) {
|
||||
$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) {
|
||||
@@ -204,7 +209,7 @@ class BinaryFileResponse extends Response
|
||||
|
||||
if (!$this->headers->has('Accept-Ranges')) {
|
||||
// Only accept ranges on safe HTTP methods
|
||||
$this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
|
||||
$this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none');
|
||||
}
|
||||
|
||||
if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
|
||||
@@ -217,10 +222,10 @@ 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
|
||||
@@ -234,33 +239,36 @@ class BinaryFileResponse extends Response
|
||||
$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) + [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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,7 +277,7 @@ class BinaryFileResponse extends Response
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function hasValidIfRangeHeader($header)
|
||||
private function hasValidIfRangeHeader(?string $header): bool
|
||||
{
|
||||
if ($this->getEtag() === $header) {
|
||||
return true;
|
||||
@@ -283,8 +291,6 @@ class BinaryFileResponse extends Response
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the file.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendContent()
|
||||
@@ -297,15 +303,15 @@ class BinaryFileResponse extends Response
|
||||
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);
|
||||
|
||||
fclose($out);
|
||||
fclose($file);
|
||||
|
||||
if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) {
|
||||
if ($this->deleteFileAfterSend && is_file($this->file->getPathname())) {
|
||||
unlink($this->file->getPathname());
|
||||
}
|
||||
|
||||
@@ -317,17 +323,17 @@ class BinaryFileResponse extends Response
|
||||
*
|
||||
* @throws \LogicException when the content is not null
|
||||
*/
|
||||
public function setContent($content)
|
||||
public function setContent(?string $content)
|
||||
{
|
||||
if (null !== $content) {
|
||||
throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
@@ -343,14 +349,12 @@ class BinaryFileResponse extends Response
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is set to true, the file will be unlinked after the request is send
|
||||
* If this is set to true, the file will be unlinked after the request is sent
|
||||
* Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
|
||||
*
|
||||
* @param bool $shouldDelete
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function deleteFileAfterSend($shouldDelete = true)
|
||||
public function deleteFileAfterSend(bool $shouldDelete = true)
|
||||
{
|
||||
$this->deleteFileAfterSend = $shouldDelete;
|
||||
|
||||
|
||||
76
vendor/symfony/http-foundation/CHANGELOG.md
vendored
76
vendor/symfony/http-foundation/CHANGELOG.md
vendored
@@ -1,6 +1,80 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
5.4
|
||||
---
|
||||
|
||||
* Deprecate passing `null` as `$requestIp` to `IpUtils::__checkIp()`, `IpUtils::__checkIp4()` or `IpUtils::__checkIp6()`, pass an empty string instead.
|
||||
* Add the `litespeed_finish_request` method to work with Litespeed
|
||||
* Deprecate `upload_progress.*` and `url_rewriter.tags` session options
|
||||
* Allow setting session options via DSN
|
||||
|
||||
5.3
|
||||
---
|
||||
|
||||
* Add the `SessionFactory`, `NativeSessionStorageFactory`, `PhpBridgeSessionStorageFactory` and `MockFileSessionStorageFactory` classes
|
||||
* Calling `Request::getSession()` when there is no available session throws a `SessionNotFoundException`
|
||||
* Add the `RequestStack::getSession` method
|
||||
* Deprecate the `NamespacedAttributeBag` class
|
||||
* Add `ResponseFormatSame` PHPUnit constraint
|
||||
* Deprecate the `RequestStack::getMasterRequest()` method and add `getMainRequest()` as replacement
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
||||
* added support for `X-Forwarded-Prefix` header
|
||||
* added `HeaderUtils::parseQuery()`: it does the same as `parse_str()` but preserves dots in variable names
|
||||
* added `File::getContent()`
|
||||
* added ability to use comma separated ip addresses for `RequestMatcher::matchIps()`
|
||||
* added `Request::toArray()` to parse a JSON request body to an array
|
||||
* added `RateLimiter\RequestRateLimiterInterface` and `RateLimiter\AbstractRequestRateLimiter`
|
||||
* deprecated not passing a `Closure` together with `FILTER_CALLBACK` to `ParameterBag::filter()`; wrap your filter in a closure instead.
|
||||
* Deprecated the `Request::HEADER_X_FORWARDED_ALL` constant, use either `HEADER_X_FORWARDED_FOR | HEADER_X_FORWARDED_HOST | HEADER_X_FORWARDED_PORT | HEADER_X_FORWARDED_PROTO` or `HEADER_X_FORWARDED_AWS_ELB` or `HEADER_X_FORWARDED_TRAEFIK` constants instead.
|
||||
* Deprecated `BinaryFileResponse::create()`, use `__construct()` instead
|
||||
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* added `Cookie::withValue`, `Cookie::withDomain`, `Cookie::withExpires`,
|
||||
`Cookie::withPath`, `Cookie::withSecure`, `Cookie::withHttpOnly`,
|
||||
`Cookie::withRaw`, `Cookie::withSameSite`
|
||||
* Deprecate `Response::create()`, `JsonResponse::create()`,
|
||||
`RedirectResponse::create()`, and `StreamedResponse::create()` methods (use
|
||||
`__construct()` instead)
|
||||
* added `Request::preferSafeContent()` and `Response::setContentSafe()` to handle "safe" HTTP preference
|
||||
according to [RFC 8674](https://tools.ietf.org/html/rfc8674)
|
||||
* made the Mime component an optional dependency
|
||||
* added `MarshallingSessionHandler`, `IdentityMarshaller`
|
||||
* made `Session` accept a callback to report when the session is being used
|
||||
* Add support for all core cache control directives
|
||||
* Added `Symfony\Component\HttpFoundation\InputBag`
|
||||
* Deprecated retrieving non-string values using `InputBag::get()`, use `InputBag::all()` if you need access to the collection of values
|
||||
|
||||
5.0.0
|
||||
-----
|
||||
|
||||
* made `Cookie` auto-secure and lax by default
|
||||
* removed classes in the `MimeType` namespace, use the Symfony Mime component instead
|
||||
* removed method `UploadedFile::getClientSize()` and the related constructor argument
|
||||
* made `Request::getSession()` throw if the session has not been set before
|
||||
* removed `Response::HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL`
|
||||
* passing a null url when instantiating a `RedirectResponse` is not allowed
|
||||
|
||||
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
|
||||
-----
|
||||
|
||||
@@ -78,7 +152,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
|
||||
|
||||
174
vendor/symfony/http-foundation/Cookie.php
vendored
174
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,23 +29,21 @@ class Cookie
|
||||
protected $path;
|
||||
protected $secure;
|
||||
protected $httpOnly;
|
||||
|
||||
private $raw;
|
||||
private $sameSite;
|
||||
private $secureDefault = false;
|
||||
|
||||
const SAMESITE_NONE = 'none';
|
||||
const SAMESITE_LAX = 'lax';
|
||||
const SAMESITE_STRICT = 'strict';
|
||||
private const RESERVED_CHARS_LIST = "=,; \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.
|
||||
*
|
||||
* @param string $cookie
|
||||
* @param bool $decode
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function fromString($cookie, $decode = false)
|
||||
public static function fromString(string $cookie, bool $decode = false)
|
||||
{
|
||||
$data = [
|
||||
'expires' => 0,
|
||||
@@ -60,8 +62,9 @@ class Cookie
|
||||
$value = isset($part[1]) ? ($decode ? urldecode($part[1]) : $part[1]) : null;
|
||||
|
||||
$data = HeaderUtils::combine($parts) + $data;
|
||||
$data['expires'] = self::expiresTimestamp($data['expires']);
|
||||
|
||||
if (isset($data['max-age'])) {
|
||||
if (isset($data['max-age']) && ($data['max-age'] > 0 || $data['expires'] > time())) {
|
||||
$data['expires'] = time() + (int) $data['max-age'];
|
||||
}
|
||||
|
||||
@@ -86,14 +89,10 @@ class Cookie
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null)
|
||||
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = 'lax')
|
||||
{
|
||||
if (9 > \func_num_args()) {
|
||||
@trigger_error(sprintf('The default value of the "$secure" and "$samesite" arguments of "%s"\'s constructor will respectively change from "false" to "null" and from "null" to "lax" in Symfony 5.0, you should define their values explicitly or use "Cookie::create()" instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
// from PHP source code
|
||||
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
|
||||
if ($raw && false !== strpbrk($name, self::RESERVED_CHARS_LIST)) {
|
||||
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
|
||||
}
|
||||
|
||||
@@ -101,6 +100,65 @@ class Cookie
|
||||
throw new \InvalidArgumentException('The cookie name cannot be empty.');
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
$this->domain = $domain;
|
||||
$this->expire = self::expiresTimestamp($expire);
|
||||
$this->path = empty($path) ? '/' : $path;
|
||||
$this->secure = $secure;
|
||||
$this->httpOnly = $httpOnly;
|
||||
$this->raw = $raw;
|
||||
$this->sameSite = $this->withSameSite($sameSite)->sameSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie copy with a new value.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function withValue(?string $value): self
|
||||
{
|
||||
$cookie = clone $this;
|
||||
$cookie->value = $value;
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie copy with a new domain that the cookie is available to.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function withDomain(?string $domain): self
|
||||
{
|
||||
$cookie = clone $this;
|
||||
$cookie->domain = $domain;
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie copy with a new time the cookie expires.
|
||||
*
|
||||
* @param int|string|\DateTimeInterface $expire
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function withExpires($expire = 0): self
|
||||
{
|
||||
$cookie = clone $this;
|
||||
$cookie->expire = self::expiresTimestamp($expire);
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts expires formats to a unix timestamp.
|
||||
*
|
||||
* @param int|string|\DateTimeInterface $expire
|
||||
*/
|
||||
private static function expiresTimestamp($expire = 0): int
|
||||
{
|
||||
// convert expiration time to a Unix timestamp
|
||||
if ($expire instanceof \DateTimeInterface) {
|
||||
$expire = $expire->format('U');
|
||||
@@ -112,15 +170,72 @@ class Cookie
|
||||
}
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
$this->domain = $domain;
|
||||
$this->expire = 0 < $expire ? (int) $expire : 0;
|
||||
$this->path = empty($path) ? '/' : $path;
|
||||
$this->secure = $secure;
|
||||
$this->httpOnly = $httpOnly;
|
||||
$this->raw = $raw;
|
||||
return 0 < $expire ? (int) $expire : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie copy with a new path on the server in which the cookie will be available on.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function withPath(string $path): self
|
||||
{
|
||||
$cookie = clone $this;
|
||||
$cookie->path = '' === $path ? '/' : $path;
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie copy that only be transmitted over a secure HTTPS connection from the client.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function withSecure(bool $secure = true): self
|
||||
{
|
||||
$cookie = clone $this;
|
||||
$cookie->secure = $secure;
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie copy that be accessible only through the HTTP protocol.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function withHttpOnly(bool $httpOnly = true): self
|
||||
{
|
||||
$cookie = clone $this;
|
||||
$cookie->httpOnly = $httpOnly;
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie copy that uses no url encoding.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function withRaw(bool $raw = true): self
|
||||
{
|
||||
if ($raw && false !== strpbrk($this->name, self::RESERVED_CHARS_LIST)) {
|
||||
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $this->name));
|
||||
}
|
||||
|
||||
$cookie = clone $this;
|
||||
$cookie->raw = $raw;
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cookie copy with SameSite attribute.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function withSameSite(?string $sameSite): self
|
||||
{
|
||||
if ('' === $sameSite) {
|
||||
$sameSite = null;
|
||||
} elseif (null !== $sameSite) {
|
||||
@@ -131,17 +246,26 @@ class Cookie
|
||||
throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
|
||||
}
|
||||
|
||||
$this->sameSite = $sameSite;
|
||||
$cookie = clone $this;
|
||||
$cookie->sameSite = $sameSite;
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cookie as a string.
|
||||
*
|
||||
* @return string The cookie
|
||||
* @return string
|
||||
*/
|
||||
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';
|
||||
|
||||
19
vendor/symfony/http-foundation/Exception/BadRequestException.php
vendored
Normal file
19
vendor/symfony/http-foundation/Exception/BadRequestException.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Raised when a user sends a malformed request.
|
||||
*/
|
||||
class BadRequestException extends \UnexpectedValueException implements RequestExceptionInterface
|
||||
{
|
||||
}
|
||||
21
vendor/symfony/http-foundation/Exception/JsonException.php
vendored
Normal file
21
vendor/symfony/http-foundation/Exception/JsonException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Thrown by Request::toArray() when the content cannot be JSON-decoded.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
final class JsonException extends \UnexpectedValueException implements RequestExceptionInterface
|
||||
{
|
||||
}
|
||||
27
vendor/symfony/http-foundation/Exception/SessionNotFoundException.php
vendored
Normal file
27
vendor/symfony/http-foundation/Exception/SessionNotFoundException.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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\Exception;
|
||||
|
||||
/**
|
||||
* Raised when a session does not exists. This happens in the following cases:
|
||||
* - the session is not enabled
|
||||
* - attempt to read a session outside a request context (ie. cli script).
|
||||
*
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class SessionNotFoundException extends \LogicException implements RequestExceptionInterface
|
||||
{
|
||||
public function __construct(string $message = 'There is currently no session available.', int $code = 0, \Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -15,6 +15,6 @@ class UnexpectedTypeException extends FileException
|
||||
{
|
||||
public function __construct($value, string $expectedType)
|
||||
{
|
||||
parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, \is_object($value) ? \get_class($value) : \gettype($value)));
|
||||
parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, get_debug_type($value)));
|
||||
}
|
||||
}
|
||||
|
||||
54
vendor/symfony/http-foundation/File/File.php
vendored
54
vendor/symfony/http-foundation/File/File.php
vendored
@@ -47,13 +47,17 @@ class File extends \SplFileInfo
|
||||
* This method uses the mime type as guessed by getMimeType()
|
||||
* to guess the file extension.
|
||||
*
|
||||
* @return string|null The guessed extension or null if it cannot be guessed
|
||||
* @return string|null
|
||||
*
|
||||
* @see MimeTypes
|
||||
* @see getMimeType()
|
||||
*/
|
||||
public function guessExtension()
|
||||
{
|
||||
if (!class_exists(MimeTypes::class)) {
|
||||
throw new \LogicException('You cannot guess the extension as the Mime component is not installed. Try running "composer require symfony/mime".');
|
||||
}
|
||||
|
||||
return MimeTypes::getDefault()->getExtensions($this->getMimeType())[0] ?? null;
|
||||
}
|
||||
|
||||
@@ -64,34 +68,38 @@ class File extends \SplFileInfo
|
||||
* 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")
|
||||
* @return string|null
|
||||
*
|
||||
* @see MimeTypes
|
||||
*/
|
||||
public function getMimeType()
|
||||
{
|
||||
if (!class_exists(MimeTypes::class)) {
|
||||
throw new \LogicException('You cannot guess the mime type as the Mime component is not installed. Try running "composer require symfony/mime".');
|
||||
}
|
||||
|
||||
return MimeTypes::getDefault()->guessMimeType($this->getPathname());
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the file to a new location.
|
||||
*
|
||||
* @param string $directory The destination folder
|
||||
* @param string $name The new file name
|
||||
*
|
||||
* @return self A File object representing the new file
|
||||
* @return self
|
||||
*
|
||||
* @throws FileException if the target file could not be created
|
||||
*/
|
||||
public function move($directory, $name = null)
|
||||
public function move(string $directory, string $name = null)
|
||||
{
|
||||
$target = $this->getTargetFile($directory, $name);
|
||||
|
||||
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
|
||||
$renamed = rename($this->getPathname(), $target);
|
||||
restore_error_handler();
|
||||
try {
|
||||
$renamed = rename($this->getPathname(), $target);
|
||||
} finally {
|
||||
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());
|
||||
@@ -99,14 +107,28 @@ class File extends \SplFileInfo
|
||||
return $target;
|
||||
}
|
||||
|
||||
protected function getTargetFile($directory, $name = null)
|
||||
public function getContent(): string
|
||||
{
|
||||
$content = file_get_contents($this->getPathname());
|
||||
|
||||
if (false === $content) {
|
||||
throw new FileException(sprintf('Could not get the content of the file "%s".', $this->getPathname()));
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
protected function getTargetFile(string $directory, string $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));
|
||||
@@ -117,11 +139,9 @@ class File extends \SplFileInfo
|
||||
/**
|
||||
* Returns locale independent base name of the given path.
|
||||
*
|
||||
* @param string $name The new file name
|
||||
*
|
||||
* @return string containing
|
||||
* @return string
|
||||
*/
|
||||
protected function getName($name)
|
||||
protected function getName(string $name)
|
||||
{
|
||||
$originalName = str_replace('\\', '/', $name);
|
||||
$pos = strrpos($originalName, '/');
|
||||
|
||||
@@ -1,100 +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\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.
|
||||
*
|
||||
* A default guesser is provided.
|
||||
* You can register custom guessers by calling the register()
|
||||
* method on the singleton instance:
|
||||
*
|
||||
* $guesser = ExtensionGuesser::getInstance();
|
||||
* $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
|
||||
{
|
||||
/**
|
||||
* The singleton instance.
|
||||
*
|
||||
* @var ExtensionGuesser
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* All registered ExtensionGuesserInterface instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guessers = [];
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all natively provided extension guessers.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
$this->register(new MimeTypeExtensionGuesser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new extension guesser.
|
||||
*
|
||||
* When guessing, this guesser is preferred over previously registered ones.
|
||||
*/
|
||||
public function register(ExtensionGuesserInterface $guesser)
|
||||
{
|
||||
array_unshift($this->guessers, $guesser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to guess the extension.
|
||||
*
|
||||
* The mime type is passed to each registered mime type guesser in reverse order
|
||||
* of their registration (last registered is queried first). Once a guesser
|
||||
* returns a value that is not NULL, this method terminates and returns the
|
||||
* value.
|
||||
*
|
||||
* @param string $mimeType The mime type
|
||||
*
|
||||
* @return string The guessed extension or NULL, if none could be guessed
|
||||
*/
|
||||
public function guess($mimeType)
|
||||
{
|
||||
foreach ($this->guessers as $guesser) {
|
||||
if (null !== $extension = $guesser->guess($mimeType)) {
|
||||
return $extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +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\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
|
||||
{
|
||||
/**
|
||||
* Makes a best guess for a file extension, given a mime type.
|
||||
*
|
||||
* @param string $mimeType The mime type
|
||||
*
|
||||
* @return string The guessed extension or NULL, if none could be guessed
|
||||
*/
|
||||
public function guess($mimeType);
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\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
|
||||
{
|
||||
private $cmd;
|
||||
|
||||
/**
|
||||
* The $cmd pattern must contain a "%s" string that will be replaced
|
||||
* with the file name to guess.
|
||||
*
|
||||
* The command output must start with the mime type of the file.
|
||||
*
|
||||
* @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')
|
||||
{
|
||||
$this->cmd = $cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this guesser is supported on the current OS.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported()
|
||||
{
|
||||
static $supported = null;
|
||||
|
||||
if (null !== $supported) {
|
||||
return $supported;
|
||||
}
|
||||
|
||||
if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('passthru') || !\function_exists('escapeshellarg')) {
|
||||
return $supported = false;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
passthru('command -v file', $exitStatus);
|
||||
$binPath = trim(ob_get_clean());
|
||||
|
||||
return $supported = 0 === $exitStatus && '' !== $binPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function guess($path)
|
||||
{
|
||||
if (!is_file($path)) {
|
||||
throw new FileNotFoundException($path);
|
||||
}
|
||||
|
||||
if (!is_readable($path)) {
|
||||
throw new AccessDeniedException($path);
|
||||
}
|
||||
|
||||
if (!self::isSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
// need to use --mime instead of -i. see #6641
|
||||
passthru(sprintf($this->cmd, escapeshellarg($path)), $return);
|
||||
if ($return > 0) {
|
||||
ob_end_clean();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$type = trim(ob_get_clean());
|
||||
|
||||
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
|
||||
// it's not a type, but an error message
|
||||
return;
|
||||
}
|
||||
|
||||
return $match[1];
|
||||
}
|
||||
}
|
||||
@@ -1,74 +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\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
|
||||
{
|
||||
private $magicFile;
|
||||
|
||||
/**
|
||||
* @param string $magicFile A magic file to use with the finfo instance
|
||||
*
|
||||
* @see http://www.php.net/manual/en/function.finfo-open.php
|
||||
*/
|
||||
public function __construct(string $magicFile = null)
|
||||
{
|
||||
$this->magicFile = $magicFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this guesser is supported on the current OS/PHP setup.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupported()
|
||||
{
|
||||
return \function_exists('finfo_open');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function guess($path)
|
||||
{
|
||||
if (!is_file($path)) {
|
||||
throw new FileNotFoundException($path);
|
||||
}
|
||||
|
||||
if (!is_readable($path)) {
|
||||
throw new AccessDeniedException($path);
|
||||
}
|
||||
|
||||
if (!self::isSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $finfo->file($path);
|
||||
}
|
||||
}
|
||||
@@ -1,826 +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\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
|
||||
{
|
||||
/**
|
||||
* 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 2019-01-14.
|
||||
*
|
||||
* @see https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
*/
|
||||
protected $defaultExtensions = [
|
||||
'application/andrew-inset' => 'ez',
|
||||
'application/applixware' => 'aw',
|
||||
'application/atom+xml' => 'atom',
|
||||
'application/atomcat+xml' => 'atomcat',
|
||||
'application/atomsvc+xml' => 'atomsvc',
|
||||
'application/ccxml+xml' => 'ccxml',
|
||||
'application/cdmi-capability' => 'cdmia',
|
||||
'application/cdmi-container' => 'cdmic',
|
||||
'application/cdmi-domain' => 'cdmid',
|
||||
'application/cdmi-object' => 'cdmio',
|
||||
'application/cdmi-queue' => 'cdmiq',
|
||||
'application/cu-seeme' => 'cu',
|
||||
'application/davmount+xml' => 'davmount',
|
||||
'application/docbook+xml' => 'dbk',
|
||||
'application/dssc+der' => 'dssc',
|
||||
'application/dssc+xml' => 'xdssc',
|
||||
'application/ecmascript' => 'ecma',
|
||||
'application/emma+xml' => 'emma',
|
||||
'application/epub+zip' => 'epub',
|
||||
'application/exi' => 'exi',
|
||||
'application/font-tdpfr' => 'pfr',
|
||||
'application/gml+xml' => 'gml',
|
||||
'application/gpx+xml' => 'gpx',
|
||||
'application/gxf' => 'gxf',
|
||||
'application/hyperstudio' => 'stk',
|
||||
'application/inkml+xml' => 'ink',
|
||||
'application/ipfix' => 'ipfix',
|
||||
'application/java-archive' => 'jar',
|
||||
'application/java-serialized-object' => 'ser',
|
||||
'application/java-vm' => 'class',
|
||||
'application/javascript' => 'js',
|
||||
'application/json' => 'json',
|
||||
'application/jsonml+json' => 'jsonml',
|
||||
'application/lost+xml' => 'lostxml',
|
||||
'application/mac-binhex40' => 'hqx',
|
||||
'application/mac-compactpro' => 'cpt',
|
||||
'application/mads+xml' => 'mads',
|
||||
'application/marc' => 'mrc',
|
||||
'application/marcxml+xml' => 'mrcx',
|
||||
'application/mathematica' => 'ma',
|
||||
'application/mathml+xml' => 'mathml',
|
||||
'application/mbox' => 'mbox',
|
||||
'application/mediaservercontrol+xml' => 'mscml',
|
||||
'application/metalink+xml' => 'metalink',
|
||||
'application/metalink4+xml' => 'meta4',
|
||||
'application/mets+xml' => 'mets',
|
||||
'application/mods+xml' => 'mods',
|
||||
'application/mp21' => 'm21',
|
||||
'application/mp4' => 'mp4s',
|
||||
'application/msword' => 'doc',
|
||||
'application/mxf' => 'mxf',
|
||||
'application/octet-stream' => 'bin',
|
||||
'application/oda' => 'oda',
|
||||
'application/oebps-package+xml' => 'opf',
|
||||
'application/ogg' => 'ogx',
|
||||
'application/omdoc+xml' => 'omdoc',
|
||||
'application/onenote' => 'onetoc',
|
||||
'application/oxps' => 'oxps',
|
||||
'application/patch-ops-error+xml' => 'xer',
|
||||
'application/pdf' => 'pdf',
|
||||
'application/pgp-encrypted' => 'pgp',
|
||||
'application/pgp-signature' => 'asc',
|
||||
'application/pics-rules' => 'prf',
|
||||
'application/pkcs10' => 'p10',
|
||||
'application/pkcs7-mime' => 'p7m',
|
||||
'application/pkcs7-signature' => 'p7s',
|
||||
'application/pkcs8' => 'p8',
|
||||
'application/pkix-attr-cert' => 'ac',
|
||||
'application/pkix-cert' => 'cer',
|
||||
'application/pkix-crl' => 'crl',
|
||||
'application/pkix-pkipath' => 'pkipath',
|
||||
'application/pkixcmp' => 'pki',
|
||||
'application/pls+xml' => 'pls',
|
||||
'application/postscript' => 'ai',
|
||||
'application/prs.cww' => 'cww',
|
||||
'application/pskc+xml' => 'pskcxml',
|
||||
'application/rdf+xml' => 'rdf',
|
||||
'application/reginfo+xml' => 'rif',
|
||||
'application/relax-ng-compact-syntax' => 'rnc',
|
||||
'application/resource-lists+xml' => 'rl',
|
||||
'application/resource-lists-diff+xml' => 'rld',
|
||||
'application/rls-services+xml' => 'rs',
|
||||
'application/rpki-ghostbusters' => 'gbr',
|
||||
'application/rpki-manifest' => 'mft',
|
||||
'application/rpki-roa' => 'roa',
|
||||
'application/rsd+xml' => 'rsd',
|
||||
'application/rss+xml' => 'rss',
|
||||
'application/rtf' => 'rtf',
|
||||
'application/sbml+xml' => 'sbml',
|
||||
'application/scvp-cv-request' => 'scq',
|
||||
'application/scvp-cv-response' => 'scs',
|
||||
'application/scvp-vp-request' => 'spq',
|
||||
'application/scvp-vp-response' => 'spp',
|
||||
'application/sdp' => 'sdp',
|
||||
'application/set-payment-initiation' => 'setpay',
|
||||
'application/set-registration-initiation' => 'setreg',
|
||||
'application/shf+xml' => 'shf',
|
||||
'application/smil+xml' => 'smi',
|
||||
'application/sparql-query' => 'rq',
|
||||
'application/sparql-results+xml' => 'srx',
|
||||
'application/srgs' => 'gram',
|
||||
'application/srgs+xml' => 'grxml',
|
||||
'application/sru+xml' => 'sru',
|
||||
'application/ssdl+xml' => 'ssdl',
|
||||
'application/ssml+xml' => 'ssml',
|
||||
'application/tei+xml' => 'tei',
|
||||
'application/thraud+xml' => 'tfi',
|
||||
'application/timestamped-data' => 'tsd',
|
||||
'application/vnd.3gpp.pic-bw-large' => 'plb',
|
||||
'application/vnd.3gpp.pic-bw-small' => 'psb',
|
||||
'application/vnd.3gpp.pic-bw-var' => 'pvb',
|
||||
'application/vnd.3gpp2.tcap' => 'tcap',
|
||||
'application/vnd.3m.post-it-notes' => 'pwn',
|
||||
'application/vnd.accpac.simply.aso' => 'aso',
|
||||
'application/vnd.accpac.simply.imp' => 'imp',
|
||||
'application/vnd.acucobol' => 'acu',
|
||||
'application/vnd.acucorp' => 'atc',
|
||||
'application/vnd.adobe.air-application-installer-package+zip' => 'air',
|
||||
'application/vnd.adobe.formscentral.fcdt' => 'fcdt',
|
||||
'application/vnd.adobe.fxp' => 'fxp',
|
||||
'application/vnd.adobe.xdp+xml' => 'xdp',
|
||||
'application/vnd.adobe.xfdf' => 'xfdf',
|
||||
'application/vnd.ahead.space' => 'ahead',
|
||||
'application/vnd.airzip.filesecure.azf' => 'azf',
|
||||
'application/vnd.airzip.filesecure.azs' => 'azs',
|
||||
'application/vnd.amazon.ebook' => 'azw',
|
||||
'application/vnd.americandynamics.acc' => 'acc',
|
||||
'application/vnd.amiga.ami' => 'ami',
|
||||
'application/vnd.android.package-archive' => 'apk',
|
||||
'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
|
||||
'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
|
||||
'application/vnd.antix.game-component' => 'atx',
|
||||
'application/vnd.apple.installer+xml' => 'mpkg',
|
||||
'application/vnd.apple.mpegurl' => 'm3u8',
|
||||
'application/vnd.aristanetworks.swi' => 'swi',
|
||||
'application/vnd.astraea-software.iota' => 'iota',
|
||||
'application/vnd.audiograph' => 'aep',
|
||||
'application/vnd.blueice.multipass' => 'mpm',
|
||||
'application/vnd.bmi' => 'bmi',
|
||||
'application/vnd.businessobjects' => 'rep',
|
||||
'application/vnd.chemdraw+xml' => 'cdxml',
|
||||
'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
|
||||
'application/vnd.cinderella' => 'cdy',
|
||||
'application/vnd.claymore' => 'cla',
|
||||
'application/vnd.cloanto.rp9' => 'rp9',
|
||||
'application/vnd.clonk.c4group' => 'c4g',
|
||||
'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
|
||||
'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
|
||||
'application/vnd.commonspace' => 'csp',
|
||||
'application/vnd.contact.cmsg' => 'cdbcmsg',
|
||||
'application/vnd.cosmocaller' => 'cmc',
|
||||
'application/vnd.crick.clicker' => 'clkx',
|
||||
'application/vnd.crick.clicker.keyboard' => 'clkk',
|
||||
'application/vnd.crick.clicker.palette' => 'clkp',
|
||||
'application/vnd.crick.clicker.template' => 'clkt',
|
||||
'application/vnd.crick.clicker.wordbank' => 'clkw',
|
||||
'application/vnd.criticaltools.wbs+xml' => 'wbs',
|
||||
'application/vnd.ctc-posml' => 'pml',
|
||||
'application/vnd.cups-ppd' => 'ppd',
|
||||
'application/vnd.curl.car' => 'car',
|
||||
'application/vnd.curl.pcurl' => 'pcurl',
|
||||
'application/vnd.dart' => 'dart',
|
||||
'application/vnd.data-vision.rdz' => 'rdz',
|
||||
'application/vnd.dece.data' => 'uvf',
|
||||
'application/vnd.dece.ttml+xml' => 'uvt',
|
||||
'application/vnd.dece.unspecified' => 'uvx',
|
||||
'application/vnd.dece.zip' => 'uvz',
|
||||
'application/vnd.denovo.fcselayout-link' => 'fe_launch',
|
||||
'application/vnd.dna' => 'dna',
|
||||
'application/vnd.dolby.mlp' => 'mlp',
|
||||
'application/vnd.dpgraph' => 'dpg',
|
||||
'application/vnd.dreamfactory' => 'dfac',
|
||||
'application/vnd.ds-keypoint' => 'kpxx',
|
||||
'application/vnd.dvb.ait' => 'ait',
|
||||
'application/vnd.dvb.service' => 'svc',
|
||||
'application/vnd.dynageo' => 'geo',
|
||||
'application/vnd.ecowin.chart' => 'mag',
|
||||
'application/vnd.enliven' => 'nml',
|
||||
'application/vnd.epson.esf' => 'esf',
|
||||
'application/vnd.epson.msf' => 'msf',
|
||||
'application/vnd.epson.quickanime' => 'qam',
|
||||
'application/vnd.epson.salt' => 'slt',
|
||||
'application/vnd.epson.ssf' => 'ssf',
|
||||
'application/vnd.eszigno3+xml' => 'es3',
|
||||
'application/vnd.ezpix-album' => 'ez2',
|
||||
'application/vnd.ezpix-package' => 'ez3',
|
||||
'application/vnd.fdf' => 'fdf',
|
||||
'application/vnd.fdsn.mseed' => 'mseed',
|
||||
'application/vnd.fdsn.seed' => 'seed',
|
||||
'application/vnd.flographit' => 'gph',
|
||||
'application/vnd.fluxtime.clip' => 'ftc',
|
||||
'application/vnd.framemaker' => 'fm',
|
||||
'application/vnd.frogans.fnc' => 'fnc',
|
||||
'application/vnd.frogans.ltf' => 'ltf',
|
||||
'application/vnd.fsc.weblaunch' => 'fsc',
|
||||
'application/vnd.fujitsu.oasys' => 'oas',
|
||||
'application/vnd.fujitsu.oasys2' => 'oa2',
|
||||
'application/vnd.fujitsu.oasys3' => 'oa3',
|
||||
'application/vnd.fujitsu.oasysgp' => 'fg5',
|
||||
'application/vnd.fujitsu.oasysprs' => 'bh2',
|
||||
'application/vnd.fujixerox.ddd' => 'ddd',
|
||||
'application/vnd.fujixerox.docuworks' => 'xdw',
|
||||
'application/vnd.fujixerox.docuworks.binder' => 'xbd',
|
||||
'application/vnd.fuzzysheet' => 'fzs',
|
||||
'application/vnd.genomatix.tuxedo' => 'txd',
|
||||
'application/vnd.geogebra.file' => 'ggb',
|
||||
'application/vnd.geogebra.tool' => 'ggt',
|
||||
'application/vnd.geometry-explorer' => 'gex',
|
||||
'application/vnd.geonext' => 'gxt',
|
||||
'application/vnd.geoplan' => 'g2w',
|
||||
'application/vnd.geospace' => 'g3w',
|
||||
'application/vnd.gmx' => 'gmx',
|
||||
'application/vnd.google-earth.kml+xml' => 'kml',
|
||||
'application/vnd.google-earth.kmz' => 'kmz',
|
||||
'application/vnd.grafeq' => 'gqf',
|
||||
'application/vnd.groove-account' => 'gac',
|
||||
'application/vnd.groove-help' => 'ghf',
|
||||
'application/vnd.groove-identity-message' => 'gim',
|
||||
'application/vnd.groove-injector' => 'grv',
|
||||
'application/vnd.groove-tool-message' => 'gtm',
|
||||
'application/vnd.groove-tool-template' => 'tpl',
|
||||
'application/vnd.groove-vcard' => 'vcg',
|
||||
'application/vnd.hal+xml' => 'hal',
|
||||
'application/vnd.handheld-entertainment+xml' => 'zmm',
|
||||
'application/vnd.hbci' => 'hbci',
|
||||
'application/vnd.hhe.lesson-player' => 'les',
|
||||
'application/vnd.hp-hpgl' => 'hpgl',
|
||||
'application/vnd.hp-hpid' => 'hpid',
|
||||
'application/vnd.hp-hps' => 'hps',
|
||||
'application/vnd.hp-jlyt' => 'jlt',
|
||||
'application/vnd.hp-pcl' => 'pcl',
|
||||
'application/vnd.hp-pclxl' => 'pclxl',
|
||||
'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
|
||||
'application/vnd.ibm.minipay' => 'mpy',
|
||||
'application/vnd.ibm.modcap' => 'afp',
|
||||
'application/vnd.ibm.rights-management' => 'irm',
|
||||
'application/vnd.ibm.secure-container' => 'sc',
|
||||
'application/vnd.iccprofile' => 'icc',
|
||||
'application/vnd.igloader' => 'igl',
|
||||
'application/vnd.immervision-ivp' => 'ivp',
|
||||
'application/vnd.immervision-ivu' => 'ivu',
|
||||
'application/vnd.insors.igm' => 'igm',
|
||||
'application/vnd.intercon.formnet' => 'xpw',
|
||||
'application/vnd.intergeo' => 'i2g',
|
||||
'application/vnd.intu.qbo' => 'qbo',
|
||||
'application/vnd.intu.qfx' => 'qfx',
|
||||
'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
|
||||
'application/vnd.irepository.package+xml' => 'irp',
|
||||
'application/vnd.is-xpr' => 'xpr',
|
||||
'application/vnd.isac.fcs' => 'fcs',
|
||||
'application/vnd.jam' => 'jam',
|
||||
'application/vnd.jcp.javame.midlet-rms' => 'rms',
|
||||
'application/vnd.jisp' => 'jisp',
|
||||
'application/vnd.joost.joda-archive' => 'joda',
|
||||
'application/vnd.kahootz' => 'ktz',
|
||||
'application/vnd.kde.karbon' => 'karbon',
|
||||
'application/vnd.kde.kchart' => 'chrt',
|
||||
'application/vnd.kde.kformula' => 'kfo',
|
||||
'application/vnd.kde.kivio' => 'flw',
|
||||
'application/vnd.kde.kontour' => 'kon',
|
||||
'application/vnd.kde.kpresenter' => 'kpr',
|
||||
'application/vnd.kde.kspread' => 'ksp',
|
||||
'application/vnd.kde.kword' => 'kwd',
|
||||
'application/vnd.kenameaapp' => 'htke',
|
||||
'application/vnd.kidspiration' => 'kia',
|
||||
'application/vnd.kinar' => 'kne',
|
||||
'application/vnd.koan' => 'skp',
|
||||
'application/vnd.kodak-descriptor' => 'sse',
|
||||
'application/vnd.las.las+xml' => 'lasxml',
|
||||
'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
|
||||
'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
|
||||
'application/vnd.lotus-1-2-3' => '123',
|
||||
'application/vnd.lotus-approach' => 'apr',
|
||||
'application/vnd.lotus-freelance' => 'pre',
|
||||
'application/vnd.lotus-notes' => 'nsf',
|
||||
'application/vnd.lotus-organizer' => 'org',
|
||||
'application/vnd.lotus-screencam' => 'scm',
|
||||
'application/vnd.lotus-wordpro' => 'lwp',
|
||||
'application/vnd.macports.portpkg' => 'portpkg',
|
||||
'application/vnd.mcd' => 'mcd',
|
||||
'application/vnd.medcalcdata' => 'mc1',
|
||||
'application/vnd.mediastation.cdkey' => 'cdkey',
|
||||
'application/vnd.mfer' => 'mwf',
|
||||
'application/vnd.mfmp' => 'mfm',
|
||||
'application/vnd.micrografx.flo' => 'flo',
|
||||
'application/vnd.micrografx.igx' => 'igx',
|
||||
'application/vnd.mif' => 'mif',
|
||||
'application/vnd.mobius.daf' => 'daf',
|
||||
'application/vnd.mobius.dis' => 'dis',
|
||||
'application/vnd.mobius.mbk' => 'mbk',
|
||||
'application/vnd.mobius.mqy' => 'mqy',
|
||||
'application/vnd.mobius.msl' => 'msl',
|
||||
'application/vnd.mobius.plc' => 'plc',
|
||||
'application/vnd.mobius.txf' => 'txf',
|
||||
'application/vnd.mophun.application' => 'mpn',
|
||||
'application/vnd.mophun.certificate' => 'mpc',
|
||||
'application/vnd.mozilla.xul+xml' => 'xul',
|
||||
'application/vnd.ms-artgalry' => 'cil',
|
||||
'application/vnd.ms-cab-compressed' => 'cab',
|
||||
'application/vnd.ms-excel' => 'xls',
|
||||
'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
|
||||
'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
|
||||
'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
|
||||
'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
|
||||
'application/vnd.ms-fontobject' => 'eot',
|
||||
'application/vnd.ms-htmlhelp' => 'chm',
|
||||
'application/vnd.ms-ims' => 'ims',
|
||||
'application/vnd.ms-lrm' => 'lrm',
|
||||
'application/vnd.ms-officetheme' => 'thmx',
|
||||
'application/vnd.ms-pki.seccat' => 'cat',
|
||||
'application/vnd.ms-pki.stl' => 'stl',
|
||||
'application/vnd.ms-powerpoint' => 'ppt',
|
||||
'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
|
||||
'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
|
||||
'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
|
||||
'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
|
||||
'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
|
||||
'application/vnd.ms-project' => 'mpp',
|
||||
'application/vnd.ms-word.document.macroenabled.12' => 'docm',
|
||||
'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
|
||||
'application/vnd.ms-works' => 'wps',
|
||||
'application/vnd.ms-wpl' => 'wpl',
|
||||
'application/vnd.ms-xpsdocument' => 'xps',
|
||||
'application/vnd.mseq' => 'mseq',
|
||||
'application/vnd.musician' => 'mus',
|
||||
'application/vnd.muvee.style' => 'msty',
|
||||
'application/vnd.mynfc' => 'taglet',
|
||||
'application/vnd.neurolanguage.nlu' => 'nlu',
|
||||
'application/vnd.nitf' => 'ntf',
|
||||
'application/vnd.noblenet-directory' => 'nnd',
|
||||
'application/vnd.noblenet-sealer' => 'nns',
|
||||
'application/vnd.noblenet-web' => 'nnw',
|
||||
'application/vnd.nokia.n-gage.data' => 'ngdat',
|
||||
'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
|
||||
'application/vnd.nokia.radio-preset' => 'rpst',
|
||||
'application/vnd.nokia.radio-presets' => 'rpss',
|
||||
'application/vnd.novadigm.edm' => 'edm',
|
||||
'application/vnd.novadigm.edx' => 'edx',
|
||||
'application/vnd.novadigm.ext' => 'ext',
|
||||
'application/vnd.oasis.opendocument.chart' => 'odc',
|
||||
'application/vnd.oasis.opendocument.chart-template' => 'otc',
|
||||
'application/vnd.oasis.opendocument.database' => 'odb',
|
||||
'application/vnd.oasis.opendocument.formula' => 'odf',
|
||||
'application/vnd.oasis.opendocument.formula-template' => 'odft',
|
||||
'application/vnd.oasis.opendocument.graphics' => 'odg',
|
||||
'application/vnd.oasis.opendocument.graphics-template' => 'otg',
|
||||
'application/vnd.oasis.opendocument.image' => 'odi',
|
||||
'application/vnd.oasis.opendocument.image-template' => 'oti',
|
||||
'application/vnd.oasis.opendocument.presentation' => 'odp',
|
||||
'application/vnd.oasis.opendocument.presentation-template' => 'otp',
|
||||
'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
|
||||
'application/vnd.oasis.opendocument.text' => 'odt',
|
||||
'application/vnd.oasis.opendocument.text-master' => 'odm',
|
||||
'application/vnd.oasis.opendocument.text-template' => 'ott',
|
||||
'application/vnd.oasis.opendocument.text-web' => 'oth',
|
||||
'application/vnd.olpc-sugar' => 'xo',
|
||||
'application/vnd.oma.dd2+xml' => 'dd2',
|
||||
'application/vnd.openofficeorg.extension' => 'oxt',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
|
||||
'application/vnd.osgeo.mapguide.package' => 'mgp',
|
||||
'application/vnd.osgi.dp' => 'dp',
|
||||
'application/vnd.osgi.subsystem' => 'esa',
|
||||
'application/vnd.palm' => 'pdb',
|
||||
'application/vnd.pawaafile' => 'paw',
|
||||
'application/vnd.pg.format' => 'str',
|
||||
'application/vnd.pg.osasli' => 'ei6',
|
||||
'application/vnd.picsel' => 'efif',
|
||||
'application/vnd.pmi.widget' => 'wg',
|
||||
'application/vnd.pocketlearn' => 'plf',
|
||||
'application/vnd.powerbuilder6' => 'pbd',
|
||||
'application/vnd.previewsystems.box' => 'box',
|
||||
'application/vnd.proteus.magazine' => 'mgz',
|
||||
'application/vnd.publishare-delta-tree' => 'qps',
|
||||
'application/vnd.pvi.ptid1' => 'ptid',
|
||||
'application/vnd.quark.quarkxpress' => 'qxd',
|
||||
'application/vnd.realvnc.bed' => 'bed',
|
||||
'application/vnd.recordare.musicxml' => 'mxl',
|
||||
'application/vnd.recordare.musicxml+xml' => 'musicxml',
|
||||
'application/vnd.rig.cryptonote' => 'cryptonote',
|
||||
'application/vnd.rim.cod' => 'cod',
|
||||
'application/vnd.rn-realmedia' => 'rm',
|
||||
'application/vnd.rn-realmedia-vbr' => 'rmvb',
|
||||
'application/vnd.route66.link66+xml' => 'link66',
|
||||
'application/vnd.sailingtracker.track' => 'st',
|
||||
'application/vnd.seemail' => 'see',
|
||||
'application/vnd.sema' => 'sema',
|
||||
'application/vnd.semd' => 'semd',
|
||||
'application/vnd.semf' => 'semf',
|
||||
'application/vnd.shana.informed.formdata' => 'ifm',
|
||||
'application/vnd.shana.informed.formtemplate' => 'itp',
|
||||
'application/vnd.shana.informed.interchange' => 'iif',
|
||||
'application/vnd.shana.informed.package' => 'ipk',
|
||||
'application/vnd.simtech-mindmapper' => 'twd',
|
||||
'application/vnd.smaf' => 'mmf',
|
||||
'application/vnd.smart.teacher' => 'teacher',
|
||||
'application/vnd.solent.sdkm+xml' => 'sdkm',
|
||||
'application/vnd.spotfire.dxp' => 'dxp',
|
||||
'application/vnd.spotfire.sfs' => 'sfs',
|
||||
'application/vnd.stardivision.calc' => 'sdc',
|
||||
'application/vnd.stardivision.draw' => 'sda',
|
||||
'application/vnd.stardivision.impress' => 'sdd',
|
||||
'application/vnd.stardivision.math' => 'smf',
|
||||
'application/vnd.stardivision.writer' => 'sdw',
|
||||
'application/vnd.stardivision.writer-global' => 'sgl',
|
||||
'application/vnd.stepmania.package' => 'smzip',
|
||||
'application/vnd.stepmania.stepchart' => 'sm',
|
||||
'application/vnd.sun.xml.calc' => 'sxc',
|
||||
'application/vnd.sun.xml.calc.template' => 'stc',
|
||||
'application/vnd.sun.xml.draw' => 'sxd',
|
||||
'application/vnd.sun.xml.draw.template' => 'std',
|
||||
'application/vnd.sun.xml.impress' => 'sxi',
|
||||
'application/vnd.sun.xml.impress.template' => 'sti',
|
||||
'application/vnd.sun.xml.math' => 'sxm',
|
||||
'application/vnd.sun.xml.writer' => 'sxw',
|
||||
'application/vnd.sun.xml.writer.global' => 'sxg',
|
||||
'application/vnd.sun.xml.writer.template' => 'stw',
|
||||
'application/vnd.sus-calendar' => 'sus',
|
||||
'application/vnd.svd' => 'svd',
|
||||
'application/vnd.symbian.install' => 'sis',
|
||||
'application/vnd.syncml+xml' => 'xsm',
|
||||
'application/vnd.syncml.dm+wbxml' => 'bdm',
|
||||
'application/vnd.syncml.dm+xml' => 'xdm',
|
||||
'application/vnd.tao.intent-module-archive' => 'tao',
|
||||
'application/vnd.tcpdump.pcap' => 'pcap',
|
||||
'application/vnd.tmobile-livetv' => 'tmo',
|
||||
'application/vnd.trid.tpt' => 'tpt',
|
||||
'application/vnd.triscape.mxs' => 'mxs',
|
||||
'application/vnd.trueapp' => 'tra',
|
||||
'application/vnd.ufdl' => 'ufd',
|
||||
'application/vnd.uiq.theme' => 'utz',
|
||||
'application/vnd.umajin' => 'umj',
|
||||
'application/vnd.unity' => 'unityweb',
|
||||
'application/vnd.uoml+xml' => 'uoml',
|
||||
'application/vnd.vcx' => 'vcx',
|
||||
'application/vnd.visio' => 'vsd',
|
||||
'application/vnd.visionary' => 'vis',
|
||||
'application/vnd.vsf' => 'vsf',
|
||||
'application/vnd.wap.wbxml' => 'wbxml',
|
||||
'application/vnd.wap.wmlc' => 'wmlc',
|
||||
'application/vnd.wap.wmlscriptc' => 'wmlsc',
|
||||
'application/vnd.webturbo' => 'wtb',
|
||||
'application/vnd.wolfram.player' => 'nbp',
|
||||
'application/vnd.wordperfect' => 'wpd',
|
||||
'application/vnd.wqd' => 'wqd',
|
||||
'application/vnd.wt.stf' => 'stf',
|
||||
'application/vnd.xara' => 'xar',
|
||||
'application/vnd.xfdl' => 'xfdl',
|
||||
'application/vnd.yamaha.hv-dic' => 'hvd',
|
||||
'application/vnd.yamaha.hv-script' => 'hvs',
|
||||
'application/vnd.yamaha.hv-voice' => 'hvp',
|
||||
'application/vnd.yamaha.openscoreformat' => 'osf',
|
||||
'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
|
||||
'application/vnd.yamaha.smaf-audio' => 'saf',
|
||||
'application/vnd.yamaha.smaf-phrase' => 'spf',
|
||||
'application/vnd.yellowriver-custom-menu' => 'cmp',
|
||||
'application/vnd.zul' => 'zir',
|
||||
'application/vnd.zzazz.deck+xml' => 'zaz',
|
||||
'application/voicexml+xml' => 'vxml',
|
||||
'application/widget' => 'wgt',
|
||||
'application/winhlp' => 'hlp',
|
||||
'application/wsdl+xml' => 'wsdl',
|
||||
'application/wspolicy+xml' => 'wspolicy',
|
||||
'application/x-7z-compressed' => '7z',
|
||||
'application/x-abiword' => 'abw',
|
||||
'application/x-ace-compressed' => 'ace',
|
||||
'application/x-apple-diskimage' => 'dmg',
|
||||
'application/x-authorware-bin' => 'aab',
|
||||
'application/x-authorware-map' => 'aam',
|
||||
'application/x-authorware-seg' => 'aas',
|
||||
'application/x-bcpio' => 'bcpio',
|
||||
'application/x-bittorrent' => 'torrent',
|
||||
'application/x-blorb' => 'blb',
|
||||
'application/x-bzip' => 'bz',
|
||||
'application/x-bzip2' => 'bz2',
|
||||
'application/x-cbr' => 'cbr',
|
||||
'application/x-cdlink' => 'vcd',
|
||||
'application/x-cfs-compressed' => 'cfs',
|
||||
'application/x-chat' => 'chat',
|
||||
'application/x-chess-pgn' => 'pgn',
|
||||
'application/x-conference' => 'nsc',
|
||||
'application/x-cpio' => 'cpio',
|
||||
'application/x-csh' => 'csh',
|
||||
'application/x-debian-package' => 'deb',
|
||||
'application/x-dgc-compressed' => 'dgc',
|
||||
'application/x-director' => 'dir',
|
||||
'application/x-doom' => 'wad',
|
||||
'application/x-dtbncx+xml' => 'ncx',
|
||||
'application/x-dtbook+xml' => 'dtb',
|
||||
'application/x-dtbresource+xml' => 'res',
|
||||
'application/x-dvi' => 'dvi',
|
||||
'application/x-envoy' => 'evy',
|
||||
'application/x-eva' => 'eva',
|
||||
'application/x-font-bdf' => 'bdf',
|
||||
'application/x-font-ghostscript' => 'gsf',
|
||||
'application/x-font-linux-psf' => 'psf',
|
||||
'application/x-font-otf' => 'otf',
|
||||
'application/x-font-pcf' => 'pcf',
|
||||
'application/x-font-snf' => 'snf',
|
||||
'application/x-font-ttf' => 'ttf',
|
||||
'application/x-font-type1' => 'pfa',
|
||||
'application/x-font-woff' => 'woff',
|
||||
'application/x-freearc' => 'arc',
|
||||
'application/x-futuresplash' => 'spl',
|
||||
'application/x-gca-compressed' => 'gca',
|
||||
'application/x-glulx' => 'ulx',
|
||||
'application/x-gnumeric' => 'gnumeric',
|
||||
'application/x-gramps-xml' => 'gramps',
|
||||
'application/x-gtar' => 'gtar',
|
||||
'application/x-hdf' => 'hdf',
|
||||
'application/x-install-instructions' => 'install',
|
||||
'application/x-iso9660-image' => 'iso',
|
||||
'application/x-java-jnlp-file' => 'jnlp',
|
||||
'application/x-latex' => 'latex',
|
||||
'application/x-lzh-compressed' => 'lzh',
|
||||
'application/x-mie' => 'mie',
|
||||
'application/x-mobipocket-ebook' => 'prc',
|
||||
'application/x-ms-application' => 'application',
|
||||
'application/x-ms-shortcut' => 'lnk',
|
||||
'application/x-ms-wmd' => 'wmd',
|
||||
'application/x-ms-wmz' => 'wmz',
|
||||
'application/x-ms-xbap' => 'xbap',
|
||||
'application/x-msaccess' => 'mdb',
|
||||
'application/x-msbinder' => 'obd',
|
||||
'application/x-mscardfile' => 'crd',
|
||||
'application/x-msclip' => 'clp',
|
||||
'application/x-msdownload' => 'exe',
|
||||
'application/x-msmediaview' => 'mvb',
|
||||
'application/x-msmetafile' => 'wmf',
|
||||
'application/x-msmoney' => 'mny',
|
||||
'application/x-mspublisher' => 'pub',
|
||||
'application/x-msschedule' => 'scd',
|
||||
'application/x-msterminal' => 'trm',
|
||||
'application/x-mswrite' => 'wri',
|
||||
'application/x-netcdf' => 'nc',
|
||||
'application/x-nzb' => 'nzb',
|
||||
'application/x-pkcs12' => 'p12',
|
||||
'application/x-pkcs7-certificates' => 'p7b',
|
||||
'application/x-pkcs7-certreqresp' => 'p7r',
|
||||
'application/x-rar-compressed' => 'rar',
|
||||
'application/x-rar' => 'rar',
|
||||
'application/x-research-info-systems' => 'ris',
|
||||
'application/x-sh' => 'sh',
|
||||
'application/x-shar' => 'shar',
|
||||
'application/x-shockwave-flash' => 'swf',
|
||||
'application/x-silverlight-app' => 'xap',
|
||||
'application/x-sql' => 'sql',
|
||||
'application/x-stuffit' => 'sit',
|
||||
'application/x-stuffitx' => 'sitx',
|
||||
'application/x-subrip' => 'srt',
|
||||
'application/x-sv4cpio' => 'sv4cpio',
|
||||
'application/x-sv4crc' => 'sv4crc',
|
||||
'application/x-t3vm-image' => 't3',
|
||||
'application/x-tads' => 'gam',
|
||||
'application/x-tar' => 'tar',
|
||||
'application/x-tcl' => 'tcl',
|
||||
'application/x-tex' => 'tex',
|
||||
'application/x-tex-tfm' => 'tfm',
|
||||
'application/x-texinfo' => 'texinfo',
|
||||
'application/x-tgif' => 'obj',
|
||||
'application/x-ustar' => 'ustar',
|
||||
'application/x-wais-source' => 'src',
|
||||
'application/x-x509-ca-cert' => 'der',
|
||||
'application/x-xfig' => 'fig',
|
||||
'application/x-xliff+xml' => 'xlf',
|
||||
'application/x-xpinstall' => 'xpi',
|
||||
'application/x-xz' => 'xz',
|
||||
'application/x-zip-compressed' => 'zip',
|
||||
'application/x-zmachine' => 'z1',
|
||||
'application/xaml+xml' => 'xaml',
|
||||
'application/xcap-diff+xml' => 'xdf',
|
||||
'application/xenc+xml' => 'xenc',
|
||||
'application/xhtml+xml' => 'xhtml',
|
||||
'application/xml' => 'xml',
|
||||
'application/xml-dtd' => 'dtd',
|
||||
'application/xop+xml' => 'xop',
|
||||
'application/xproc+xml' => 'xpl',
|
||||
'application/xslt+xml' => 'xslt',
|
||||
'application/xspf+xml' => 'xspf',
|
||||
'application/xv+xml' => 'mxml',
|
||||
'application/yang' => 'yang',
|
||||
'application/yin+xml' => 'yin',
|
||||
'application/zip' => 'zip',
|
||||
'audio/adpcm' => 'adp',
|
||||
'audio/basic' => 'au',
|
||||
'audio/midi' => 'mid',
|
||||
'audio/mp4' => 'm4a',
|
||||
'audio/mpeg' => 'mpga',
|
||||
'audio/ogg' => 'oga',
|
||||
'audio/s3m' => 's3m',
|
||||
'audio/silk' => 'sil',
|
||||
'audio/vnd.dece.audio' => 'uva',
|
||||
'audio/vnd.digital-winds' => 'eol',
|
||||
'audio/vnd.dra' => 'dra',
|
||||
'audio/vnd.dts' => 'dts',
|
||||
'audio/vnd.dts.hd' => 'dtshd',
|
||||
'audio/vnd.lucent.voice' => 'lvp',
|
||||
'audio/vnd.ms-playready.media.pya' => 'pya',
|
||||
'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
|
||||
'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
|
||||
'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
|
||||
'audio/vnd.rip' => 'rip',
|
||||
'audio/webm' => 'weba',
|
||||
'audio/x-aac' => 'aac',
|
||||
'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',
|
||||
'audio/x-ms-wma' => 'wma',
|
||||
'audio/x-pn-realaudio' => 'ram',
|
||||
'audio/x-pn-realaudio-plugin' => 'rmp',
|
||||
'audio/x-wav' => 'wav',
|
||||
'audio/xm' => 'xm',
|
||||
'chemical/x-cdx' => 'cdx',
|
||||
'chemical/x-cif' => 'cif',
|
||||
'chemical/x-cmdf' => 'cmdf',
|
||||
'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',
|
||||
'image/g3fax' => 'g3',
|
||||
'image/gif' => 'gif',
|
||||
'image/ief' => 'ief',
|
||||
'image/jpeg' => 'jpeg',
|
||||
'image/pjpeg' => 'jpeg',
|
||||
'image/ktx' => 'ktx',
|
||||
'image/png' => 'png',
|
||||
'image/prs.btif' => 'btif',
|
||||
'image/sgi' => 'sgi',
|
||||
'image/svg+xml' => 'svg',
|
||||
'image/tiff' => 'tiff',
|
||||
'image/vnd.adobe.photoshop' => 'psd',
|
||||
'image/vnd.dece.graphic' => 'uvi',
|
||||
'image/vnd.djvu' => 'djvu',
|
||||
'image/vnd.dvb.subtitle' => 'sub',
|
||||
'image/vnd.dwg' => 'dwg',
|
||||
'image/vnd.dxf' => 'dxf',
|
||||
'image/vnd.fastbidsheet' => 'fbs',
|
||||
'image/vnd.fpx' => 'fpx',
|
||||
'image/vnd.fst' => 'fst',
|
||||
'image/vnd.fujixerox.edmics-mmr' => 'mmr',
|
||||
'image/vnd.fujixerox.edmics-rlc' => 'rlc',
|
||||
'image/vnd.ms-modi' => 'mdi',
|
||||
'image/vnd.ms-photo' => 'wdp',
|
||||
'image/vnd.net-fpx' => 'npx',
|
||||
'image/vnd.wap.wbmp' => 'wbmp',
|
||||
'image/vnd.xiff' => 'xif',
|
||||
'image/webp' => 'webp',
|
||||
'image/x-3ds' => '3ds',
|
||||
'image/x-cmu-raster' => 'ras',
|
||||
'image/x-cmx' => 'cmx',
|
||||
'image/x-freehand' => 'fh',
|
||||
'image/x-icon' => 'ico',
|
||||
'image/x-mrsid-image' => 'sid',
|
||||
'image/x-pcx' => 'pcx',
|
||||
'image/x-pict' => 'pic',
|
||||
'image/x-portable-anymap' => 'pnm',
|
||||
'image/x-portable-bitmap' => 'pbm',
|
||||
'image/x-portable-graymap' => 'pgm',
|
||||
'image/x-portable-pixmap' => 'ppm',
|
||||
'image/x-rgb' => 'rgb',
|
||||
'image/x-tga' => 'tga',
|
||||
'image/x-xbitmap' => 'xbm',
|
||||
'image/x-xpixmap' => 'xpm',
|
||||
'image/x-xwindowdump' => 'xwd',
|
||||
'message/rfc822' => 'eml',
|
||||
'model/iges' => 'igs',
|
||||
'model/mesh' => 'msh',
|
||||
'model/vnd.collada+xml' => 'dae',
|
||||
'model/vnd.dwf' => 'dwf',
|
||||
'model/vnd.gdl' => 'gdl',
|
||||
'model/vnd.gtw' => 'gtw',
|
||||
'model/vnd.mts' => 'mts',
|
||||
'model/vnd.vtu' => 'vtu',
|
||||
'model/vrml' => 'wrl',
|
||||
'model/x3d+binary' => 'x3db',
|
||||
'model/x3d+vrml' => 'x3dv',
|
||||
'model/x3d+xml' => 'x3d',
|
||||
'text/cache-manifest' => 'appcache',
|
||||
'text/calendar' => 'ics',
|
||||
'text/css' => 'css',
|
||||
'text/csv' => 'csv',
|
||||
'text/html' => 'html',
|
||||
'text/n3' => 'n3',
|
||||
'text/plain' => 'txt',
|
||||
'text/prs.lines.tag' => 'dsc',
|
||||
'text/richtext' => 'rtx',
|
||||
'text/rtf' => 'rtf',
|
||||
'text/sgml' => 'sgml',
|
||||
'text/tab-separated-values' => 'tsv',
|
||||
'text/troff' => 't',
|
||||
'text/turtle' => 'ttl',
|
||||
'text/uri-list' => 'uri',
|
||||
'text/vcard' => 'vcard',
|
||||
'text/vnd.curl' => 'curl',
|
||||
'text/vnd.curl.dcurl' => 'dcurl',
|
||||
'text/vnd.curl.mcurl' => 'mcurl',
|
||||
'text/vnd.curl.scurl' => 'scurl',
|
||||
'text/vnd.dvb.subtitle' => 'sub',
|
||||
'text/vnd.fly' => 'fly',
|
||||
'text/vnd.fmi.flexstor' => 'flx',
|
||||
'text/vnd.graphviz' => 'gv',
|
||||
'text/vnd.in3d.3dml' => '3dml',
|
||||
'text/vnd.in3d.spot' => 'spot',
|
||||
'text/vnd.sun.j2me.app-descriptor' => 'jad',
|
||||
'text/vnd.wap.wml' => 'wml',
|
||||
'text/vnd.wap.wmlscript' => 'wmls',
|
||||
'text/vtt' => 'vtt',
|
||||
'text/x-asm' => 's',
|
||||
'text/x-c' => 'c',
|
||||
'text/x-fortran' => 'f',
|
||||
'text/x-java-source' => 'java',
|
||||
'text/x-nfo' => 'nfo',
|
||||
'text/x-opml' => 'opml',
|
||||
'text/x-pascal' => 'p',
|
||||
'text/x-setext' => 'etx',
|
||||
'text/x-sfv' => 'sfv',
|
||||
'text/x-uuencode' => 'uu',
|
||||
'text/x-vcalendar' => 'vcs',
|
||||
'text/x-vcard' => 'vcf',
|
||||
'video/3gpp' => '3gp',
|
||||
'video/3gpp2' => '3g2',
|
||||
'video/h261' => 'h261',
|
||||
'video/h263' => 'h263',
|
||||
'video/h264' => 'h264',
|
||||
'video/jpeg' => 'jpgv',
|
||||
'video/jpm' => 'jpm',
|
||||
'video/mj2' => 'mj2',
|
||||
'video/mp4' => 'mp4',
|
||||
'video/mpeg' => 'mpeg',
|
||||
'video/ogg' => 'ogv',
|
||||
'video/quicktime' => 'qt',
|
||||
'video/vnd.dece.hd' => 'uvh',
|
||||
'video/vnd.dece.mobile' => 'uvm',
|
||||
'video/vnd.dece.pd' => 'uvp',
|
||||
'video/vnd.dece.sd' => 'uvs',
|
||||
'video/vnd.dece.video' => 'uvv',
|
||||
'video/vnd.dvb.file' => 'dvb',
|
||||
'video/vnd.fvt' => 'fvt',
|
||||
'video/vnd.mpegurl' => 'mxu',
|
||||
'video/vnd.ms-playready.media.pyv' => 'pyv',
|
||||
'video/vnd.uvvu.mp4' => 'uvu',
|
||||
'video/vnd.vivo' => 'viv',
|
||||
'video/webm' => 'webm',
|
||||
'video/x-f4v' => 'f4v',
|
||||
'video/x-fli' => 'fli',
|
||||
'video/x-flv' => 'flv',
|
||||
'video/x-m4v' => 'm4v',
|
||||
'video/x-matroska' => 'mkv',
|
||||
'video/x-mng' => 'mng',
|
||||
'video/x-ms-asf' => 'asf',
|
||||
'video/x-ms-vob' => 'vob',
|
||||
'video/x-ms-wm' => 'wm',
|
||||
'video/x-ms-wmv' => 'wmv',
|
||||
'video/x-ms-wmx' => 'wmx',
|
||||
'video/x-ms-wvx' => 'wvx',
|
||||
'video/x-msvideo' => 'avi',
|
||||
'video/x-sgi-movie' => 'movie',
|
||||
'video/x-smv' => 'smv',
|
||||
'x-conference/x-cooltalk' => 'ice',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function guess($mimeType)
|
||||
{
|
||||
if (isset($this->defaultExtensions[$mimeType])) {
|
||||
return $this->defaultExtensions[$mimeType];
|
||||
}
|
||||
|
||||
$lcMimeType = strtolower($mimeType);
|
||||
|
||||
return isset($this->defaultExtensions[$lcMimeType]) ? $this->defaultExtensions[$lcMimeType] : null;
|
||||
}
|
||||
}
|
||||
@@ -1,136 +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\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.
|
||||
*
|
||||
* By default, all mime type guessers provided by the framework are installed
|
||||
* (if available on the current OS/PHP setup).
|
||||
*
|
||||
* You can register custom guessers by calling the register() method on the
|
||||
* singleton instance. Custom guessers are always called before any default ones.
|
||||
*
|
||||
* $guesser = MimeTypeGuesser::getInstance();
|
||||
* $guesser->register(new MyCustomMimeTypeGuesser());
|
||||
*
|
||||
* If you want to change the order of the default guessers, just re-register your
|
||||
* preferred one as a custom one. The last registered guesser is preferred over
|
||||
* previously registered ones.
|
||||
*
|
||||
* Re-registering a built-in guesser also allows you to configure it:
|
||||
*
|
||||
* $guesser = MimeTypeGuesser::getInstance();
|
||||
* $guesser->register(new FileinfoMimeTypeGuesser('/path/to/magic/file'));
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class MimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
{
|
||||
/**
|
||||
* The singleton instance.
|
||||
*
|
||||
* @var MimeTypeGuesser
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* All registered MimeTypeGuesserInterface instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guessers = [];
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the singleton instance.
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
self::$instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all natively provided mime type guessers.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
$this->register(new FileBinaryMimeTypeGuesser());
|
||||
$this->register(new FileinfoMimeTypeGuesser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new mime type guesser.
|
||||
*
|
||||
* When guessing, this guesser is preferred over previously registered ones.
|
||||
*/
|
||||
public function register(MimeTypeGuesserInterface $guesser)
|
||||
{
|
||||
array_unshift($this->guessers, $guesser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to guess the mime type of the given file.
|
||||
*
|
||||
* The file is passed to each registered mime type guesser in reverse order
|
||||
* of their registration (last registered is queried first). Once a guesser
|
||||
* returns a value that is not NULL, this method terminates and returns the
|
||||
* value.
|
||||
*
|
||||
* @param string $path The path to the file
|
||||
*
|
||||
* @return string The mime type or NULL, if none could be guessed
|
||||
*
|
||||
* @throws \LogicException
|
||||
* @throws FileNotFoundException
|
||||
* @throws AccessDeniedException
|
||||
*/
|
||||
public function guess($path)
|
||||
{
|
||||
if (!is_file($path)) {
|
||||
throw new FileNotFoundException($path);
|
||||
}
|
||||
|
||||
if (!is_readable($path)) {
|
||||
throw new AccessDeniedException($path);
|
||||
}
|
||||
|
||||
foreach ($this->guessers as $guesser) {
|
||||
if (null !== $mimeType = $guesser->guess($path)) {
|
||||
return $mimeType;
|
||||
}
|
||||
}
|
||||
|
||||
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?)');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\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
|
||||
{
|
||||
/**
|
||||
* Guesses the mime type of the file with the given path.
|
||||
*
|
||||
* @param string $path The path to the file
|
||||
*
|
||||
* @return string 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
|
||||
*/
|
||||
public function guess($path);
|
||||
}
|
||||
@@ -20,7 +20,10 @@ class Stream extends File
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getSize()
|
||||
{
|
||||
return false;
|
||||
|
||||
133
vendor/symfony/http-foundation/File/UploadedFile.php
vendored
133
vendor/symfony/http-foundation/File/UploadedFile.php
vendored
@@ -31,7 +31,7 @@ use Symfony\Component\Mime\MimeTypes;
|
||||
*/
|
||||
class UploadedFile extends File
|
||||
{
|
||||
private $test = false;
|
||||
private $test;
|
||||
private $originalName;
|
||||
private $mimeType;
|
||||
private $error;
|
||||
@@ -60,21 +60,14 @@ class UploadedFile extends File
|
||||
* @throws FileException If file_uploads is disabled
|
||||
* @throws FileNotFoundException If the file does not exist
|
||||
*/
|
||||
public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false)
|
||||
public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, bool $test = false)
|
||||
{
|
||||
$this->originalName = $this->getName($originalName);
|
||||
$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);
|
||||
$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 +76,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
|
||||
*/
|
||||
public function getClientOriginalName()
|
||||
{
|
||||
@@ -96,11 +89,11 @@ class UploadedFile extends File
|
||||
* It is extracted from the original file name that was uploaded.
|
||||
* Then it should not be considered as a safe value.
|
||||
*
|
||||
* @return string The extension
|
||||
* @return string
|
||||
*/
|
||||
public function getClientOriginalExtension()
|
||||
{
|
||||
return pathinfo($this->originalName, PATHINFO_EXTENSION);
|
||||
return pathinfo($this->originalName, \PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +105,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
|
||||
*
|
||||
* @see getMimeType()
|
||||
*/
|
||||
@@ -133,40 +126,27 @@ class UploadedFile extends File
|
||||
* For a trusted extension, use guessExtension() instead (which guesses
|
||||
* the extension based on the guessed mime type for the file).
|
||||
*
|
||||
* @return string|null The guessed extension or null if it cannot be guessed
|
||||
* @return string|null
|
||||
*
|
||||
* @see guessExtension()
|
||||
* @see getClientMimeType()
|
||||
*/
|
||||
public function guessClientExtension()
|
||||
{
|
||||
if (!class_exists(MimeTypes::class)) {
|
||||
throw new \LogicException('You cannot guess the extension as the Mime component is not installed. Try running "composer require symfony/mime".');
|
||||
}
|
||||
|
||||
return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file size.
|
||||
*
|
||||
* It is extracted from the request from which the file has been uploaded.
|
||||
* Then it should not be considered as a safe value.
|
||||
*
|
||||
* @deprecated since Symfony 4.1, use getSize() instead.
|
||||
*
|
||||
* @return int|null The file sizes
|
||||
*/
|
||||
public function getClientSize()
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return $this->getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the upload error.
|
||||
*
|
||||
* If the upload was successful, the constant UPLOAD_ERR_OK is returned.
|
||||
* Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
|
||||
*
|
||||
* @return int The upload error
|
||||
* @return int
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
@@ -174,13 +154,13 @@ class UploadedFile extends File
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the file was uploaded successfully.
|
||||
* Returns whether the file has been uploaded with HTTP and no error occurred.
|
||||
*
|
||||
* @return bool True if the file has been uploaded with HTTP and no error occurred
|
||||
* @return bool
|
||||
*/
|
||||
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());
|
||||
}
|
||||
@@ -188,14 +168,11 @@ class UploadedFile extends File
|
||||
/**
|
||||
* Moves the file to a new location.
|
||||
*
|
||||
* @param string $directory The destination folder
|
||||
* @param string $name The new file name
|
||||
*
|
||||
* @return File A File object representing the new file
|
||||
* @return File
|
||||
*
|
||||
* @throws FileException if, for any reason, the file could not have been moved
|
||||
*/
|
||||
public function move($directory, $name = null)
|
||||
public function move(string $directory, string $name = null)
|
||||
{
|
||||
if ($this->isValid()) {
|
||||
if ($this->test) {
|
||||
@@ -205,10 +182,13 @@ class UploadedFile extends File
|
||||
$target = $this->getTargetFile($directory, $name);
|
||||
|
||||
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
|
||||
$moved = move_uploaded_file($this->getPathname(), $target);
|
||||
restore_error_handler();
|
||||
try {
|
||||
$moved = move_uploaded_file($this->getPathname(), $target);
|
||||
} finally {
|
||||
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());
|
||||
@@ -217,19 +197,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());
|
||||
}
|
||||
|
||||
@@ -239,26 +219,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;
|
||||
@@ -274,23 +267,23 @@ class UploadedFile extends File
|
||||
/**
|
||||
* Returns an informative upload error message.
|
||||
*
|
||||
* @return string The error message regarding the specified error code
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage()
|
||||
{
|
||||
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.',
|
||||
\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);
|
||||
}
|
||||
|
||||
44
vendor/symfony/http-foundation/FileBag.php
vendored
44
vendor/symfony/http-foundation/FileBag.php
vendored
@@ -21,10 +21,10 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
*/
|
||||
class FileBag extends ParameterBag
|
||||
{
|
||||
private static $fileKeys = ['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 = [])
|
||||
{
|
||||
@@ -43,7 +43,7 @@ class FileBag extends ParameterBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($key, $value)
|
||||
public function set(string $key, $value)
|
||||
{
|
||||
if (!\is_array($value) && !$value instanceof UploadedFile) {
|
||||
throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
|
||||
@@ -67,7 +67,7 @@ class FileBag extends ParameterBag
|
||||
*
|
||||
* @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information
|
||||
*
|
||||
* @return UploadedFile[]|UploadedFile|null A (multi-dimensional) array of UploadedFile instances
|
||||
* @return UploadedFile[]|UploadedFile|null
|
||||
*/
|
||||
protected function convertFileInformation($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'], false);
|
||||
}
|
||||
if (self::FILE_KEYS == $keys) {
|
||||
if (\UPLOAD_ERR_NO_FILE == $file['error']) {
|
||||
$file = null;
|
||||
} else {
|
||||
$file = array_map([$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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,21 +109,19 @@ class FileBag extends ParameterBag
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function fixPhpFilesArray($data)
|
||||
protected function fixPhpFilesArray(array $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]);
|
||||
}
|
||||
|
||||
|
||||
134
vendor/symfony/http-foundation/HeaderBag.php
vendored
134
vendor/symfony/http-foundation/HeaderBag.php
vendored
@@ -15,15 +15,20 @@ namespace Symfony\Component\HttpFoundation;
|
||||
* HeaderBag is a container for HTTP headers.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @implements \IteratorAggregate<string, list<string|null>>
|
||||
*/
|
||||
class HeaderBag implements \IteratorAggregate, \Countable
|
||||
{
|
||||
protected const UPPER = '_ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
protected const LOWER = '-abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
/**
|
||||
* @var array<string, list<string|null>>
|
||||
*/
|
||||
protected $headers = [];
|
||||
protected $cacheControl = [];
|
||||
|
||||
/**
|
||||
* @param array $headers An array of HTTP headers
|
||||
*/
|
||||
public function __construct(array $headers = [])
|
||||
{
|
||||
foreach ($headers as $key => $values) {
|
||||
@@ -34,7 +39,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the headers as a string.
|
||||
*
|
||||
* @return string The headers
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
@@ -58,17 +63,23 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the headers.
|
||||
*
|
||||
* @return array An array of headers
|
||||
* @param string|null $key The name of the headers to return or null to get them all
|
||||
*
|
||||
* @return array<string, array<int, string|null>>|array<int, string|null>
|
||||
*/
|
||||
public function all()
|
||||
public function all(string $key = null)
|
||||
{
|
||||
if (null !== $key) {
|
||||
return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? [];
|
||||
}
|
||||
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameter keys.
|
||||
*
|
||||
* @return array An array of parameter keys
|
||||
* @return string[]
|
||||
*/
|
||||
public function keys()
|
||||
{
|
||||
@@ -77,8 +88,6 @@ 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 = [])
|
||||
{
|
||||
@@ -88,8 +97,6 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
|
||||
/**
|
||||
* Adds new headers the current HTTP headers set.
|
||||
*
|
||||
* @param array $headers An array of HTTP headers
|
||||
*/
|
||||
public function add(array $headers)
|
||||
{
|
||||
@@ -99,44 +106,34 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a header value by name.
|
||||
* Returns the first header by name or the default one.
|
||||
*
|
||||
* @param string $key The header name
|
||||
* @param string|null $default The default value
|
||||
* @param bool $first Whether to return the first value or all header values
|
||||
*
|
||||
* @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise
|
||||
* @return string|null
|
||||
*/
|
||||
public function get($key, $default = null, $first = true)
|
||||
public function get(string $key, string $default = null)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$headers = $this->all();
|
||||
$headers = $this->all($key);
|
||||
|
||||
if (!\array_key_exists($key, $headers)) {
|
||||
if (null === $default) {
|
||||
return $first ? null : [];
|
||||
}
|
||||
|
||||
return $first ? $default : [$default];
|
||||
if (!$headers) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
if ($first) {
|
||||
return \count($headers[$key]) ? $headers[$key][0] : $default;
|
||||
if (null === $headers[0]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $headers[$key];
|
||||
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|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)
|
||||
public function set(string $key, $values, bool $replace = true)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$key = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
if (\is_array($values)) {
|
||||
$values = array_values($values);
|
||||
@@ -162,36 +159,29 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns true if the HTTP header is defined.
|
||||
*
|
||||
* @param string $key The HTTP header
|
||||
*
|
||||
* @return bool true if the parameter exists, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
public function has(string $key)
|
||||
{
|
||||
return \array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
|
||||
return \array_key_exists(strtr($key, self::UPPER, self::LOWER), $this->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given HTTP header contains the given value.
|
||||
*
|
||||
* @param string $key The HTTP header name
|
||||
* @param string $value The HTTP value
|
||||
*
|
||||
* @return bool true if the value is contained in the header, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function contains($key, $value)
|
||||
public function contains(string $key, string $value)
|
||||
{
|
||||
return \in_array($value, $this->get($key, null, false));
|
||||
return \in_array($value, $this->all($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a header.
|
||||
*
|
||||
* @param string $key The HTTP header name
|
||||
*/
|
||||
public function remove($key)
|
||||
public function remove(string $key)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$key = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
unset($this->headers[$key]);
|
||||
|
||||
@@ -203,21 +193,18 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the HTTP header value converted to a date.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param \DateTime $default The default value
|
||||
*
|
||||
* @return \DateTime|null The parsed DateTime or the default value if the header does not exist
|
||||
* @return \DateTimeInterface|null
|
||||
*
|
||||
* @throws \RuntimeException When the HTTP header is not parseable
|
||||
*/
|
||||
public function getDate($key, \DateTime $default = null)
|
||||
public function getDate(string $key, \DateTime $default = null)
|
||||
{
|
||||
if (null === $value = $this->get($key)) {
|
||||
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,10 +213,9 @@ 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 bool|string $value The Cache-Control directive value
|
||||
*/
|
||||
public function addCacheControlDirective($key, $value = true)
|
||||
public function addCacheControlDirective(string $key, $value = true)
|
||||
{
|
||||
$this->cacheControl[$key] = $value;
|
||||
|
||||
@@ -239,11 +225,9 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns true if the Cache-Control directive is defined.
|
||||
*
|
||||
* @param string $key The Cache-Control directive
|
||||
*
|
||||
* @return bool true if the directive exists, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCacheControlDirective($key)
|
||||
public function hasCacheControlDirective(string $key)
|
||||
{
|
||||
return \array_key_exists($key, $this->cacheControl);
|
||||
}
|
||||
@@ -251,21 +235,17 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns a Cache-Control directive value by name.
|
||||
*
|
||||
* @param string $key The directive name
|
||||
*
|
||||
* @return mixed|null The directive value if defined, null otherwise
|
||||
* @return bool|string|null
|
||||
*/
|
||||
public function getCacheControlDirective($key)
|
||||
public function getCacheControlDirective(string $key)
|
||||
{
|
||||
return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
|
||||
return $this->cacheControl[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a Cache-Control directive.
|
||||
*
|
||||
* @param string $key The Cache-Control directive
|
||||
*/
|
||||
public function removeCacheControlDirective($key)
|
||||
public function removeCacheControlDirective(string $key)
|
||||
{
|
||||
unset($this->cacheControl[$key]);
|
||||
|
||||
@@ -275,8 +255,9 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns an iterator for headers.
|
||||
*
|
||||
* @return \ArrayIterator An \ArrayIterator instance
|
||||
* @return \ArrayIterator<string, list<string|null>>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->headers);
|
||||
@@ -285,8 +266,9 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the number of headers.
|
||||
*
|
||||
* @return int The number of headers
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \count($this->headers);
|
||||
@@ -302,11 +284,9 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Parses a Cache-Control HTTP header.
|
||||
*
|
||||
* @param string $header The value of the Cache-Control HTTP header
|
||||
*
|
||||
* @return array An array representing the attribute values
|
||||
* @return array
|
||||
*/
|
||||
protected function parseCacheControl($header)
|
||||
protected function parseCacheControl(string $header)
|
||||
{
|
||||
$parts = HeaderUtils::split($header, ',=');
|
||||
|
||||
|
||||
88
vendor/symfony/http-foundation/HeaderUtils.php
vendored
88
vendor/symfony/http-foundation/HeaderUtils.php
vendored
@@ -36,7 +36,6 @@ class HeaderUtils
|
||||
* HeaderUtils::split("da, 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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -155,8 +154,6 @@ class HeaderUtils
|
||||
* is semantically equivalent to $filename. If the filename is already ASCII,
|
||||
* it can be omitted, or just copied from $filename
|
||||
*
|
||||
* @return string A string suitable for use as a Content-Disposition field-value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @see RFC 6266
|
||||
@@ -177,12 +174,12 @@ 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.');
|
||||
}
|
||||
|
||||
@@ -194,17 +191,81 @@ class HeaderUtils
|
||||
return $disposition.'; '.self::toString($params, ';');
|
||||
}
|
||||
|
||||
private static function groupParts(array $matches, string $separators): array
|
||||
/**
|
||||
* Like parse_str(), but preserves dots in variable names.
|
||||
*/
|
||||
public static function parseQuery(string $query, bool $ignoreBrackets = false, string $separator = '&'): array
|
||||
{
|
||||
$q = [];
|
||||
|
||||
foreach (explode($separator, $query) as $v) {
|
||||
if (false !== $i = strpos($v, "\0")) {
|
||||
$v = substr($v, 0, $i);
|
||||
}
|
||||
|
||||
if (false === $i = strpos($v, '=')) {
|
||||
$k = urldecode($v);
|
||||
$v = '';
|
||||
} else {
|
||||
$k = urldecode(substr($v, 0, $i));
|
||||
$v = substr($v, $i);
|
||||
}
|
||||
|
||||
if (false !== $i = strpos($k, "\0")) {
|
||||
$k = substr($k, 0, $i);
|
||||
}
|
||||
|
||||
$k = ltrim($k, ' ');
|
||||
|
||||
if ($ignoreBrackets) {
|
||||
$q[$k][] = urldecode(substr($v, 1));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false === $i = strpos($k, '[')) {
|
||||
$q[] = bin2hex($k).$v;
|
||||
} else {
|
||||
$q[] = bin2hex(substr($k, 0, $i)).rawurlencode(substr($k, $i)).$v;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ignoreBrackets) {
|
||||
return $q;
|
||||
}
|
||||
|
||||
parse_str(implode('&', $q), $q);
|
||||
|
||||
$query = [];
|
||||
|
||||
foreach ($q as $k => $v) {
|
||||
if (false !== $i = strpos($k, '_')) {
|
||||
$query[substr_replace($k, hex2bin(substr($k, 0, $i)).'[', 0, 1 + $i)] = $v;
|
||||
} else {
|
||||
$query[hex2bin($k)] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private static function groupParts(array $matches, string $separators, bool $first = true): array
|
||||
{
|
||||
$separator = $separators[0];
|
||||
$partSeparators = substr($separators, 1);
|
||||
|
||||
$i = 0;
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -212,12 +273,19 @@ class HeaderUtils
|
||||
$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;
|
||||
|
||||
113
vendor/symfony/http-foundation/InputBag.php
vendored
Normal file
113
vendor/symfony/http-foundation/InputBag.php
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
||||
|
||||
/**
|
||||
* InputBag is a container for user input values such as $_GET, $_POST, $_REQUEST, and $_COOKIE.
|
||||
*
|
||||
* @author Saif Eddin Gmati <azjezz@protonmail.com>
|
||||
*/
|
||||
final class InputBag extends ParameterBag
|
||||
{
|
||||
/**
|
||||
* Returns a scalar input value by name.
|
||||
*
|
||||
* @param string|int|float|bool|null $default The default value if the input key does not exist
|
||||
*
|
||||
* @return string|int|float|bool|null
|
||||
*/
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
if (null !== $default && !is_scalar($default) && !(\is_object($default) && method_exists($default, '__toString'))) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.1', 'Passing a non-scalar value as 2nd argument to "%s()" is deprecated, pass a scalar or null instead.', __METHOD__);
|
||||
}
|
||||
|
||||
$value = parent::get($key, $this);
|
||||
|
||||
if (null !== $value && $this !== $value && !is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.1', 'Retrieving a non-string value from "%s()" is deprecated, and will throw a "%s" exception in Symfony 6.0, use "%s::all($key)" instead.', __METHOD__, BadRequestException::class, __CLASS__);
|
||||
}
|
||||
|
||||
return $this === $value ? $default : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function all(string $key = null): array
|
||||
{
|
||||
return parent::all($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current input values by a new set.
|
||||
*/
|
||||
public function replace(array $inputs = [])
|
||||
{
|
||||
$this->parameters = [];
|
||||
$this->add($inputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds input values.
|
||||
*/
|
||||
public function add(array $inputs = [])
|
||||
{
|
||||
foreach ($inputs as $input => $value) {
|
||||
$this->set($input, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an input by name.
|
||||
*
|
||||
* @param string|int|float|bool|array|null $value
|
||||
*/
|
||||
public function set(string $key, $value)
|
||||
{
|
||||
if (null !== $value && !is_scalar($value) && !\is_array($value) && !method_exists($value, '__toString')) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.1', 'Passing "%s" as a 2nd Argument to "%s()" is deprecated, pass a scalar, array, or null instead.', get_debug_type($value), __METHOD__);
|
||||
}
|
||||
|
||||
$this->parameters[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function filter(string $key, $default = null, int $filter = \FILTER_DEFAULT, $options = [])
|
||||
{
|
||||
$value = $this->has($key) ? $this->all()[$key] : $default;
|
||||
|
||||
// Always turn $options into an array - this allows filter_var option shortcuts.
|
||||
if (!\is_array($options) && $options) {
|
||||
$options = ['flags' => $options];
|
||||
}
|
||||
|
||||
if (\is_array($value) && !(($options['flags'] ?? 0) & (\FILTER_REQUIRE_ARRAY | \FILTER_FORCE_ARRAY))) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.1', 'Filtering an array value with "%s()" without passing the FILTER_REQUIRE_ARRAY or FILTER_FORCE_ARRAY flag is deprecated', __METHOD__);
|
||||
|
||||
if (!isset($options['flags'])) {
|
||||
$options['flags'] = \FILTER_REQUIRE_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
if ((\FILTER_CALLBACK & $filter) && !(($options['options'] ?? null) instanceof \Closure)) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.2', 'Not passing a Closure together with FILTER_CALLBACK to "%s()" is deprecated. Wrap your filter in a closure instead.', __METHOD__);
|
||||
// throw new \InvalidArgumentException(sprintf('A Closure must be passed to "%s()" when FILTER_CALLBACK is used, "%s" given.', __METHOD__, get_debug_type($options['options'] ?? null)));
|
||||
}
|
||||
|
||||
return filter_var($value, $filter, $options);
|
||||
}
|
||||
}
|
||||
83
vendor/symfony/http-foundation/IpUtils.php
vendored
83
vendor/symfony/http-foundation/IpUtils.php
vendored
@@ -30,13 +30,18 @@ class IpUtils
|
||||
/**
|
||||
* Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
|
||||
*
|
||||
* @param string $requestIp IP to check
|
||||
* @param string|array $ips List of IPs or subnets (can be a string if only a single one)
|
||||
* @param string|array $ips List of IPs or subnets (can be a string if only a single one)
|
||||
*
|
||||
* @return bool Whether the IP is valid
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkIp($requestIp, $ips)
|
||||
public static function checkIp(?string $requestIp, $ips)
|
||||
{
|
||||
if (null === $requestIp) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.4', 'Passing null as $requestIp to "%s()" is deprecated, pass an empty string instead.', __METHOD__);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!\is_array($ips)) {
|
||||
$ips = [$ips];
|
||||
}
|
||||
@@ -56,27 +61,32 @@ class IpUtils
|
||||
* Compares two IPv4 addresses.
|
||||
* In case a subnet is given, it checks if it contains the request IP.
|
||||
*
|
||||
* @param string $requestIp IPv4 address to check
|
||||
* @param string $ip IPv4 address or subnet in CIDR notation
|
||||
* @param string $ip IPv4 address or subnet in CIDR notation
|
||||
*
|
||||
* @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
|
||||
*/
|
||||
public static function checkIp4($requestIp, $ip)
|
||||
public static function checkIp4(?string $requestIp, string $ip)
|
||||
{
|
||||
if (null === $requestIp) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.4', 'Passing null as $requestIp to "%s()" is deprecated, pass an empty string instead.', __METHOD__);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheKey = $requestIp.'-'.$ip;
|
||||
if (isset(self::$checkedIps[$cacheKey])) {
|
||||
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) {
|
||||
@@ -102,15 +112,20 @@ class IpUtils
|
||||
*
|
||||
* @see https://github.com/dsp/v6tools
|
||||
*
|
||||
* @param string $requestIp IPv6 address to check
|
||||
* @param string $ip IPv6 address or subnet in CIDR notation
|
||||
* @param string $ip IPv6 address or subnet in CIDR notation
|
||||
*
|
||||
* @return bool Whether the IP is valid
|
||||
* @return bool
|
||||
*
|
||||
* @throws \RuntimeException When IPV6 support is not enabled
|
||||
*/
|
||||
public static function checkIp6($requestIp, $ip)
|
||||
public static function checkIp6(?string $requestIp, string $ip)
|
||||
{
|
||||
if (null === $requestIp) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.4', 'Passing null as $requestIp to "%s()" is deprecated, pass an empty string instead.', __METHOD__);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheKey = $requestIp.'-'.$ip;
|
||||
if (isset(self::$checkedIps[$cacheKey])) {
|
||||
return self::$checkedIps[$cacheKey];
|
||||
@@ -120,8 +135,8 @@ 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);
|
||||
if (str_contains($ip, '/')) {
|
||||
[$address, $netmask] = explode('/', $ip, 2);
|
||||
|
||||
if ('0' === $netmask) {
|
||||
return (bool) unpack('n*', @inet_pton($address));
|
||||
@@ -145,7 +160,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 +168,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;
|
||||
}
|
||||
}
|
||||
|
||||
55
vendor/symfony/http-foundation/JsonResponse.php
vendored
55
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;
|
||||
|
||||
@@ -43,6 +43,10 @@ class JsonResponse extends Response
|
||||
{
|
||||
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,39 @@ class JsonResponse extends Response
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* return JsonResponse::create($data, 200)
|
||||
* return JsonResponse::create(['key' => 'value'])
|
||||
* ->setSharedMaxAge(300);
|
||||
*
|
||||
* @param mixed $data The json response data
|
||||
* @param mixed $data The JSON response data
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @deprecated since Symfony 5.1, use __construct() instead.
|
||||
*/
|
||||
public static function create($data = null, $status = 200, $headers = [])
|
||||
public static function create($data = null, int $status = 200, array $headers = [])
|
||||
{
|
||||
trigger_deprecation('symfony/http-foundation', '5.1', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, static::class);
|
||||
|
||||
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 = [])
|
||||
public static function fromJsonString(string $data, int $status = 200, array $headers = [])
|
||||
{
|
||||
return new static($data, $status, $headers, true);
|
||||
}
|
||||
@@ -86,10 +105,10 @@ class JsonResponse extends Response
|
||||
*
|
||||
* @throws \InvalidArgumentException When the callback name is not valid
|
||||
*/
|
||||
public function setCallback($callback = null)
|
||||
public function setCallback(string $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>
|
||||
@@ -115,13 +134,9 @@ class JsonResponse extends Response
|
||||
/**
|
||||
* Sets a raw string containing a JSON document to be sent.
|
||||
*
|
||||
* @param string $json
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setJson($json)
|
||||
public function setJson(string $json)
|
||||
{
|
||||
$this->data = $json;
|
||||
|
||||
@@ -142,17 +157,17 @@ class JsonResponse extends Response
|
||||
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 (\PHP_VERSION_ID >= 70300 && (JSON_THROW_ON_ERROR & $this->encodingOptions)) {
|
||||
if (\PHP_VERSION_ID >= 70300 && (\JSON_THROW_ON_ERROR & $this->encodingOptions)) {
|
||||
return $this->setJson($data);
|
||||
}
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
if (\JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new \InvalidArgumentException(json_last_error_msg());
|
||||
}
|
||||
|
||||
@@ -172,13 +187,11 @@ class JsonResponse extends Response
|
||||
/**
|
||||
* Sets options used while encoding data to JSON.
|
||||
*
|
||||
* @param int $encodingOptions
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEncodingOptions($encodingOptions)
|
||||
public function setEncodingOptions(int $encodingOptions)
|
||||
{
|
||||
$this->encodingOptions = (int) $encodingOptions;
|
||||
$this->encodingOptions = $encodingOptions;
|
||||
|
||||
return $this->setData(json_decode($this->data));
|
||||
}
|
||||
|
||||
2
vendor/symfony/http-foundation/LICENSE
vendored
2
vendor/symfony/http-foundation/LICENSE
vendored
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2019 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
|
||||
|
||||
114
vendor/symfony/http-foundation/ParameterBag.php
vendored
114
vendor/symfony/http-foundation/ParameterBag.php
vendored
@@ -11,10 +11,14 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
||||
|
||||
/**
|
||||
* ParameterBag is a container for key/value pairs.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @implements \IteratorAggregate<string, mixed>
|
||||
*/
|
||||
class ParameterBag implements \IteratorAggregate, \Countable
|
||||
{
|
||||
@@ -23,9 +27,6 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function __construct(array $parameters = [])
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
@@ -34,17 +35,29 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the parameters.
|
||||
*
|
||||
* @return array An array of parameters
|
||||
* @param string|null $key The name of the parameter to return or null to get them all
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
public function all(/*string $key = null*/)
|
||||
{
|
||||
return $this->parameters;
|
||||
$key = \func_num_args() > 0 ? func_get_arg(0) : null;
|
||||
|
||||
if (null === $key) {
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
if (!\is_array($value = $this->parameters[$key] ?? [])) {
|
||||
throw new BadRequestException(sprintf('Unexpected value for parameter "%s": expecting "array", got "%s".', $key, get_debug_type($value)));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameter keys.
|
||||
*
|
||||
* @return array An array of parameter keys
|
||||
* @return array
|
||||
*/
|
||||
public function keys()
|
||||
{
|
||||
@@ -53,8 +66,6 @@ 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 = [])
|
||||
{
|
||||
@@ -63,8 +74,6 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
|
||||
/**
|
||||
* Adds parameters.
|
||||
*
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function add(array $parameters = [])
|
||||
{
|
||||
@@ -74,12 +83,11 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns a parameter by name.
|
||||
*
|
||||
* @param string $key The key
|
||||
* @param mixed $default The default value if the parameter key does not exist
|
||||
* @param mixed $default The default value if the parameter key does not exist
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
|
||||
}
|
||||
@@ -87,10 +95,9 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Sets a parameter by name.
|
||||
*
|
||||
* @param string $key The key
|
||||
* @param mixed $value The value
|
||||
* @param mixed $value The value
|
||||
*/
|
||||
public function set($key, $value)
|
||||
public function set(string $key, $value)
|
||||
{
|
||||
$this->parameters[$key] = $value;
|
||||
}
|
||||
@@ -98,21 +105,17 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns true if the parameter is defined.
|
||||
*
|
||||
* @param string $key The key
|
||||
*
|
||||
* @return bool true if the parameter exists, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
public function has(string $key)
|
||||
{
|
||||
return \array_key_exists($key, $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a parameter.
|
||||
*
|
||||
* @param string $key The key
|
||||
*/
|
||||
public function remove($key)
|
||||
public function remove(string $key)
|
||||
{
|
||||
unset($this->parameters[$key]);
|
||||
}
|
||||
@@ -120,12 +123,9 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the alphabetic characters of the parameter value.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param string $default The default value if the parameter key does not exist
|
||||
*
|
||||
* @return string The filtered value
|
||||
* @return string
|
||||
*/
|
||||
public function getAlpha($key, $default = '')
|
||||
public function getAlpha(string $key, string $default = '')
|
||||
{
|
||||
return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default));
|
||||
}
|
||||
@@ -133,12 +133,9 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the alphabetic characters and digits of the parameter value.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param string $default The default value if the parameter key does not exist
|
||||
*
|
||||
* @return string The filtered value
|
||||
* @return string
|
||||
*/
|
||||
public function getAlnum($key, $default = '')
|
||||
public function getAlnum(string $key, string $default = '')
|
||||
{
|
||||
return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default));
|
||||
}
|
||||
@@ -146,26 +143,20 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the digits of the parameter value.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param string $default The default value if the parameter key does not exist
|
||||
*
|
||||
* @return string The filtered value
|
||||
* @return string
|
||||
*/
|
||||
public function getDigits($key, $default = '')
|
||||
public function getDigits(string $key, string $default = '')
|
||||
{
|
||||
// we need to remove - and + because they're allowed in the filter
|
||||
return str_replace(['-', '+'], '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
|
||||
return str_replace(['-', '+'], '', $this->filter($key, $default, \FILTER_SANITIZE_NUMBER_INT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameter value converted to integer.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param int $default The default value if the parameter key does not exist
|
||||
*
|
||||
* @return int The filtered value
|
||||
* @return int
|
||||
*/
|
||||
public function getInt($key, $default = 0)
|
||||
public function getInt(string $key, int $default = 0)
|
||||
{
|
||||
return (int) $this->get($key, $default);
|
||||
}
|
||||
@@ -173,29 +164,25 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the parameter value converted to boolean.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param bool $default The default value if the parameter key does not exist
|
||||
*
|
||||
* @return bool The filtered value
|
||||
* @return bool
|
||||
*/
|
||||
public function getBoolean($key, $default = false)
|
||||
public function getBoolean(string $key, bool $default = false)
|
||||
{
|
||||
return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN);
|
||||
return $this->filter($key, $default, \FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter key.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @param mixed $default Default = null
|
||||
* @param int $filter FILTER_* constant
|
||||
* @param mixed $options Filter options
|
||||
* @param mixed $default Default = null
|
||||
* @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 = [])
|
||||
public function filter(string $key, $default = null, int $filter = \FILTER_DEFAULT, $options = [])
|
||||
{
|
||||
$value = $this->get($key, $default);
|
||||
|
||||
@@ -206,7 +193,12 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
|
||||
// Add a convenience check for arrays.
|
||||
if (\is_array($value) && !isset($options['flags'])) {
|
||||
$options['flags'] = FILTER_REQUIRE_ARRAY;
|
||||
$options['flags'] = \FILTER_REQUIRE_ARRAY;
|
||||
}
|
||||
|
||||
if ((\FILTER_CALLBACK & $filter) && !(($options['options'] ?? null) instanceof \Closure)) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.2', 'Not passing a Closure together with FILTER_CALLBACK to "%s()" is deprecated. Wrap your filter in a closure instead.', __METHOD__);
|
||||
// throw new \InvalidArgumentException(sprintf('A Closure must be passed to "%s()" when FILTER_CALLBACK is used, "%s" given.', __METHOD__, get_debug_type($options['options'] ?? null)));
|
||||
}
|
||||
|
||||
return filter_var($value, $filter, $options);
|
||||
@@ -215,8 +207,9 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns an iterator for parameters.
|
||||
*
|
||||
* @return \ArrayIterator An \ArrayIterator instance
|
||||
* @return \ArrayIterator<string, mixed>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->parameters);
|
||||
@@ -225,8 +218,9 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns the number of parameters.
|
||||
*
|
||||
* @return int The number of parameters
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \count($this->parameters);
|
||||
|
||||
24
vendor/symfony/http-foundation/README.md
vendored
24
vendor/symfony/http-foundation/README.md
vendored
@@ -4,11 +4,25 @@ HttpFoundation Component
|
||||
The HttpFoundation component defines an object-oriented layer for the HTTP
|
||||
specification.
|
||||
|
||||
Sponsor
|
||||
-------
|
||||
|
||||
The HttpFoundation component for Symfony 5.4/6.0 is [backed][1] by [Laravel][2].
|
||||
|
||||
Laravel is a PHP web development framework that is passionate about maximum developer
|
||||
happiness. Laravel is built using a variety of bespoke and Symfony based components.
|
||||
|
||||
Help Symfony by [sponsoring][3] its development!
|
||||
|
||||
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)
|
||||
|
||||
[1]: https://symfony.com/backers
|
||||
[2]: https://laravel.com/
|
||||
[3]: https://symfony.com/sponsor
|
||||
|
||||
57
vendor/symfony/http-foundation/RateLimiter/AbstractRequestRateLimiter.php
vendored
Normal file
57
vendor/symfony/http-foundation/RateLimiter/AbstractRequestRateLimiter.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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\RateLimiter;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\RateLimiter\LimiterInterface;
|
||||
use Symfony\Component\RateLimiter\Policy\NoLimiter;
|
||||
use Symfony\Component\RateLimiter\RateLimit;
|
||||
|
||||
/**
|
||||
* An implementation of RequestRateLimiterInterface that
|
||||
* fits most use-cases.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
abstract class AbstractRequestRateLimiter implements RequestRateLimiterInterface
|
||||
{
|
||||
public function consume(Request $request): RateLimit
|
||||
{
|
||||
$limiters = $this->getLimiters($request);
|
||||
if (0 === \count($limiters)) {
|
||||
$limiters = [new NoLimiter()];
|
||||
}
|
||||
|
||||
$minimalRateLimit = null;
|
||||
foreach ($limiters as $limiter) {
|
||||
$rateLimit = $limiter->consume(1);
|
||||
|
||||
if (null === $minimalRateLimit || $rateLimit->getRemainingTokens() < $minimalRateLimit->getRemainingTokens()) {
|
||||
$minimalRateLimit = $rateLimit;
|
||||
}
|
||||
}
|
||||
|
||||
return $minimalRateLimit;
|
||||
}
|
||||
|
||||
public function reset(Request $request): void
|
||||
{
|
||||
foreach ($this->getLimiters($request) as $limiter) {
|
||||
$limiter->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LimiterInterface[] a set of limiters using keys extracted from the request
|
||||
*/
|
||||
abstract protected function getLimiters(Request $request): array;
|
||||
}
|
||||
30
vendor/symfony/http-foundation/RateLimiter/RequestRateLimiterInterface.php
vendored
Normal file
30
vendor/symfony/http-foundation/RateLimiter/RequestRateLimiterInterface.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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\RateLimiter;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\RateLimiter\RateLimit;
|
||||
|
||||
/**
|
||||
* A special type of limiter that deals with requests.
|
||||
*
|
||||
* This allows to limit on different types of information
|
||||
* from the requests.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
interface RequestRateLimiterInterface
|
||||
{
|
||||
public function consume(Request $request): RateLimit;
|
||||
|
||||
public function reset(Request $request): void;
|
||||
}
|
||||
@@ -30,9 +30,9 @@ class RedirectResponse extends Response
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc2616#section-10.3
|
||||
* @see https://tools.ietf.org/html/rfc2616#section-10.3
|
||||
*/
|
||||
public function __construct(?string $url, int $status = 302, array $headers = [])
|
||||
public function __construct(string $url, int $status = 302, array $headers = [])
|
||||
{
|
||||
parent::__construct('', $status, $headers);
|
||||
|
||||
@@ -50,21 +50,23 @@ class RedirectResponse extends Response
|
||||
/**
|
||||
* Factory method for chainability.
|
||||
*
|
||||
* @param string $url The url to redirect to
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
* @param string $url The URL to redirect to
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @deprecated since Symfony 5.1, use __construct() instead.
|
||||
*/
|
||||
public static function create($url = '', $status = 302, $headers = [])
|
||||
public static function create($url = '', int $status = 302, array $headers = [])
|
||||
{
|
||||
trigger_deprecation('symfony/http-foundation', '5.1', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, static::class);
|
||||
|
||||
return new static($url, $status, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target URL.
|
||||
*
|
||||
* @return string target URL
|
||||
* @return string
|
||||
*/
|
||||
public function getTargetUrl()
|
||||
{
|
||||
@@ -74,15 +76,13 @@ class RedirectResponse extends Response
|
||||
/**
|
||||
* Sets the redirect target of this response.
|
||||
*
|
||||
* @param string $url The URL to redirect to
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setTargetUrl($url)
|
||||
public function setTargetUrl(string $url)
|
||||
{
|
||||
if (empty($url)) {
|
||||
if ('' === $url) {
|
||||
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
|
||||
}
|
||||
|
||||
@@ -93,14 +93,14 @@ class RedirectResponse extends Response
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="refresh" content="0;url=%1$s" />
|
||||
<meta http-equiv="refresh" content="0;url=\'%1$s\'" />
|
||||
|
||||
<title>Redirecting to %1$s</title>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="%1$s">%1$s</a>.
|
||||
</body>
|
||||
</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
|
||||
</html>', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8')));
|
||||
|
||||
$this->headers->set('Location', $url);
|
||||
|
||||
|
||||
504
vendor/symfony/http-foundation/Request.php
vendored
504
vendor/symfony/http-foundation/Request.php
vendored
File diff suppressed because it is too large
Load Diff
@@ -54,11 +54,8 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
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 = [], $schemes = null, int $port = null)
|
||||
@@ -87,10 +84,8 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
|
||||
/**
|
||||
* Adds a check for the URL host name.
|
||||
*
|
||||
* @param string|null $regexp A Regexp
|
||||
*/
|
||||
public function matchHost($regexp)
|
||||
public function matchHost(?string $regexp)
|
||||
{
|
||||
$this->host = $regexp;
|
||||
}
|
||||
@@ -100,17 +95,15 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a check for the URL path info.
|
||||
*
|
||||
* @param string|null $regexp A Regexp
|
||||
*/
|
||||
public function matchPath($regexp)
|
||||
public function matchPath(?string $regexp)
|
||||
{
|
||||
$this->path = $regexp;
|
||||
}
|
||||
@@ -120,7 +113,7 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
*
|
||||
* @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
|
||||
*/
|
||||
public function matchIp($ip)
|
||||
public function matchIp(string $ip)
|
||||
{
|
||||
$this->matchIps($ip);
|
||||
}
|
||||
@@ -132,7 +125,11 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
*/
|
||||
public function matchIps($ips)
|
||||
{
|
||||
$this->ips = null !== $ips ? (array) $ips : [];
|
||||
$ips = null !== $ips ? (array) $ips : [];
|
||||
|
||||
$this->ips = array_reduce($ips, static function (array $ips, string $ip) {
|
||||
return array_merge($ips, preg_split('/\s*,\s*/', $ip));
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,11 +144,8 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
|
||||
/**
|
||||
* Adds a check for request attribute.
|
||||
*
|
||||
* @param string $key The request attribute name
|
||||
* @param string $regexp A Regexp
|
||||
*/
|
||||
public function matchAttribute($key, $regexp)
|
||||
public function matchAttribute(string $key, string $regexp)
|
||||
{
|
||||
$this->attributes[$key] = $regexp;
|
||||
}
|
||||
@@ -170,7 +164,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;
|
||||
}
|
||||
}
|
||||
@@ -187,7 +185,7 @@ class RequestMatcher implements RequestMatcherInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
|
||||
if (IpUtils::checkIp($request->getClientIp() ?? '', $this->ips)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ interface RequestMatcherInterface
|
||||
/**
|
||||
* Decides whether the rule(s) implemented by the strategy matches the supplied request.
|
||||
*
|
||||
* @return bool true if the request matches, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function matches(Request $request);
|
||||
}
|
||||
|
||||
47
vendor/symfony/http-foundation/RequestStack.php
vendored
47
vendor/symfony/http-foundation/RequestStack.php
vendored
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
|
||||
/**
|
||||
* Request stack that controls the lifecycle of requests.
|
||||
*
|
||||
@@ -47,7 +50,7 @@ class RequestStack
|
||||
public function pop()
|
||||
{
|
||||
if (!$this->requests) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_pop($this->requests);
|
||||
@@ -62,23 +65,35 @@ class RequestStack
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the master Request.
|
||||
* Gets the main request.
|
||||
*
|
||||
* Be warned that making your code aware of the master request
|
||||
* Be warned that making your code aware of the main request
|
||||
* might make it un-compatible with other features of your framework
|
||||
* like ESI support.
|
||||
*
|
||||
* @return Request|null
|
||||
*/
|
||||
public function getMasterRequest()
|
||||
public function getMainRequest(): ?Request
|
||||
{
|
||||
if (!$this->requests) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->requests[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the master request.
|
||||
*
|
||||
* @return Request|null
|
||||
*
|
||||
* @deprecated since symfony/http-foundation 5.3, use getMainRequest() instead
|
||||
*/
|
||||
public function getMasterRequest()
|
||||
{
|
||||
trigger_deprecation('symfony/http-foundation', '5.3', '"%s()" is deprecated, use "getMainRequest()" instead.', __METHOD__);
|
||||
|
||||
return $this->getMainRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent request of the current.
|
||||
*
|
||||
@@ -86,7 +101,7 @@ class RequestStack
|
||||
* might make it un-compatible with other features of your framework
|
||||
* like ESI support.
|
||||
*
|
||||
* If current Request is the master request, it returns null.
|
||||
* If current Request is the main request, it returns null.
|
||||
*
|
||||
* @return Request|null
|
||||
*/
|
||||
@@ -94,10 +109,20 @@ class RequestStack
|
||||
{
|
||||
$pos = \count($this->requests) - 2;
|
||||
|
||||
if (!isset($this->requests[$pos])) {
|
||||
return;
|
||||
return $this->requests[$pos] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current session.
|
||||
*
|
||||
* @throws SessionNotFoundException
|
||||
*/
|
||||
public function getSession(): SessionInterface
|
||||
{
|
||||
if ((null !== $request = end($this->requests) ?: null) && $request->hasSession()) {
|
||||
return $request->getSession();
|
||||
}
|
||||
|
||||
return $this->requests[$pos];
|
||||
throw new SessionNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
318
vendor/symfony/http-foundation/Response.php
vendored
318
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,90 @@ 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
|
||||
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;
|
||||
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
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
||||
*/
|
||||
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
|
||||
private const HTTP_RESPONSE_CACHE_CONTROL_DIRECTIVES = [
|
||||
'must_revalidate' => false,
|
||||
'no_cache' => false,
|
||||
'no_store' => false,
|
||||
'no_transform' => false,
|
||||
'public' => false,
|
||||
'private' => false,
|
||||
'proxy_revalidate' => false,
|
||||
'max_age' => true,
|
||||
's_maxage' => true,
|
||||
'immutable' => false,
|
||||
'last_modified' => true,
|
||||
'etag' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
|
||||
* @var ResponseHeaderBag
|
||||
*/
|
||||
public $headers;
|
||||
|
||||
@@ -121,8 +137,8 @@ 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 2021-10-01).
|
||||
*
|
||||
* Unless otherwise noted, the status code is defined in RFC2616.
|
||||
*
|
||||
@@ -164,14 +180,14 @@ class Response
|
||||
410 => 'Gone',
|
||||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Payload Too Large',
|
||||
413 => 'Content Too Large', // RFC-ietf-httpbis-semantics
|
||||
414 => 'URI Too Long',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Range Not Satisfiable',
|
||||
417 => 'Expectation Failed',
|
||||
418 => 'I\'m a teapot', // RFC2324
|
||||
421 => 'Misdirected Request', // RFC7540
|
||||
422 => 'Unprocessable Entity', // RFC4918
|
||||
422 => 'Unprocessable Content', // RFC-ietf-httpbis-semantics
|
||||
423 => 'Locked', // RFC4918
|
||||
424 => 'Failed Dependency', // RFC4918
|
||||
425 => 'Too Early', // RFC-ietf-httpbis-replay-04
|
||||
@@ -196,7 +212,7 @@ class Response
|
||||
/**
|
||||
* @throws \InvalidArgumentException When the HTTP status code is not valid
|
||||
*/
|
||||
public function __construct($content = '', int $status = 200, array $headers = [])
|
||||
public function __construct(?string $content = '', int $status = 200, array $headers = [])
|
||||
{
|
||||
$this->headers = new ResponseHeaderBag($headers);
|
||||
$this->setContent($content);
|
||||
@@ -212,14 +228,14 @@ class Response
|
||||
* return Response::create($body, 200)
|
||||
* ->setSharedMaxAge(300);
|
||||
*
|
||||
* @param mixed $content The response content, see setContent()
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @deprecated since Symfony 5.1, use __construct() instead.
|
||||
*/
|
||||
public static function create($content = '', $status = 200, $headers = [])
|
||||
public static function create(?string $content = '', int $status = 200, array $headers = [])
|
||||
{
|
||||
trigger_deprecation('symfony/http-foundation', '5.1', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, static::class);
|
||||
|
||||
return new static($content, $status, $headers);
|
||||
}
|
||||
|
||||
@@ -230,7 +246,7 @@ class Response
|
||||
* one that will be sent to the client only if the prepare() method
|
||||
* has been called before.
|
||||
*
|
||||
* @return string The Response as an HTTP string
|
||||
* @return string
|
||||
*
|
||||
* @see prepare()
|
||||
*/
|
||||
@@ -267,10 +283,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,7 +324,7 @@ class Response
|
||||
}
|
||||
|
||||
// Check if we need to send extra expire info headers
|
||||
if ('1.0' == $this->getProtocolVersion() && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
|
||||
if ('1.0' == $this->getProtocolVersion() && str_contains($headers->get('Cache-Control', ''), 'no-cache')) {
|
||||
$headers->set('pragma', 'no-cache');
|
||||
$headers->set('expires', -1);
|
||||
}
|
||||
@@ -344,7 +362,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,6 +395,8 @@ class Response
|
||||
|
||||
if (\function_exists('fastcgi_finish_request')) {
|
||||
fastcgi_finish_request();
|
||||
} elseif (\function_exists('litespeed_finish_request')) {
|
||||
litespeed_finish_request();
|
||||
} elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
|
||||
static::closeOutputBuffers(0, true);
|
||||
}
|
||||
@@ -387,21 +407,11 @@ class Response
|
||||
/**
|
||||
* Sets the response content.
|
||||
*
|
||||
* Valid types are strings, numbers, null, and objects that implement a __toString() method.
|
||||
*
|
||||
* @param mixed $content Content that can be cast to string
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function setContent($content)
|
||||
public function setContent(?string $content)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
|
||||
$this->content = (string) $content;
|
||||
$this->content = $content ?? '';
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -409,7 +419,7 @@ class Response
|
||||
/**
|
||||
* Gets the current response content.
|
||||
*
|
||||
* @return string Content
|
||||
* @return string|false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
@@ -423,7 +433,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setProtocolVersion(string $version)
|
||||
public function setProtocolVersion(string $version): object
|
||||
{
|
||||
$this->version = $version;
|
||||
|
||||
@@ -452,7 +462,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setStatusCode(int $code, $text = null)
|
||||
public function setStatusCode(int $code, string $text = null): object
|
||||
{
|
||||
$this->statusCode = $code;
|
||||
if ($this->isInvalid()) {
|
||||
@@ -460,7 +470,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;
|
||||
}
|
||||
@@ -493,7 +503,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setCharset(string $charset)
|
||||
public function setCharset(string $charset): object
|
||||
{
|
||||
$this->charset = $charset;
|
||||
|
||||
@@ -574,7 +584,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setPrivate()
|
||||
public function setPrivate(): object
|
||||
{
|
||||
$this->headers->removeCacheControlDirective('public');
|
||||
$this->headers->addCacheControlDirective('private');
|
||||
@@ -591,7 +601,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setPublic()
|
||||
public function setPublic(): object
|
||||
{
|
||||
$this->headers->addCacheControlDirective('public');
|
||||
$this->headers->removeCacheControlDirective('private');
|
||||
@@ -606,7 +616,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setImmutable(bool $immutable = true)
|
||||
public function setImmutable(bool $immutable = true): object
|
||||
{
|
||||
if ($immutable) {
|
||||
$this->headers->addCacheControlDirective('immutable');
|
||||
@@ -628,7 +638,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.
|
||||
@@ -661,7 +671,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setDate(\DateTimeInterface $date)
|
||||
public function setDate(\DateTimeInterface $date): object
|
||||
{
|
||||
if ($date instanceof \DateTime) {
|
||||
$date = \DateTimeImmutable::createFromMutable($date);
|
||||
@@ -684,7 +694,7 @@ class Response
|
||||
return (int) $age;
|
||||
}
|
||||
|
||||
return max(time() - $this->getDate()->format('U'), 0);
|
||||
return max(time() - (int) $this->getDate()->format('U'), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -726,7 +736,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setExpires(\DateTimeInterface $date = null)
|
||||
public function setExpires(\DateTimeInterface $date = null): object
|
||||
{
|
||||
if (null === $date) {
|
||||
$this->headers->remove('Expires');
|
||||
@@ -764,7 +774,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;
|
||||
@@ -779,7 +789,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setMaxAge(int $value)
|
||||
public function setMaxAge(int $value): object
|
||||
{
|
||||
$this->headers->addCacheControlDirective('max-age', $value);
|
||||
|
||||
@@ -795,7 +805,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setSharedMaxAge(int $value)
|
||||
public function setSharedMaxAge(int $value): object
|
||||
{
|
||||
$this->setPublic();
|
||||
$this->headers->addCacheControlDirective('s-maxage', $value);
|
||||
@@ -829,7 +839,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setTtl(int $seconds)
|
||||
public function setTtl(int $seconds): object
|
||||
{
|
||||
$this->setSharedMaxAge($this->getAge() + $seconds);
|
||||
|
||||
@@ -845,7 +855,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setClientTtl(int $seconds)
|
||||
public function setClientTtl(int $seconds): object
|
||||
{
|
||||
$this->setMaxAge($this->getAge() + $seconds);
|
||||
|
||||
@@ -873,7 +883,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setLastModified(\DateTimeInterface $date = null)
|
||||
public function setLastModified(\DateTimeInterface $date = null): object
|
||||
{
|
||||
if (null === $date) {
|
||||
$this->headers->remove('Last-Modified');
|
||||
@@ -911,12 +921,12 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setEtag(string $etag = null, bool $weak = false)
|
||||
public function setEtag(string $etag = null, bool $weak = false): object
|
||||
{
|
||||
if (null === $etag) {
|
||||
$this->headers->remove('Etag');
|
||||
} else {
|
||||
if (0 !== strpos($etag, '"')) {
|
||||
if (!str_starts_with($etag, '"')) {
|
||||
$etag = '"'.$etag.'"';
|
||||
}
|
||||
|
||||
@@ -929,7 +939,7 @@ class Response
|
||||
/**
|
||||
* Sets the response's cache headers (validation and/or expiration).
|
||||
*
|
||||
* Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable.
|
||||
* Available options are: must_revalidate, no_cache, no_store, no_transform, public, private, proxy_revalidate, max_age, s_maxage, immutable, last_modified and etag.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
@@ -937,9 +947,9 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setCache(array $options)
|
||||
public function setCache(array $options): object
|
||||
{
|
||||
if ($diff = array_diff(array_keys($options), ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'])) {
|
||||
if ($diff = array_diff(array_keys($options), array_keys(self::HTTP_RESPONSE_CACHE_CONTROL_DIRECTIVES))) {
|
||||
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff)));
|
||||
}
|
||||
|
||||
@@ -959,6 +969,16 @@ class Response
|
||||
$this->setSharedMaxAge($options['s_maxage']);
|
||||
}
|
||||
|
||||
foreach (self::HTTP_RESPONSE_CACHE_CONTROL_DIRECTIVES as $directive => $hasValue) {
|
||||
if (!$hasValue && isset($options[$directive])) {
|
||||
if ($options[$directive]) {
|
||||
$this->headers->addCacheControlDirective(str_replace('_', '-', $directive));
|
||||
} else {
|
||||
$this->headers->removeCacheControlDirective(str_replace('_', '-', $directive));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['public'])) {
|
||||
if ($options['public']) {
|
||||
$this->setPublic();
|
||||
@@ -975,10 +995,6 @@ class Response
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['immutable'])) {
|
||||
$this->setImmutable((bool) $options['immutable']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -990,11 +1006,11 @@ 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
|
||||
*/
|
||||
public function setNotModified()
|
||||
public function setNotModified(): object
|
||||
{
|
||||
$this->setStatusCode(304);
|
||||
$this->setContent(null);
|
||||
@@ -1024,16 +1040,16 @@ class Response
|
||||
*/
|
||||
public function getVary(): array
|
||||
{
|
||||
if (!$vary = $this->headers->get('Vary', null, false)) {
|
||||
if (!$vary = $this->headers->all('Vary')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ret = [];
|
||||
foreach ($vary as $item) {
|
||||
$ret = array_merge($ret, preg_split('/[\s,]+/', $item));
|
||||
$ret[] = preg_split('/[\s,]+/', $item);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
return array_merge([], ...$ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1046,7 +1062,7 @@ class Response
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setVary($headers, bool $replace = true)
|
||||
public function setVary($headers, bool $replace = true): object
|
||||
{
|
||||
$this->headers->set('Vary', $headers, $replace);
|
||||
|
||||
@@ -1060,8 +1076,6 @@ class Response
|
||||
* If the Response is not modified, it sets the status code to 304 and
|
||||
* removes the actual content by calling the setNotModified() method.
|
||||
*
|
||||
* @return bool true if the Response validators match the Request, false otherwise
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function isNotModified(Request $request): bool
|
||||
@@ -1074,12 +1088,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 +1120,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
|
||||
*/
|
||||
@@ -1208,11 +1236,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) {
|
||||
@@ -1223,6 +1251,22 @@ class Response
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a response as safe according to RFC8674.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc8674
|
||||
*/
|
||||
public function setContentSafe(bool $safe = true): void
|
||||
{
|
||||
if ($safe) {
|
||||
$this->headers->set('Preference-Applied', 'safe');
|
||||
} elseif ('safe' === $this->headers->get('Preference-Applied')) {
|
||||
$this->headers->remove('Preference-Applied');
|
||||
}
|
||||
|
||||
$this->setVary('Prefer', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9.
|
||||
*
|
||||
@@ -1230,9 +1274,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,11 +18,11 @@ 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 = [];
|
||||
protected $cookies = [];
|
||||
@@ -45,13 +45,13 @@ class ResponseHeaderBag extends HeaderBag
|
||||
/**
|
||||
* Returns the headers, with original capitalizations.
|
||||
*
|
||||
* @return array An array of headers
|
||||
* @return array
|
||||
*/
|
||||
public function allPreserveCase()
|
||||
{
|
||||
$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;
|
||||
@@ -88,9 +88,16 @@ class ResponseHeaderBag extends HeaderBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function all()
|
||||
public function all(string $key = null)
|
||||
{
|
||||
$headers = parent::all();
|
||||
|
||||
if (null !== $key) {
|
||||
$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;
|
||||
}
|
||||
@@ -101,9 +108,9 @@ class ResponseHeaderBag extends HeaderBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($key, $values, $replace = true)
|
||||
public function set(string $key, $values, bool $replace = true)
|
||||
{
|
||||
$uniqueKey = str_replace('_', '-', strtolower($key));
|
||||
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
if ('set-cookie' === $uniqueKey) {
|
||||
if ($replace) {
|
||||
@@ -132,9 +139,9 @@ class ResponseHeaderBag extends HeaderBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($key)
|
||||
public function remove(string $key)
|
||||
{
|
||||
$uniqueKey = str_replace('_', '-', strtolower($key));
|
||||
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
|
||||
unset($this->headerNames[$uniqueKey]);
|
||||
|
||||
if ('set-cookie' === $uniqueKey) {
|
||||
@@ -157,7 +164,7 @@ class ResponseHeaderBag extends HeaderBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCacheControlDirective($key)
|
||||
public function hasCacheControlDirective(string $key)
|
||||
{
|
||||
return \array_key_exists($key, $this->computedCacheControl);
|
||||
}
|
||||
@@ -165,9 +172,9 @@ class ResponseHeaderBag extends HeaderBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheControlDirective($key)
|
||||
public function getCacheControlDirective(string $key)
|
||||
{
|
||||
return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
|
||||
return $this->computedCacheControl[$key] ?? null;
|
||||
}
|
||||
|
||||
public function setCookie(Cookie $cookie)
|
||||
@@ -178,12 +185,8 @@ class ResponseHeaderBag extends HeaderBag
|
||||
|
||||
/**
|
||||
* Removes a cookie from the array, but does not unset it in the browser.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @param string $domain
|
||||
*/
|
||||
public function removeCookie($name, $path = '/', $domain = null)
|
||||
public function removeCookie(string $name, ?string $path = '/', string $domain = null)
|
||||
{
|
||||
if (null === $path) {
|
||||
$path = '/';
|
||||
@@ -207,13 +210,11 @@ class ResponseHeaderBag extends HeaderBag
|
||||
/**
|
||||
* Returns an array with all cookies.
|
||||
*
|
||||
* @param string $format
|
||||
*
|
||||
* @return Cookie[]
|
||||
*
|
||||
* @throws \InvalidArgumentException When the $format is invalid
|
||||
*/
|
||||
public function getCookies($format = self::COOKIES_FLAT)
|
||||
public function getCookies(string $format = self::COOKIES_FLAT)
|
||||
{
|
||||
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])));
|
||||
@@ -237,24 +238,18 @@ class ResponseHeaderBag extends HeaderBag
|
||||
|
||||
/**
|
||||
* Clears a cookie in the browser.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @param string $domain
|
||||
* @param bool $secure
|
||||
* @param bool $httpOnly
|
||||
*/
|
||||
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
|
||||
public function clearCookie(string $name, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true, string $sameSite = null)
|
||||
{
|
||||
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
|
||||
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, $sameSite));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see HeaderUtils::makeDisposition()
|
||||
*/
|
||||
public function makeDisposition($disposition, $filename, $filenameFallback = '')
|
||||
public function makeDisposition(string $disposition, string $filename, string $filenameFallback = '')
|
||||
{
|
||||
return HeaderUtils::makeDisposition((string) $disposition, (string) $filename, (string) $filenameFallback);
|
||||
return HeaderUtils::makeDisposition($disposition, $filename, $filenameFallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,13 +262,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();
|
||||
@@ -289,10 +284,8 @@ class ResponseHeaderBag extends HeaderBag
|
||||
return $header;
|
||||
}
|
||||
|
||||
private function initDate()
|
||||
private function initDate(): void
|
||||
{
|
||||
$now = \DateTime::createFromFormat('U', time());
|
||||
$now->setTimezone(new \DateTimeZone('UTC'));
|
||||
$this->set('Date', $now->format('D, d M Y H:i:s').' GMT');
|
||||
$this->set('Date', gmdate('D, d M Y H:i:s').' GMT');
|
||||
}
|
||||
}
|
||||
|
||||
23
vendor/symfony/http-foundation/ServerBag.php
vendored
23
vendor/symfony/http-foundation/ServerBag.php
vendored
@@ -28,31 +28,28 @@ class ServerBag extends ParameterBag
|
||||
public function getHeaders()
|
||||
{
|
||||
$headers = [];
|
||||
$contentHeaders = ['CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true];
|
||||
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'];
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Symfony\Component\HttpFoundation\Session\Attribute;
|
||||
|
||||
/**
|
||||
* This class relates to session attribute storage.
|
||||
*
|
||||
* @implements \IteratorAggregate<string, mixed>
|
||||
*/
|
||||
class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable
|
||||
{
|
||||
@@ -37,7 +39,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
@@ -61,7 +63,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($name)
|
||||
public function has(string $name)
|
||||
{
|
||||
return \array_key_exists($name, $this->attributes);
|
||||
}
|
||||
@@ -69,7 +71,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($name, $default = null)
|
||||
public function get(string $name, $default = null)
|
||||
{
|
||||
return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
|
||||
}
|
||||
@@ -77,7 +79,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($name, $value)
|
||||
public function set(string $name, $value)
|
||||
{
|
||||
$this->attributes[$name] = $value;
|
||||
}
|
||||
@@ -104,7 +106,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($name)
|
||||
public function remove(string $name)
|
||||
{
|
||||
$retval = null;
|
||||
if (\array_key_exists($name, $this->attributes)) {
|
||||
@@ -129,8 +131,9 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
/**
|
||||
* Returns an iterator for attributes.
|
||||
*
|
||||
* @return \ArrayIterator An \ArrayIterator instance
|
||||
* @return \ArrayIterator<string, mixed>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->attributes);
|
||||
@@ -139,8 +142,9 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
||||
/**
|
||||
* Returns the number of attributes.
|
||||
*
|
||||
* @return int The number of attributes
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \count($this->attributes);
|
||||
|
||||
@@ -23,50 +23,39 @@ interface AttributeBagInterface extends SessionBagInterface
|
||||
/**
|
||||
* Checks if an attribute is defined.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return bool true if the attribute is defined, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name);
|
||||
public function has(string $name);
|
||||
|
||||
/**
|
||||
* Returns an attribute.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
* @param mixed $default The default value if not found
|
||||
* @param mixed $default The default value if not found
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = null);
|
||||
public function get(string $name, $default = null);
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set($name, $value);
|
||||
public function set(string $name, $value);
|
||||
|
||||
/**
|
||||
* Returns attributes.
|
||||
*
|
||||
* @return array Attributes
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function all();
|
||||
|
||||
/**
|
||||
* Sets attributes.
|
||||
*
|
||||
* @param array $attributes Attributes
|
||||
*/
|
||||
public function replace(array $attributes);
|
||||
|
||||
/**
|
||||
* Removes an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return mixed The removed value or null when it does not exist
|
||||
*/
|
||||
public function remove($name);
|
||||
public function remove(string $name);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,15 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Attribute;
|
||||
|
||||
trigger_deprecation('symfony/http-foundation', '5.3', 'The "%s" class is deprecated.', NamespacedAttributeBag::class);
|
||||
|
||||
/**
|
||||
* This class provides structured storage of session attributes using
|
||||
* a name spacing character in the key.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*
|
||||
* @deprecated since Symfony 5.3
|
||||
*/
|
||||
class NamespacedAttributeBag extends AttributeBag
|
||||
{
|
||||
@@ -34,7 +38,7 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($name)
|
||||
public function has(string $name)
|
||||
{
|
||||
// reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
|
||||
$attributes = $this->resolveAttributePath($name);
|
||||
@@ -50,7 +54,7 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($name, $default = null)
|
||||
public function get(string $name, $default = null)
|
||||
{
|
||||
// reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
|
||||
$attributes = $this->resolveAttributePath($name);
|
||||
@@ -66,7 +70,7 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($name, $value)
|
||||
public function set(string $name, $value)
|
||||
{
|
||||
$attributes = &$this->resolveAttributePath($name, true);
|
||||
$name = $this->resolveKey($name);
|
||||
@@ -76,7 +80,7 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($name)
|
||||
public function remove(string $name)
|
||||
{
|
||||
$retval = null;
|
||||
$attributes = &$this->resolveAttributePath($name);
|
||||
@@ -97,12 +101,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)
|
||||
protected function &resolveAttributePath(string $name, bool $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) {
|
||||
@@ -144,11 +148,9 @@ class NamespacedAttributeBag extends AttributeBag
|
||||
*
|
||||
* This is the last part in a dot separated string.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveKey($name)
|
||||
protected function resolveKey(string $name)
|
||||
{
|
||||
if (false !== $pos = strrpos($name, $this->namespaceCharacter)) {
|
||||
$name = substr($name, $pos + 1);
|
||||
|
||||
@@ -38,7 +38,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
@@ -60,7 +60,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add($type, $message)
|
||||
public function add(string $type, $message)
|
||||
{
|
||||
$this->flashes['new'][$type][] = $message;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function peek($type, array $default = [])
|
||||
public function peek(string $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'] : [];
|
||||
return \array_key_exists('display', $this->flashes) ? $this->flashes['display'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($type, array $default = [])
|
||||
public function get(string $type, array $default = [])
|
||||
{
|
||||
$return = $default;
|
||||
|
||||
@@ -122,7 +122,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($type, $messages)
|
||||
public function set(string $type, $messages)
|
||||
{
|
||||
$this->flashes['new'][$type] = (array) $messages;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($type)
|
||||
public function has(string $type)
|
||||
{
|
||||
return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class FlashBag implements FlashBagInterface
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class FlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add($type, $message)
|
||||
public function add(string $type, $message)
|
||||
{
|
||||
$this->flashes[$type][] = $message;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class FlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function peek($type, array $default = [])
|
||||
public function peek(string $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 = [])
|
||||
public function get(string $type, array $default = [])
|
||||
{
|
||||
if (!$this->has($type)) {
|
||||
return $default;
|
||||
@@ -105,7 +105,7 @@ class FlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($type, $messages)
|
||||
public function set(string $type, $messages)
|
||||
{
|
||||
$this->flashes[$type] = (array) $messages;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class FlashBag implements FlashBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($type)
|
||||
public function has(string $type)
|
||||
{
|
||||
return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
|
||||
}
|
||||
|
||||
@@ -23,18 +23,16 @@ interface FlashBagInterface extends SessionBagInterface
|
||||
/**
|
||||
* Adds a flash message for the given type.
|
||||
*
|
||||
* @param string $type
|
||||
* @param mixed $message
|
||||
* @param mixed $message
|
||||
*/
|
||||
public function add($type, $message);
|
||||
public function add(string $type, $message);
|
||||
|
||||
/**
|
||||
* Registers one or more messages for a given type.
|
||||
*
|
||||
* @param string $type
|
||||
* @param string|array $messages
|
||||
*/
|
||||
public function set($type, $messages);
|
||||
public function set(string $type, $messages);
|
||||
|
||||
/**
|
||||
* Gets flash messages for a given type.
|
||||
@@ -44,7 +42,7 @@ interface FlashBagInterface extends SessionBagInterface
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function peek($type, array $default = []);
|
||||
public function peek(string $type, array $default = []);
|
||||
|
||||
/**
|
||||
* Gets all flash messages.
|
||||
@@ -56,12 +54,11 @@ interface FlashBagInterface extends SessionBagInterface
|
||||
/**
|
||||
* Gets and clears flash from the stack.
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $default Default value if $type does not exist
|
||||
* @param array $default Default value if $type does not exist
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get($type, array $default = []);
|
||||
public function get(string $type, array $default = []);
|
||||
|
||||
/**
|
||||
* Gets and clears flashes from the stack.
|
||||
@@ -78,11 +75,9 @@ interface FlashBagInterface extends SessionBagInterface
|
||||
/**
|
||||
* Has flash messages for a given type?
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($type);
|
||||
public function has(string $type);
|
||||
|
||||
/**
|
||||
* Returns a list of all defined types.
|
||||
|
||||
@@ -18,9 +18,16 @@ 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>
|
||||
*
|
||||
* @implements \IteratorAggregate<string, mixed>
|
||||
*/
|
||||
class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
{
|
||||
@@ -30,21 +37,18 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
private $attributeName;
|
||||
private $data = [];
|
||||
private $usageIndex = 0;
|
||||
private $usageReporter;
|
||||
|
||||
/**
|
||||
* @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)
|
||||
public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null, callable $usageReporter = null)
|
||||
{
|
||||
$this->storage = $storage ?: new NativeSessionStorage();
|
||||
$this->storage = $storage ?? new NativeSessionStorage();
|
||||
$this->usageReporter = $usageReporter;
|
||||
|
||||
$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);
|
||||
}
|
||||
@@ -60,7 +64,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($name)
|
||||
public function has(string $name)
|
||||
{
|
||||
return $this->getAttributeBag()->has($name);
|
||||
}
|
||||
@@ -68,7 +72,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($name, $default = null)
|
||||
public function get(string $name, $default = null)
|
||||
{
|
||||
return $this->getAttributeBag()->get($name, $default);
|
||||
}
|
||||
@@ -76,7 +80,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($name, $value)
|
||||
public function set(string $name, $value)
|
||||
{
|
||||
$this->getAttributeBag()->set($name, $value);
|
||||
}
|
||||
@@ -100,7 +104,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($name)
|
||||
public function remove(string $name)
|
||||
{
|
||||
return $this->getAttributeBag()->remove($name);
|
||||
}
|
||||
@@ -124,8 +128,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* Returns an iterator for attributes.
|
||||
*
|
||||
* @return \ArrayIterator An \ArrayIterator instance
|
||||
* @return \ArrayIterator<string, mixed>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->getAttributeBag()->all());
|
||||
@@ -134,32 +139,29 @@ 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;
|
||||
if ($this->usageReporter && 0 <= $this->usageIndex) {
|
||||
($this->usageReporter)();
|
||||
}
|
||||
}
|
||||
foreach ($this->data as &$data) {
|
||||
if (!empty($data)) {
|
||||
@@ -173,7 +175,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidate($lifetime = null)
|
||||
public function invalidate(int $lifetime = null)
|
||||
{
|
||||
$this->storage->clear();
|
||||
|
||||
@@ -183,7 +185,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function migrate($destroy = false, $lifetime = null)
|
||||
public function migrate(bool $destroy = false, int $lifetime = null)
|
||||
{
|
||||
return $this->storage->regenerate($destroy, $lifetime);
|
||||
}
|
||||
@@ -207,7 +209,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setId($id)
|
||||
public function setId(string $id)
|
||||
{
|
||||
if ($this->storage->getId() !== $id) {
|
||||
$this->storage->setId($id);
|
||||
@@ -225,7 +227,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->storage->setName($name);
|
||||
}
|
||||
@@ -236,6 +238,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
public function getMetadataBag()
|
||||
{
|
||||
++$this->usageIndex;
|
||||
if ($this->usageReporter && 0 <= $this->usageIndex) {
|
||||
($this->usageReporter)();
|
||||
}
|
||||
|
||||
return $this->storage->getMetadataBag();
|
||||
}
|
||||
@@ -245,15 +250,17 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
||||
*/
|
||||
public function registerBag(SessionBagInterface $bag)
|
||||
{
|
||||
$this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->usageIndex));
|
||||
$this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->usageIndex, $this->usageReporter));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBag($name)
|
||||
public function getBag(string $name)
|
||||
{
|
||||
return $this->storage->getBag($name)->getBag();
|
||||
$bag = $this->storage->getBag($name);
|
||||
|
||||
return method_exists($bag, 'getBag') ? $bag->getBag() : $bag;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,10 +277,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);
|
||||
}
|
||||
|
||||
@@ -21,33 +21,35 @@ final class SessionBagProxy implements SessionBagInterface
|
||||
private $bag;
|
||||
private $data;
|
||||
private $usageIndex;
|
||||
private $usageReporter;
|
||||
|
||||
public function __construct(SessionBagInterface $bag, array &$data, &$usageIndex)
|
||||
public function __construct(SessionBagInterface $bag, array &$data, ?int &$usageIndex, ?callable $usageReporter)
|
||||
{
|
||||
$this->bag = $bag;
|
||||
$this->data = &$data;
|
||||
$this->usageIndex = &$usageIndex;
|
||||
$this->usageReporter = $usageReporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SessionBagInterface
|
||||
*/
|
||||
public function getBag()
|
||||
public function getBag(): SessionBagInterface
|
||||
{
|
||||
++$this->usageIndex;
|
||||
if ($this->usageReporter && 0 <= $this->usageIndex) {
|
||||
($this->usageReporter)();
|
||||
}
|
||||
|
||||
return $this->bag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
if (!isset($this->data[$this->bag->getStorageKey()])) {
|
||||
return true;
|
||||
}
|
||||
++$this->usageIndex;
|
||||
if ($this->usageReporter && 0 <= $this->usageIndex) {
|
||||
($this->usageReporter)();
|
||||
}
|
||||
|
||||
return empty($this->data[$this->bag->getStorageKey()]);
|
||||
}
|
||||
@@ -55,7 +57,7 @@ final class SessionBagProxy implements SessionBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->bag->getName();
|
||||
}
|
||||
@@ -63,9 +65,13 @@ final class SessionBagProxy implements SessionBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initialize(array &$array)
|
||||
public function initialize(array &$array): void
|
||||
{
|
||||
++$this->usageIndex;
|
||||
if ($this->usageReporter && 0 <= $this->usageIndex) {
|
||||
($this->usageReporter)();
|
||||
}
|
||||
|
||||
$this->data[$this->bag->getStorageKey()] = &$array;
|
||||
|
||||
$this->bag->initialize($array);
|
||||
@@ -74,7 +80,7 @@ final class SessionBagProxy implements SessionBagInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStorageKey()
|
||||
public function getStorageKey(): string
|
||||
{
|
||||
return $this->bag->getStorageKey();
|
||||
}
|
||||
|
||||
40
vendor/symfony/http-foundation/Session/SessionFactory.php
vendored
Normal file
40
vendor/symfony/http-foundation/Session/SessionFactory.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageFactoryInterface;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(Session::class);
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class SessionFactory implements SessionFactoryInterface
|
||||
{
|
||||
private $requestStack;
|
||||
private $storageFactory;
|
||||
private $usageReporter;
|
||||
|
||||
public function __construct(RequestStack $requestStack, SessionStorageFactoryInterface $storageFactory, callable $usageReporter = null)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->storageFactory = $storageFactory;
|
||||
$this->usageReporter = $usageReporter;
|
||||
}
|
||||
|
||||
public function createSession(): SessionInterface
|
||||
{
|
||||
return new Session($this->storageFactory->createStorage($this->requestStack->getMainRequest()), null, null, $this->usageReporter);
|
||||
}
|
||||
}
|
||||
20
vendor/symfony/http-foundation/Session/SessionFactoryInterface.php
vendored
Normal file
20
vendor/symfony/http-foundation/Session/SessionFactoryInterface.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
*/
|
||||
interface SessionFactoryInterface
|
||||
{
|
||||
public function createSession(): SessionInterface;
|
||||
}
|
||||
@@ -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,30 +32,26 @@ interface SessionInterface
|
||||
/**
|
||||
* Returns the session ID.
|
||||
*
|
||||
* @return string The session ID
|
||||
* @return string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function setId($id);
|
||||
public function setId(string $id);
|
||||
|
||||
/**
|
||||
* Returns the session name.
|
||||
*
|
||||
* @return mixed The session name
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Sets the session name.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name);
|
||||
public function setName(string $name);
|
||||
|
||||
/**
|
||||
* Invalidates the current session.
|
||||
@@ -68,9 +64,9 @@ 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);
|
||||
public function invalidate(int $lifetime = null);
|
||||
|
||||
/**
|
||||
* Migrates the current session to a new session id while maintaining all
|
||||
@@ -82,9 +78,9 @@ 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);
|
||||
public function migrate(bool $destroy = false, int $lifetime = null);
|
||||
|
||||
/**
|
||||
* Force the session to be saved and closed.
|
||||
@@ -98,52 +94,44 @@ interface SessionInterface
|
||||
/**
|
||||
* Checks if an attribute is defined.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return bool true if the attribute is defined, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name);
|
||||
public function has(string $name);
|
||||
|
||||
/**
|
||||
* Returns an attribute.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
* @param mixed $default The default value if not found
|
||||
* @param mixed $default The default value if not found
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = null);
|
||||
public function get(string $name, $default = null);
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set($name, $value);
|
||||
public function set(string $name, $value);
|
||||
|
||||
/**
|
||||
* Returns attributes.
|
||||
*
|
||||
* @return array Attributes
|
||||
* @return array
|
||||
*/
|
||||
public function all();
|
||||
|
||||
/**
|
||||
* Sets attributes.
|
||||
*
|
||||
* @param array $attributes Attributes
|
||||
*/
|
||||
public function replace(array $attributes);
|
||||
|
||||
/**
|
||||
* Removes an attribute.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return mixed The removed value or null when it does not exist
|
||||
*/
|
||||
public function remove($name);
|
||||
public function remove(string $name);
|
||||
|
||||
/**
|
||||
* Clears all attributes.
|
||||
@@ -165,11 +153,9 @@ interface SessionInterface
|
||||
/**
|
||||
* Gets a bag instance by name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return SessionBagInterface
|
||||
*/
|
||||
public function getBag($name);
|
||||
public function getBag(string $name);
|
||||
|
||||
/**
|
||||
* Gets session meta.
|
||||
|
||||
@@ -29,8 +29,9 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess
|
||||
private $igbinaryEmptyData;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$this->sessionName = $sessionName;
|
||||
@@ -42,41 +43,45 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function doRead($sessionId);
|
||||
abstract protected function doRead(string $sessionId);
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
* @param string $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function doWrite($sessionId, $data);
|
||||
abstract protected function doWrite(string $sessionId, string $data);
|
||||
|
||||
/**
|
||||
* @param string $sessionId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function doDestroy($sessionId);
|
||||
abstract protected function doDestroy(string $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,8 +103,9 @@ abstract class AbstractSessionHandler implements \SessionHandlerInterface, \Sess
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
if (null === $this->igbinaryEmptyData) {
|
||||
@@ -115,18 +121,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']);
|
||||
|
||||
42
vendor/symfony/http-foundation/Session/Storage/Handler/IdentityMarshaller.php
vendored
Normal file
42
vendor/symfony/http-foundation/Session/Storage/Handler/IdentityMarshaller.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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 Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
|
||||
/**
|
||||
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
|
||||
*/
|
||||
class IdentityMarshaller implements MarshallerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function marshall(array $values, ?array &$failed): array
|
||||
{
|
||||
foreach ($values as $key => $value) {
|
||||
if (!\is_string($value)) {
|
||||
throw new \LogicException(sprintf('%s accepts only string as data.', __METHOD__));
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unmarshall(string $value): string
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
108
vendor/symfony/http-foundation/Session/Storage/Handler/MarshallingSessionHandler.php
vendored
Normal file
108
vendor/symfony/http-foundation/Session/Storage/Handler/MarshallingSessionHandler.php
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<?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 Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
|
||||
/**
|
||||
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
|
||||
*/
|
||||
class MarshallingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
|
||||
{
|
||||
private $handler;
|
||||
private $marshaller;
|
||||
|
||||
public function __construct(AbstractSessionHandler $handler, MarshallerInterface $marshaller)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
$this->marshaller = $marshaller;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $name)
|
||||
{
|
||||
return $this->handler->open($savePath, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
return $this->handler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return $this->handler->destroy($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return $this->handler->gc($maxlifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($sessionId)
|
||||
{
|
||||
return $this->marshaller->unmarshall($this->handler->read($sessionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
$failed = [];
|
||||
$marshalledData = $this->marshaller->marshall(['data' => $data], $failed);
|
||||
|
||||
if (isset($failed['data'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->handler->write($sessionId, $marshalledData['data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
return $this->handler->validateId($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return $this->handler->updateTimestamp($sessionId, $data);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -38,10 +38,7 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
||||
*
|
||||
* List of available options:
|
||||
* * prefix: The prefix to use for the memcached keys in order to avoid collision
|
||||
* * expiretime: The time to live in seconds.
|
||||
*
|
||||
* @param \Memcached $memcached A \Memcached instance
|
||||
* @param array $options An associative array of Memcached options
|
||||
* * ttl: The time to live in seconds.
|
||||
*
|
||||
* @throws \InvalidArgumentException When unsupported options are passed
|
||||
*/
|
||||
@@ -49,17 +46,18 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
$this->memcached = $memcached;
|
||||
|
||||
if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
|
||||
if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime', 'ttl'])) {
|
||||
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->ttl = $options['expiretime'] ?? $options['ttl'] ?? null;
|
||||
$this->prefix = $options['prefix'] ?? 'sf2s';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
return $this->memcached->quit();
|
||||
@@ -68,17 +66,18 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doRead($sessionId)
|
||||
protected function doRead(string $sessionId)
|
||||
{
|
||||
return $this->memcached->get($this->prefix.$sessionId) ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
$this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl);
|
||||
$this->memcached->touch($this->prefix.$sessionId, time() + (int) ($this->ttl ?? ini_get('session.gc_maxlifetime')));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -86,15 +85,15 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
protected function doWrite(string $sessionId, string $data)
|
||||
{
|
||||
return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
|
||||
return $this->memcached->set($this->prefix.$sessionId, $data, time() + (int) ($this->ttl ?? ini_get('session.gc_maxlifetime')));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
protected function doDestroy(string $sessionId)
|
||||
{
|
||||
$result = $this->memcached->delete($this->prefix.$sessionId);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,14 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
*/
|
||||
class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var \SessionHandlerInterface&\SessionUpdateTimestampHandlerInterface
|
||||
*/
|
||||
private $currentHandler;
|
||||
|
||||
/**
|
||||
* @var \SessionHandlerInterface&\SessionUpdateTimestampHandlerInterface
|
||||
*/
|
||||
private $writeOnlyHandler;
|
||||
|
||||
public function __construct(\SessionHandlerInterface $currentHandler, \SessionHandlerInterface $writeOnlyHandler)
|
||||
@@ -39,8 +46,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
$result = $this->currentHandler->close();
|
||||
@@ -50,8 +58,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
$result = $this->currentHandler->destroy($sessionId);
|
||||
@@ -61,8 +70,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$result = $this->currentHandler->gc($maxlifetime);
|
||||
@@ -72,8 +82,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$result = $this->currentHandler->open($savePath, $sessionName);
|
||||
@@ -83,8 +94,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($sessionId)
|
||||
{
|
||||
// No reading from new handler until switch-over
|
||||
@@ -92,8 +104,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function write($sessionId, $sessionData)
|
||||
{
|
||||
$result = $this->currentHandler->write($sessionId, $sessionData);
|
||||
@@ -103,8 +116,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
// No reading from new handler until switch-over
|
||||
@@ -112,8 +126,9 @@ class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdat
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $sessionData)
|
||||
{
|
||||
$result = $this->currentHandler->updateTimestamp($sessionId, $sessionData);
|
||||
|
||||
@@ -11,20 +11,25 @@
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
use MongoDB\BSON\Binary;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use MongoDB\Client;
|
||||
use MongoDB\Collection;
|
||||
|
||||
/**
|
||||
* Session handler using the mongodb/mongodb package and MongoDB driver extension.
|
||||
*
|
||||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||
*
|
||||
* @see https://packagist.org/packages/mongodb/mongodb
|
||||
* @see http://php.net/manual/en/set.mongodb.php
|
||||
* @see https://php.net/mongodb
|
||||
*/
|
||||
class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
private $mongo;
|
||||
|
||||
/**
|
||||
* @var \MongoDB\Collection
|
||||
* @var Collection
|
||||
*/
|
||||
private $collection;
|
||||
|
||||
@@ -51,25 +56,22 @@ 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)
|
||||
public function __construct(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;
|
||||
@@ -83,8 +85,9 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
@@ -93,7 +96,7 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
protected function doDestroy(string $sessionId)
|
||||
{
|
||||
$this->getCollection()->deleteOne([
|
||||
$this->options['id_field'] => $sessionId,
|
||||
@@ -103,28 +106,27 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|false
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$this->getCollection()->deleteMany([
|
||||
$this->options['expiry_field'] => ['$lt' => new \MongoDB\BSON\UTCDateTime()],
|
||||
]);
|
||||
|
||||
return true;
|
||||
return $this->getCollection()->deleteMany([
|
||||
$this->options['expiry_field'] => ['$lt' => new UTCDateTime()],
|
||||
])->getDeletedCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
protected function doWrite(string $sessionId, string $data)
|
||||
{
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
$expiry = new UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
|
||||
$fields = [
|
||||
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
|
||||
$this->options['time_field'] => new UTCDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
$this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY),
|
||||
$this->options['data_field'] => new Binary($data, Binary::TYPE_OLD_BINARY),
|
||||
];
|
||||
|
||||
$this->getCollection()->updateOne(
|
||||
@@ -137,16 +139,17 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
$expiry = new UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
|
||||
$this->getCollection()->updateOne(
|
||||
[$this->options['id_field'] => $sessionId],
|
||||
['$set' => [
|
||||
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
|
||||
$this->options['time_field'] => new UTCDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
]]
|
||||
);
|
||||
@@ -157,11 +160,11 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doRead($sessionId)
|
||||
protected function doRead(string $sessionId)
|
||||
{
|
||||
$dbData = $this->getCollection()->findOne([
|
||||
$this->options['id_field'] => $sessionId,
|
||||
$this->options['expiry_field'] => ['$gte' => new \MongoDB\BSON\UTCDateTime()],
|
||||
$this->options['expiry_field'] => ['$gte' => new UTCDateTime()],
|
||||
]);
|
||||
|
||||
if (null === $dbData) {
|
||||
@@ -171,10 +174,7 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
return $dbData[$this->options['data_field']]->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \MongoDB\Collection
|
||||
*/
|
||||
private function getCollection()
|
||||
private function getCollection(): Collection
|
||||
{
|
||||
if (null === $this->collection) {
|
||||
$this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
|
||||
@@ -184,7 +184,7 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \MongoDB\Client
|
||||
* @return Client
|
||||
*/
|
||||
protected function getMongo()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -38,7 +38,7 @@ class NativeFileSessionHandler extends \SessionHandler
|
||||
|
||||
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;
|
||||
@@ -37,14 +39,15 @@ class NullSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doRead($sessionId)
|
||||
protected function doRead(string $sessionId)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return true;
|
||||
@@ -53,7 +56,7 @@ class NullSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
protected function doWrite(string $sessionId, string $data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -61,16 +64,17 @@ class NullSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
protected function doDestroy(string $sessionId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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
|
||||
@@ -71,57 +73,67 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
private $pdo;
|
||||
|
||||
/**
|
||||
* @var string|false|null DSN string or null for session.save_path or false when lazy connection disabled
|
||||
* DSN string or null for session.save_path or false when lazy connection disabled.
|
||||
*
|
||||
* @var string|false|null
|
||||
*/
|
||||
private $dsn = false;
|
||||
|
||||
/**
|
||||
* @var string Database driver
|
||||
* @var string|null
|
||||
*/
|
||||
private $driver;
|
||||
|
||||
/**
|
||||
* @var string Table name
|
||||
* @var string
|
||||
*/
|
||||
private $table = 'sessions';
|
||||
|
||||
/**
|
||||
* @var string Column for session id
|
||||
* @var string
|
||||
*/
|
||||
private $idCol = 'sess_id';
|
||||
|
||||
/**
|
||||
* @var string Column for session data
|
||||
* @var string
|
||||
*/
|
||||
private $dataCol = 'sess_data';
|
||||
|
||||
/**
|
||||
* @var string Column for lifetime
|
||||
* @var string
|
||||
*/
|
||||
private $lifetimeCol = 'sess_lifetime';
|
||||
|
||||
/**
|
||||
* @var string Column for timestamp
|
||||
* @var string
|
||||
*/
|
||||
private $timeCol = 'sess_time';
|
||||
|
||||
/**
|
||||
* @var string Username when lazy-connect
|
||||
* Username when lazy-connect.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $username = '';
|
||||
|
||||
/**
|
||||
* @var string Password when lazy-connect
|
||||
* Password when lazy-connect.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $password = '';
|
||||
|
||||
/**
|
||||
* @var array Connection options when lazy-connect
|
||||
* Connection options when lazy-connect.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $connectionOptions = [];
|
||||
|
||||
/**
|
||||
* @var int The strategy for locking, see constants
|
||||
* The strategy for locking, see constants.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $lockMode = self::LOCK_TRANSACTIONAL;
|
||||
|
||||
@@ -133,17 +145,23 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
private $unlockStatements = [];
|
||||
|
||||
/**
|
||||
* @var bool True when the current session exists but expired according to session.gc_maxlifetime
|
||||
* True when the current session exists but expired according to session.gc_maxlifetime.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $sessionExpired = false;
|
||||
|
||||
/**
|
||||
* @var bool Whether a transaction is active
|
||||
* Whether a transaction is active.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $inTransaction = false;
|
||||
|
||||
/**
|
||||
* @var bool Whether gc() has been called
|
||||
* Whether gc() has been called.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $gcCalled = false;
|
||||
|
||||
@@ -165,7 +183,6 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
* * 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
|
||||
*/
|
||||
@@ -173,26 +190,26 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
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 +235,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 +255,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();
|
||||
|
||||
@@ -250,7 +268,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
*
|
||||
* Can be used to distinguish between a new session and one that expired due to inactivity.
|
||||
*
|
||||
* @return bool Whether current session expired
|
||||
* @return bool
|
||||
*/
|
||||
public function isSessionExpired()
|
||||
{
|
||||
@@ -258,8 +276,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$this->sessionExpired = false;
|
||||
@@ -272,8 +291,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($sessionId)
|
||||
{
|
||||
try {
|
||||
@@ -286,21 +306,22 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
protected function doDestroy(string $sessionId)
|
||||
{
|
||||
// delete the record associated with this id
|
||||
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
|
||||
@@ -321,7 +342,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
protected function doWrite(string $sessionId, string $data)
|
||||
{
|
||||
$maxlifetime = (int) ini_get('session.gc_maxlifetime');
|
||||
|
||||
@@ -348,7 +369,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 +386,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 +411,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
$this->commit();
|
||||
@@ -403,19 +426,27 @@ 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();
|
||||
}
|
||||
|
||||
if (false !== $this->dsn) {
|
||||
$this->pdo = null; // only close lazy-connection
|
||||
$this->driver = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -423,10 +454,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 +465,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,7 +490,7 @@ 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 = [
|
||||
@@ -476,17 +501,39 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
'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);
|
||||
}
|
||||
|
||||
$dsn = null;
|
||||
switch ($driver) {
|
||||
case 'mysql':
|
||||
$dsn = 'mysql:';
|
||||
if ('' !== ($params['query'] ?? '')) {
|
||||
$queryParams = [];
|
||||
parse_str($params['query'], $queryParams);
|
||||
if ('' !== ($queryParams['charset'] ?? '')) {
|
||||
$dsn .= 'charset='.$queryParams['charset'].';';
|
||||
}
|
||||
|
||||
if ('' !== ($queryParams['unix_socket'] ?? '')) {
|
||||
$dsn .= 'unix_socket='.$queryParams['unix_socket'].';';
|
||||
|
||||
if (isset($params['path'])) {
|
||||
$dbName = substr($params['path'], 1); // Remove the leading slash
|
||||
$dsn .= 'dbname='.$dbName.';';
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
}
|
||||
// If "unix_socket" is not in the query, we continue with the same process as pgsql
|
||||
// no break
|
||||
case 'pgsql':
|
||||
$dsn = $driver.':';
|
||||
$dsn ?? $dsn = 'pgsql:';
|
||||
|
||||
if (isset($params['host']) && '' !== $params['host']) {
|
||||
$dsn .= 'host='.$params['host'].';';
|
||||
@@ -538,10 +585,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 +606,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* Helper method to commit a transaction.
|
||||
*/
|
||||
private function commit()
|
||||
private function commit(): void
|
||||
{
|
||||
if ($this->inTransaction) {
|
||||
try {
|
||||
@@ -581,7 +628,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
|
||||
@@ -603,11 +650,9 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
* We need to make sure we do not return session data that is already considered garbage according
|
||||
* to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes.
|
||||
*
|
||||
* @param string $sessionId Session ID
|
||||
*
|
||||
* @return string The session data
|
||||
* @return string
|
||||
*/
|
||||
protected function doRead($sessionId)
|
||||
protected function doRead(string $sessionId)
|
||||
{
|
||||
if (self::LOCK_ADVISORY === $this->lockMode) {
|
||||
$this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
|
||||
@@ -618,12 +663,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 +687,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 +698,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 +712,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
||||
}
|
||||
|
||||
return '';
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -676,12 +726,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 +804,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 +825,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 +852,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 +884,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 +912,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,17 @@ 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
|
||||
*/
|
||||
@@ -45,25 +50,26 @@ class RedisSessionHandler extends AbstractSessionHandler
|
||||
!$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__, get_debug_type($redis)));
|
||||
}
|
||||
|
||||
if ($diff = array_diff(array_keys($options), ['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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doRead($sessionId): string
|
||||
protected function doRead(string $sessionId): string
|
||||
{
|
||||
return $this->redis->get($this->prefix.$sessionId) ?: '';
|
||||
}
|
||||
@@ -71,9 +77,9 @@ class RedisSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data): bool
|
||||
protected function doWrite(string $sessionId, string $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;
|
||||
}
|
||||
@@ -81,9 +87,21 @@ class RedisSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDestroy($sessionId): bool
|
||||
protected function doDestroy(string $sessionId): bool
|
||||
{
|
||||
$this->redis->del($this->prefix.$sessionId);
|
||||
static $unlink = true;
|
||||
|
||||
if ($unlink) {
|
||||
try {
|
||||
$unlink = false !== $this->redis->unlink($this->prefix.$sessionId);
|
||||
} catch (\Throwable $e) {
|
||||
$unlink = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$unlink) {
|
||||
$this->redis->del($this->prefix.$sessionId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -91,6 +109,7 @@ class RedisSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close(): bool
|
||||
{
|
||||
return true;
|
||||
@@ -98,17 +117,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')));
|
||||
}
|
||||
}
|
||||
|
||||
91
vendor/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php
vendored
Normal file
91
vendor/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
<?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__, get_debug_type($connection)));
|
||||
}
|
||||
|
||||
if ($options = \is_string($connection) ? parse_url($connection) : false) {
|
||||
parse_str($options['query'] ?? '', $options);
|
||||
}
|
||||
|
||||
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_debug_type($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, array_intersect_key($options ?: [], ['prefix' => 1, 'ttl' => 1]));
|
||||
|
||||
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, $options ?: []);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Unsupported Connection: "%s".', $connection));
|
||||
}
|
||||
}
|
||||
@@ -24,15 +24,16 @@ class StrictSessionHandler extends AbstractSessionHandler
|
||||
public function __construct(\SessionHandlerInterface $handler)
|
||||
{
|
||||
if ($handler instanceof \SessionUpdateTimestampHandlerInterface) {
|
||||
throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', \get_class($handler), self::class));
|
||||
throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', get_debug_type($handler), self::class));
|
||||
}
|
||||
|
||||
$this->handler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
parent::open($savePath, $sessionName);
|
||||
@@ -43,14 +44,15 @@ class StrictSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doRead($sessionId)
|
||||
protected function doRead(string $sessionId)
|
||||
{
|
||||
return $this->handler->read($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
return $this->write($sessionId, $data);
|
||||
@@ -59,14 +61,15 @@ class StrictSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
protected function doWrite(string $sessionId, string $data)
|
||||
{
|
||||
return $this->handler->write($sessionId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
$this->doDestroy = true;
|
||||
@@ -78,7 +81,7 @@ class StrictSessionHandler extends AbstractSessionHandler
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
protected function doDestroy(string $sessionId)
|
||||
{
|
||||
$this->doDestroy = false;
|
||||
|
||||
@@ -86,16 +89,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
|
||||
@@ -100,7 +100,7 @@ class MetadataBag implements SessionBagInterface
|
||||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
*/
|
||||
public function stampNew($lifetime = null)
|
||||
public function stampNew(int $lifetime = null)
|
||||
{
|
||||
$this->stampCreated($lifetime);
|
||||
}
|
||||
@@ -139,6 +139,7 @@ class MetadataBag implements SessionBagInterface
|
||||
public function clear()
|
||||
{
|
||||
// nothing to do
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,18 +152,16 @@ class MetadataBag implements SessionBagInterface
|
||||
|
||||
/**
|
||||
* Sets name.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(string $name)
|
||||
{
|
||||
$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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function regenerate($destroy = false, $lifetime = null)
|
||||
public function regenerate(bool $destroy = false, int $lifetime = null)
|
||||
{
|
||||
if (!$this->started) {
|
||||
$this->start();
|
||||
@@ -117,7 +117,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setId($id)
|
||||
public function setId(string $id)
|
||||
{
|
||||
if ($this->started) {
|
||||
throw new \LogicException('Cannot set session ID after the session has started.');
|
||||
@@ -137,7 +137,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -183,10 +183,10 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBag($name)
|
||||
public function getBag(string $name)
|
||||
{
|
||||
if (!isset($this->bags[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
|
||||
throw new \InvalidArgumentException(sprintf('The SessionBagInterface "%s" is not registered.', $name));
|
||||
}
|
||||
|
||||
if (!$this->started) {
|
||||
@@ -242,7 +242,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
||||
|
||||
foreach ($bags as $bag) {
|
||||
$key = $bag->getStorageKey();
|
||||
$this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : [];
|
||||
$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,7 @@ 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|null $savePath Path of directory to save session files
|
||||
*/
|
||||
public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
|
||||
{
|
||||
@@ -38,7 +37,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;
|
||||
@@ -69,7 +68,7 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function regenerate($destroy = false, $lifetime = null)
|
||||
public function regenerate(bool $destroy = false, int $lifetime = null)
|
||||
{
|
||||
if (!$this->started) {
|
||||
$this->start();
|
||||
@@ -88,7 +87,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;
|
||||
@@ -104,7 +103,10 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
||||
|
||||
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 +114,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 +123,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 +144,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)) : [];
|
||||
set_error_handler(static function () {});
|
||||
try {
|
||||
$data = file_get_contents($this->getFilePath());
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
$this->data = $data ? unserialize($data) : [];
|
||||
|
||||
$this->loadSession();
|
||||
}
|
||||
|
||||
42
vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorageFactory.php
vendored
Normal file
42
vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorageFactory.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(MockFileSessionStorage::class);
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class MockFileSessionStorageFactory implements SessionStorageFactoryInterface
|
||||
{
|
||||
private $savePath;
|
||||
private $name;
|
||||
private $metaBag;
|
||||
|
||||
/**
|
||||
* @see MockFileSessionStorage constructor.
|
||||
*/
|
||||
public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
|
||||
{
|
||||
$this->savePath = $savePath;
|
||||
$this->name = $name;
|
||||
$this->metaBag = $metaBag;
|
||||
}
|
||||
|
||||
public function createStorage(?Request $request): SessionStorageInterface
|
||||
{
|
||||
return new MockFileSessionStorage($this->savePath, $this->name, $this->metaBag);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
@@ -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,28 +86,23 @@ 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"
|
||||
* upload_progress.enabled, "1"
|
||||
* upload_progress.cleanup, "1"
|
||||
* upload_progress.prefix, "upload_progress_"
|
||||
* upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
|
||||
* upload_progress.freq, "1%"
|
||||
* upload_progress.min-freq, "1"
|
||||
* url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
|
||||
* sid_length, "32"
|
||||
* sid_bits_per_character, "5"
|
||||
* trans_sid_hosts, $_SERVER['HTTP_HOST']
|
||||
* trans_sid_tags, "a=href,area=href,frame=src,form="
|
||||
*
|
||||
* @param array $options Session configuration options
|
||||
* @param \SessionHandlerInterface|null $handler
|
||||
* @param MetadataBag $metaBag MetadataBag
|
||||
* @param AbstractProxy|\SessionHandlerInterface|null $handler
|
||||
*/
|
||||
public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null)
|
||||
{
|
||||
if (!\extension_loaded('session')) {
|
||||
throw new \LogicException('PHP extension "session" is required.');
|
||||
}
|
||||
|
||||
$options += [
|
||||
'cache_limiter' => '',
|
||||
'cache_expire' => 0,
|
||||
@@ -141,13 +141,13 @@ 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));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -173,7 +173,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setId($id)
|
||||
public function setId(string $id)
|
||||
{
|
||||
$this->saveHandler->setId($id);
|
||||
}
|
||||
@@ -189,7 +189,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->saveHandler->setName($name);
|
||||
}
|
||||
@@ -197,7 +197,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function regenerate($destroy = false, $lifetime = null)
|
||||
public function regenerate(bool $destroy = false, int $lifetime = null)
|
||||
{
|
||||
// Cannot regenerate the session ID for non-active sessions.
|
||||
if (\PHP_SESSION_ACTIVE !== session_status()) {
|
||||
@@ -208,8 +208,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,10 +220,6 @@ 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) {
|
||||
@@ -237,6 +235,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) {
|
||||
@@ -250,7 +249,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
|
||||
// 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 +261,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;
|
||||
@@ -301,10 +304,10 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBag($name)
|
||||
public function getBag(string $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()) {
|
||||
@@ -351,7 +354,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
@@ -373,12 +376,22 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
if (isset($validOptions[$key])) {
|
||||
if (str_starts_with($key, 'upload_progress.')) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.4', 'Support for the "%s" session option is deprecated. The settings prefixed with "session.upload_progress." can not be changed at runtime.', $key);
|
||||
continue;
|
||||
}
|
||||
if ('url_rewriter.tags' === $key) {
|
||||
trigger_deprecation('symfony/http-foundation', '5.4', 'Support for the "%s" session option is deprecated. Use "trans_sid_tags" instead.', $key);
|
||||
}
|
||||
if ('cookie_samesite' === $key && \PHP_VERSION_ID < 70300) {
|
||||
// PHP < 7.3 does not support same_site cookies. We will emulate it in
|
||||
// the start() method instead.
|
||||
$this->emulateSameSite = $value;
|
||||
continue;
|
||||
}
|
||||
if ('cookie_secure' === $key && 'auto' === $value) {
|
||||
continue;
|
||||
}
|
||||
ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
|
||||
}
|
||||
}
|
||||
@@ -394,15 +407,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
|
||||
*/
|
||||
@@ -449,7 +460,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
|
||||
foreach ($bags as $bag) {
|
||||
$key = $bag->getStorageKey();
|
||||
$session[$key] = isset($session[$key]) ? $session[$key] : [];
|
||||
$session[$key] = isset($session[$key]) && \is_array($session[$key]) ? $session[$key] : [];
|
||||
$bag->initialize($session[$key]);
|
||||
}
|
||||
|
||||
|
||||
49
vendor/symfony/http-foundation/Session/Storage/NativeSessionStorageFactory.php
vendored
Normal file
49
vendor/symfony/http-foundation/Session/Storage/NativeSessionStorageFactory.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(NativeSessionStorage::class);
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class NativeSessionStorageFactory implements SessionStorageFactoryInterface
|
||||
{
|
||||
private $options;
|
||||
private $handler;
|
||||
private $metaBag;
|
||||
private $secure;
|
||||
|
||||
/**
|
||||
* @see NativeSessionStorage constructor.
|
||||
*/
|
||||
public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null, bool $secure = false)
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->handler = $handler;
|
||||
$this->metaBag = $metaBag;
|
||||
$this->secure = $secure;
|
||||
}
|
||||
|
||||
public function createStorage(?Request $request): SessionStorageInterface
|
||||
{
|
||||
$storage = new NativeSessionStorage($this->options, $this->handler, $this->metaBag);
|
||||
if ($this->secure && $request && $request->isSecure()) {
|
||||
$storage->setOptions(['cookie_secure' => true]);
|
||||
}
|
||||
|
||||
return $storage;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
47
vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorageFactory.php
vendored
Normal file
47
vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorageFactory.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(PhpBridgeSessionStorage::class);
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class PhpBridgeSessionStorageFactory implements SessionStorageFactoryInterface
|
||||
{
|
||||
private $handler;
|
||||
private $metaBag;
|
||||
private $secure;
|
||||
|
||||
/**
|
||||
* @see PhpBridgeSessionStorage constructor.
|
||||
*/
|
||||
public function __construct($handler = null, MetadataBag $metaBag = null, bool $secure = false)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
$this->metaBag = $metaBag;
|
||||
$this->secure = $secure;
|
||||
}
|
||||
|
||||
public function createStorage(?Request $request): SessionStorageInterface
|
||||
{
|
||||
$storage = new PhpBridgeSessionStorage($this->handler, $this->metaBag);
|
||||
if ($this->secure && $request && $request->isSecure()) {
|
||||
$storage->setOptions(['cookie_secure' => true]);
|
||||
}
|
||||
|
||||
return $storage;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ abstract class AbstractProxy
|
||||
/**
|
||||
* Gets the session.save_handler name.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSaveHandlerName()
|
||||
{
|
||||
@@ -81,14 +81,12 @@ abstract class AbstractProxy
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function setId($id)
|
||||
public function setId(string $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);
|
||||
@@ -107,14 +105,12 @@ abstract class AbstractProxy
|
||||
/**
|
||||
* Sets the session name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setName(string $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);
|
||||
|
||||
@@ -21,7 +21,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
||||
public function __construct(\SessionHandlerInterface $handler)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
$this->wrapper = ($handler instanceof \SessionHandler);
|
||||
$this->wrapper = $handler instanceof \SessionHandler;
|
||||
$this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
|
||||
}
|
||||
|
||||
@@ -36,64 +36,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);
|
||||
|
||||
38
vendor/symfony/http-foundation/Session/Storage/ServiceSessionFactory.php
vendored
Normal file
38
vendor/symfony/http-foundation/Session/Storage/ServiceSessionFactory.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @internal to be removed in Symfony 6
|
||||
*/
|
||||
final class ServiceSessionFactory implements SessionStorageFactoryInterface
|
||||
{
|
||||
private $storage;
|
||||
|
||||
public function __construct(SessionStorageInterface $storage)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
}
|
||||
|
||||
public function createStorage(?Request $request): SessionStorageInterface
|
||||
{
|
||||
if ($this->storage instanceof NativeSessionStorage && $request && $request->isSecure()) {
|
||||
$this->storage->setOptions(['cookie_secure' => true]);
|
||||
}
|
||||
|
||||
return $this->storage;
|
||||
}
|
||||
}
|
||||
25
vendor/symfony/http-foundation/Session/Storage/SessionStorageFactoryInterface.php
vendored
Normal file
25
vendor/symfony/http-foundation/Session/Storage/SessionStorageFactoryInterface.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
interface SessionStorageFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of SessionStorageInterface.
|
||||
*/
|
||||
public function createStorage(?Request $request): SessionStorageInterface;
|
||||
}
|
||||
@@ -24,7 +24,7 @@ interface SessionStorageInterface
|
||||
/**
|
||||
* Starts the session.
|
||||
*
|
||||
* @return bool True if started
|
||||
* @return bool
|
||||
*
|
||||
* @throws \RuntimeException if something goes wrong starting the session
|
||||
*/
|
||||
@@ -33,37 +33,33 @@ interface SessionStorageInterface
|
||||
/**
|
||||
* Checks if the session is started.
|
||||
*
|
||||
* @return bool True if started, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function isStarted();
|
||||
|
||||
/**
|
||||
* Returns the session ID.
|
||||
*
|
||||
* @return string The session ID or empty
|
||||
* @return string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function setId($id);
|
||||
public function setId(string $id);
|
||||
|
||||
/**
|
||||
* Returns the session name.
|
||||
*
|
||||
* @return mixed The session name
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Sets the session name.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name);
|
||||
public function setName(string $name);
|
||||
|
||||
/**
|
||||
* Regenerates id that represents this storage.
|
||||
@@ -77,7 +73,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.
|
||||
@@ -90,11 +86,11 @@ interface SessionStorageInterface
|
||||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
*
|
||||
* @return bool True if session regenerated, false if error
|
||||
* @return bool
|
||||
*
|
||||
* @throws \RuntimeException If an error occurs while regenerating this storage
|
||||
*/
|
||||
public function regenerate($destroy = false, $lifetime = null);
|
||||
public function regenerate(bool $destroy = false, int $lifetime = null);
|
||||
|
||||
/**
|
||||
* Force the session to be saved and closed.
|
||||
@@ -117,13 +113,11 @@ interface SessionStorageInterface
|
||||
/**
|
||||
* Gets a SessionBagInterface by name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return SessionBagInterface
|
||||
*
|
||||
* @throws \InvalidArgumentException If the bag does not exist
|
||||
*/
|
||||
public function getBag($name);
|
||||
public function getBag(string $name);
|
||||
|
||||
/**
|
||||
* Registers a SessionBagInterface for use.
|
||||
|
||||
@@ -30,11 +30,6 @@ class StreamedResponse extends Response
|
||||
protected $streamed;
|
||||
private $headersSent;
|
||||
|
||||
/**
|
||||
* @param callable|null $callback A valid PHP callback or null to set it later
|
||||
* @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 = [])
|
||||
{
|
||||
parent::__construct(null, $status, $headers);
|
||||
@@ -50,21 +45,21 @@ class StreamedResponse extends Response
|
||||
* Factory method for chainability.
|
||||
*
|
||||
* @param callable|null $callback A valid PHP callback or null to set it later
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @deprecated since Symfony 5.1, use __construct() instead.
|
||||
*/
|
||||
public static function create($callback = null, $status = 200, $headers = [])
|
||||
public static function create($callback = null, int $status = 200, array $headers = [])
|
||||
{
|
||||
trigger_deprecation('symfony/http-foundation', '5.1', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, static::class);
|
||||
|
||||
return new static($callback, $status, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the PHP callback associated with this Response.
|
||||
*
|
||||
* @param callable $callback A valid PHP callback
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCallback(callable $callback)
|
||||
@@ -123,7 +118,7 @@ class StreamedResponse extends Response
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContent($content)
|
||||
public function setContent(?string $content)
|
||||
{
|
||||
if (null !== $content) {
|
||||
throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
|
||||
@@ -136,8 +131,6 @@ class StreamedResponse extends Response
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\HttpFoundation\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class RequestAttributeValueSame extends Constraint
|
||||
{
|
||||
|
||||
71
vendor/symfony/http-foundation/Test/Constraint/ResponseFormatSame.php
vendored
Normal file
71
vendor/symfony/http-foundation/Test/Constraint/ResponseFormatSame.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Asserts that the response is in the given format.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
final class ResponseFormatSame extends Constraint
|
||||
{
|
||||
private $request;
|
||||
private $format;
|
||||
|
||||
public function __construct(Request $request, ?string $format)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->format = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return 'format is '.($this->format ?? 'null');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
return $this->format === $this->request->getFormat($response->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ final class ResponseHasCookie extends Constraint
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
|
||||
protected function getCookie(Response $response): ?Cookie
|
||||
private function getCookie(Response $response): ?Cookie
|
||||
{
|
||||
$cookies = $response->headers->getCookies();
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ final class ResponseHeaderSame extends Constraint
|
||||
*/
|
||||
protected function matches($response): bool
|
||||
{
|
||||
return $this->expectedValue === $response->headers->get($this->headerName, null, true);
|
||||
return $this->expectedValue === $response->headers->get($this->headerName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
56
vendor/symfony/http-foundation/Test/Constraint/ResponseIsUnprocessable.php
vendored
Normal file
56
vendor/symfony/http-foundation/Test/Constraint/ResponseIsUnprocessable.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 ResponseIsUnprocessable extends Constraint
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return 'is unprocessable';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $other
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function matches($other): bool
|
||||
{
|
||||
return Response::HTTP_UNPROCESSABLE_ENTITY === $other->getStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $other
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function failureDescription($other): string
|
||||
{
|
||||
return 'the Response '.$this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $other
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function additionalFailureDescription($other): string
|
||||
{
|
||||
return (string) $other;
|
||||
}
|
||||
}
|
||||
@@ -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 [
|
||||
[
|
||||
'text/html',
|
||||
'text/html', [],
|
||||
],
|
||||
[
|
||||
'"this;should,not=matter"',
|
||||
'this;should,not=matter', [],
|
||||
],
|
||||
[
|
||||
"text/plain; charset=utf-8;param=\"this;should,not=matter\";\tfootnotes=true",
|
||||
'text/plain', ['charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'],
|
||||
],
|
||||
[
|
||||
'"this;should,not=matter";charset=utf-8',
|
||||
'this;should,not=matter', ['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 [
|
||||
[
|
||||
'text/html', [],
|
||||
'text/html',
|
||||
],
|
||||
[
|
||||
'text/plain', ['charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'],
|
||||
'text/plain; charset=utf-8; param="this;should,not=matter"; footnotes=true',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testValue()
|
||||
{
|
||||
$item = new AcceptHeaderItem('value', []);
|
||||
$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', []);
|
||||
$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', []);
|
||||
$this->assertEquals([], $item->getAttributes());
|
||||
$this->assertFalse($item->hasAttribute('test'));
|
||||
$this->assertNull($item->getAttribute('test'));
|
||||
$this->assertEquals('default', $item->getAttribute('test', 'default'));
|
||||
|
||||
$item->setAttribute('test', 'value');
|
||||
$this->assertEquals(['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 [
|
||||
['', []],
|
||||
['gzip', [new AcceptHeaderItem('gzip')]],
|
||||
['gzip,deflate,sdch', [new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')]],
|
||||
["gzip, deflate\t,sdch", [new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')]],
|
||||
['"this;should,not=matter"', [new AcceptHeaderItem('this;should,not=matter')]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideToStringData
|
||||
*/
|
||||
public function testToString(array $items, $string)
|
||||
{
|
||||
$header = new AcceptHeader($items);
|
||||
$this->assertEquals($string, (string) $header);
|
||||
}
|
||||
|
||||
public function provideToStringData()
|
||||
{
|
||||
return [
|
||||
[[], ''],
|
||||
[[new AcceptHeaderItem('gzip')], 'gzip'],
|
||||
[[new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')], 'gzip,deflate,sdch'],
|
||||
[[new AcceptHeaderItem('this;should,not=matter')], 'this;should,not=matter'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 [
|
||||
['fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', ['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 [
|
||||
'quality has priority' => ['*;q=0.3,ISO-8859-1,utf-8;q=0.7', ['ISO-8859-1', 'utf-8', '*']],
|
||||
'order matters when q is equal' => ['*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', ['ISO-8859-1', 'utf-8', '*']],
|
||||
'order matters when q is equal2' => ['*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', ['utf-8', 'ISO-8859-1', '*']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDefaultValueData
|
||||
*/
|
||||
public function testDefaultValue($acceptHeader, $value, $expectedQuality)
|
||||
{
|
||||
$header = AcceptHeader::fromString($acceptHeader);
|
||||
$this->assertSame($expectedQuality, $header->get($value)->getQuality());
|
||||
}
|
||||
|
||||
public function provideDefaultValueData()
|
||||
{
|
||||
yield ['text/plain;q=0.5, text/html, text/x-dvi;q=0.8, *;q=0.3', 'text/xml', 0.3];
|
||||
yield ['text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/xml', 0.3];
|
||||
yield ['text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/html', 1.0];
|
||||
yield ['text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/plain', 0.5];
|
||||
yield ['text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', '*', 0.3];
|
||||
yield ['text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', '*', 1.0];
|
||||
yield ['text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', 'text/xml', 1.0];
|
||||
yield ['text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', 'text/*', 1.0];
|
||||
yield ['text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/*', 0.8];
|
||||
yield ['text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/html', 1.0];
|
||||
yield ['text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/x-dvi', 0.8];
|
||||
yield ['*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', '*', 0.3];
|
||||
yield ['*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'utf-8', 0.7];
|
||||
yield ['*;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 [
|
||||
[
|
||||
[
|
||||
'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',
|
||||
],
|
||||
[
|
||||
[
|
||||
'REQUEST_URI' => '/foo/bar',
|
||||
'SCRIPT_NAME' => '/foo/app_dev.php',
|
||||
],
|
||||
'/foo/bar',
|
||||
'/foo',
|
||||
'/bar',
|
||||
],
|
||||
[
|
||||
[
|
||||
'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',
|
||||
],
|
||||
[
|
||||
[
|
||||
'REQUEST_URI' => '/foo/bar',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
],
|
||||
'/foo/bar',
|
||||
'',
|
||||
'/foo/bar',
|
||||
],
|
||||
[
|
||||
[
|
||||
'REQUEST_URI' => '/app_dev.php',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
],
|
||||
'/app_dev.php',
|
||||
'/app_dev.php',
|
||||
'/',
|
||||
],
|
||||
[
|
||||
[
|
||||
'REQUEST_URI' => '/',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
],
|
||||
'/',
|
||||
'',
|
||||
'/',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,367 +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, ['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, [], 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, [], 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, ['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, ['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 [
|
||||
['bytes=1-4', 1, 4, 'bytes 1-4/35'],
|
||||
['bytes=-5', 30, 5, 'bytes 30-34/35'],
|
||||
['bytes=30-', 30, 5, 'bytes 30-34/35'],
|
||||
['bytes=30-30', 30, 1, 'bytes 30-30/35'],
|
||||
['bytes=30-34', 30, 5, 'bytes 30-34/35'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testRangeRequestsWithoutLastModifiedDate()
|
||||
{
|
||||
// prevent auto last modified
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, ['Content-Type' => 'application/octet-stream'], true, null, false, false);
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$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, ['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 [
|
||||
['bytes=0-'],
|
||||
['bytes=0-34'],
|
||||
['bytes=-35'],
|
||||
// Syntactical invalid range-request should also return the full resource
|
||||
['bytes=20-10'],
|
||||
['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, ['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 [
|
||||
['bytes=-40'],
|
||||
['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, ['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 [
|
||||
[__DIR__.'/../README.md'],
|
||||
['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, ['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, ['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, ['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, ['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 [
|
||||
['/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'],
|
||||
['/home/Foo/bar.txt', '/var/www/=/files/,/home/Foo/=/baz/', '/baz/bar.txt'],
|
||||
['/home/Foo/bar.txt', '"/var/www/"="/files/", "/home/Foo/"="/baz/"', '/baz/bar.txt'],
|
||||
['/tmp/bar.txt', '"/var/www/"="/files/", "/home/Foo/"="/baz/"', null],
|
||||
];
|
||||
}
|
||||
|
||||
public function testStream()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, ['Content-Type' => 'text/plain']);
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertNull($response->headers->get('Content-Length'));
|
||||
}
|
||||
|
||||
protected function provideResponse()
|
||||
{
|
||||
return new BinaryFileResponse(__DIR__.'/../README.md', 200, ['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 [
|
||||
[''],
|
||||
[',MyName'],
|
||||
[';MyName'],
|
||||
[' MyName'],
|
||||
["\tMyName"],
|
||||
["\rMyName"],
|
||||
["\nMyName"],
|
||||
["\013MyName"],
|
||||
["\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 [
|
||||
['request.getMethod() == method', true],
|
||||
['request.getPathInfo() == path', true],
|
||||
['request.getHost() == host', true],
|
||||
['request.getClientIp() == ip', true],
|
||||
['request.attributes.all() == attributes', true],
|
||||
['request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', true],
|
||||
['request.getMethod() != method', false],
|
||||
['request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', false],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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,143 +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;
|
||||
|
||||
/**
|
||||
* @requires extension fileinfo
|
||||
*/
|
||||
class FileTest extends TestCase
|
||||
{
|
||||
protected $file;
|
||||
|
||||
public function testGetMimeTypeUsesMimeTypeGuessers()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/test.gif');
|
||||
$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');
|
||||
$this->assertEquals('gif', $file->guessExtension());
|
||||
}
|
||||
|
||||
public function testConstructWhenFileNotExists()
|
||||
{
|
||||
$this->expectException('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 [
|
||||
['original.gif', 'original.gif'],
|
||||
['..\\..\\original.gif', 'original.gif'],
|
||||
['../../original.gif', 'original.gif'],
|
||||
['файлfile.gif', 'файлfile.gif'],
|
||||
['..\\..\\файлfile.gif', 'файлfile.gif'],
|
||||
['../../файл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);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
f
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 35 B |
Binary file not shown.
|
Before Width: | Height: | Size: 35 B |
@@ -1,89 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\File\MimeType;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
|
||||
/**
|
||||
* @requires extension fileinfo
|
||||
* @group legacy
|
||||
*/
|
||||
class MimeTypeTest extends TestCase
|
||||
{
|
||||
public function testGuessImageWithoutExtension()
|
||||
{
|
||||
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
|
||||
}
|
||||
|
||||
public function testGuessImageWithDirectory()
|
||||
{
|
||||
$this->expectException('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->expectException('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->expectException('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,358 +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->expectException('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 testCaseSensitiveMimeType()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/case-sensitive-mime-type.xlsm',
|
||||
'test.xlsm',
|
||||
'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('xlsm', $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 ([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 [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 [
|
||||
[UPLOAD_ERR_INI_SIZE],
|
||||
[UPLOAD_ERR_FORM_SIZE],
|
||||
[UPLOAD_ERR_PARTIAL],
|
||||
[UPLOAD_ERR_NO_TMP_DIR],
|
||||
[UPLOAD_ERR_EXTENSION],
|
||||
];
|
||||
}
|
||||
|
||||
public function testIsInvalidIfNotHttpUpload()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
UPLOAD_ERR_OK
|
||||
);
|
||||
|
||||
$this->assertFalse($file->isValid());
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user