updated backend
This commit is contained in:
parent
1fea0f4fe7
commit
11521c2339
3459 changed files with 72735 additions and 168535 deletions
|
|
@ -1,3 +0,0 @@
|
|||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
|
|
@ -24,7 +24,7 @@ class AcceptHeader
|
|||
/**
|
||||
* @var AcceptHeaderItem[]
|
||||
*/
|
||||
private $items = array();
|
||||
private $items = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
|
@ -52,12 +52,17 @@ class AcceptHeader
|
|||
{
|
||||
$index = 0;
|
||||
|
||||
return new self(array_map(function ($itemValue) use (&$index) {
|
||||
$item = AcceptHeaderItem::fromString($itemValue);
|
||||
$parts = HeaderUtils::split((string) $headerValue, ',;=');
|
||||
|
||||
return new self(array_map(function ($subParts) use (&$index) {
|
||||
$part = array_shift($subParts);
|
||||
$attributes = HeaderUtils::combine($subParts);
|
||||
|
||||
$item = new AcceptHeaderItem($part[0], $attributes);
|
||||
$item->setIndex($index++);
|
||||
|
||||
return $item;
|
||||
}, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
|
||||
}, $parts));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,7 +96,7 @@ class AcceptHeader
|
|||
*/
|
||||
public function get($value)
|
||||
{
|
||||
return isset($this->items[$value]) ? $this->items[$value] : null;
|
||||
return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -148,7 +153,7 @@ class AcceptHeader
|
|||
/**
|
||||
* Sorts items by descending quality.
|
||||
*/
|
||||
private function sort()
|
||||
private function sort(): void
|
||||
{
|
||||
if (!$this->sorted) {
|
||||
uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) {
|
||||
|
|
|
|||
|
|
@ -21,13 +21,9 @@ class AcceptHeaderItem
|
|||
private $value;
|
||||
private $quality = 1.0;
|
||||
private $index = 0;
|
||||
private $attributes = array();
|
||||
private $attributes = [];
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($value, array $attributes = array())
|
||||
public function __construct(string $value, array $attributes = [])
|
||||
{
|
||||
$this->value = $value;
|
||||
foreach ($attributes as $name => $value) {
|
||||
|
|
@ -44,28 +40,16 @@ class AcceptHeaderItem
|
|||
*/
|
||||
public static function fromString($itemValue)
|
||||
{
|
||||
$bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
$value = array_shift($bits);
|
||||
$attributes = array();
|
||||
$parts = HeaderUtils::split($itemValue, ';=');
|
||||
|
||||
$lastNullAttribute = null;
|
||||
foreach ($bits as $bit) {
|
||||
if (($start = substr($bit, 0, 1)) === ($end = substr($bit, -1)) && ('"' === $start || '\'' === $start)) {
|
||||
$attributes[$lastNullAttribute] = substr($bit, 1, -1);
|
||||
} elseif ('=' === $end) {
|
||||
$lastNullAttribute = $bit = substr($bit, 0, -1);
|
||||
$attributes[$bit] = null;
|
||||
} else {
|
||||
$parts = explode('=', $bit);
|
||||
$attributes[$parts[0]] = isset($parts[1]) && \strlen($parts[1]) > 0 ? $parts[1] : '';
|
||||
}
|
||||
}
|
||||
$part = array_shift($parts);
|
||||
$attributes = HeaderUtils::combine($parts);
|
||||
|
||||
return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ('"' === $start || '\'' === $start) ? substr($value, 1, -1) : $value, $attributes);
|
||||
return new self($part[0], $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns header value's string representation.
|
||||
* Returns header value's string representation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
|
@ -73,9 +57,7 @@ class AcceptHeaderItem
|
|||
{
|
||||
$string = $this->value.($this->quality < 1 ? ';q='.$this->quality : '');
|
||||
if (\count($this->attributes) > 0) {
|
||||
$string .= ';'.implode(';', array_map(function ($name, $value) {
|
||||
return sprintf(preg_match('/[,;=]/', $value) ? '%s="%s"' : '%s=%s', $name, $value);
|
||||
}, array_keys($this->attributes), $this->attributes));
|
||||
$string .= '; '.HeaderUtils::toString($this->attributes, ';');
|
||||
}
|
||||
|
||||
return $string;
|
||||
|
|
|
|||
|
|
@ -11,9 +11,13 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ApacheRequest::class, Request::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Request represents an HTTP request from an Apache server.
|
||||
*
|
||||
* @deprecated since Symfony 4.4. Use the Request class instead.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ApacheRequest extends Request
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ class BinaryFileResponse extends Response
|
|||
* @var File
|
||||
*/
|
||||
protected $file;
|
||||
protected $offset;
|
||||
protected $maxlen;
|
||||
protected $offset = 0;
|
||||
protected $maxlen = -1;
|
||||
protected $deleteFileAfterSend = false;
|
||||
|
||||
/**
|
||||
|
|
@ -40,11 +40,11 @@ class BinaryFileResponse extends Response
|
|||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
* @param bool $public Files are public by default
|
||||
* @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
|
||||
* @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename
|
||||
* @param bool $autoEtag Whether the ETag header should be automatically set
|
||||
* @param bool $autoLastModified Whether the Last-Modified header should be automatically set
|
||||
*/
|
||||
public function __construct($file, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
|
||||
public function __construct($file, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
|
||||
{
|
||||
parent::__construct(null, $status, $headers);
|
||||
|
||||
|
|
@ -60,13 +60,13 @@ class BinaryFileResponse extends Response
|
|||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
* @param bool $public Files are public by default
|
||||
* @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename
|
||||
* @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename
|
||||
* @param bool $autoEtag Whether the ETag header should be automatically set
|
||||
* @param bool $autoLastModified Whether the Last-Modified header should be automatically set
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
|
||||
public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
|
||||
{
|
||||
return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
|
||||
}
|
||||
|
|
@ -204,7 +204,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')) {
|
||||
|
|
@ -218,28 +218,28 @@ 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
|
||||
foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
|
||||
$mapping = explode('=', $mapping, 2);
|
||||
|
||||
if (2 === \count($mapping)) {
|
||||
$pathPrefix = trim($mapping[0]);
|
||||
$location = trim($mapping[1]);
|
||||
|
||||
if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
|
||||
$path = $location.substr($path, \strlen($pathPrefix));
|
||||
break;
|
||||
}
|
||||
$parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',=');
|
||||
foreach ($parts as $part) {
|
||||
list($pathPrefix, $location) = $part;
|
||||
if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
|
||||
$path = $location.substr($path, \strlen($pathPrefix));
|
||||
// Only set X-Accel-Redirect header if a valid URI can be produced
|
||||
// as nginx does not serve arbitrary file paths.
|
||||
$this->headers->set($type, $path);
|
||||
$this->maxlen = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->headers->set($type, $path);
|
||||
$this->maxlen = 0;
|
||||
}
|
||||
$this->headers->set($type, $path);
|
||||
$this->maxlen = 0;
|
||||
} elseif ($request->headers->has('Range')) {
|
||||
// Process the range headers.
|
||||
if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
|
||||
$range = $request->headers->get('Range');
|
||||
|
||||
list($start, $end) = explode('-', substr($range, 6), 2) + array(0);
|
||||
list($start, $end) = explode('-', substr($range, 6), 2) + [0];
|
||||
|
||||
$end = ('' === $end) ? $fileSize - 1 : (int) $end;
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ class BinaryFileResponse extends Response
|
|||
return $this;
|
||||
}
|
||||
|
||||
private function hasValidIfRangeHeader($header)
|
||||
private function hasValidIfRangeHeader(?string $header): bool
|
||||
{
|
||||
if ($this->getEtag() === $header) {
|
||||
return true;
|
||||
|
|
@ -305,7 +305,7 @@ class BinaryFileResponse extends Response
|
|||
fclose($out);
|
||||
fclose($file);
|
||||
|
||||
if ($this->deleteFileAfterSend) {
|
||||
if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) {
|
||||
unlink($this->file->getPathname());
|
||||
}
|
||||
|
||||
|
|
@ -322,12 +322,12 @@ class BinaryFileResponse extends Response
|
|||
if (null !== $content) {
|
||||
throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
|
|
@ -343,14 +343,14 @@ 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)
|
||||
public function deleteFileAfterSend($shouldDelete = true)
|
||||
{
|
||||
$this->deleteFileAfterSend = $shouldDelete;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,84 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
3.4.14
|
||||
------
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* passing arguments to `Request::isMethodSafe()` is deprecated.
|
||||
* `ApacheRequest` is deprecated, use the `Request` class instead.
|
||||
* passing a third argument to `HeaderBag::get()` is deprecated, use method `all()` instead
|
||||
* [BC BREAK] `PdoSessionHandler` with MySQL changed the type of the lifetime column,
|
||||
make sure to run `ALTER TABLE sessions MODIFY sess_lifetime INTEGER UNSIGNED NOT NULL` to
|
||||
update your database.
|
||||
* `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column,
|
||||
make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database
|
||||
to speed up garbage collection of expired sessions.
|
||||
* added `SessionHandlerFactory` to create session handlers with a DSN
|
||||
* added `IpUtils::anonymize()` to help with GDPR compliance.
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* added PHPUnit constraints: `RequestAttributeValueSame`, `ResponseCookieValueSame`, `ResponseHasCookie`,
|
||||
`ResponseHasHeader`, `ResponseHeaderSame`, `ResponseIsRedirected`, `ResponseIsSuccessful`, and `ResponseStatusCodeSame`
|
||||
* deprecated `MimeTypeGuesserInterface` and `ExtensionGuesserInterface` in favor of `Symfony\Component\Mime\MimeTypesInterface`.
|
||||
* deprecated `MimeType` and `MimeTypeExtensionGuesser` in favor of `Symfony\Component\Mime\MimeTypes`.
|
||||
* deprecated `FileBinaryMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileBinaryMimeTypeGuesser`.
|
||||
* deprecated `FileinfoMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileinfoMimeTypeGuesser`.
|
||||
* added `UrlHelper` that allows to get an absolute URL and a relative path for a given path
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* the default value of the "$secure" and "$samesite" arguments of Cookie's constructor
|
||||
will respectively change from "false" to "null" and from "null" to "lax" in Symfony
|
||||
5.0, you should define their values explicitly or use "Cookie::create()" instead.
|
||||
* added `matchPort()` in RequestMatcher
|
||||
|
||||
4.1.3
|
||||
-----
|
||||
|
||||
* [BC BREAK] Support for the IIS-only `X_ORIGINAL_URL` and `X_REWRITE_URL`
|
||||
HTTP headers has been dropped for security reasons.
|
||||
|
||||
4.1.0
|
||||
-----
|
||||
|
||||
* Query string normalization uses `parse_str()` instead of custom parsing logic.
|
||||
* Passing the file size to the constructor of the `UploadedFile` class is deprecated.
|
||||
* The `getClientSize()` method of the `UploadedFile` class is deprecated. Use `getSize()` instead.
|
||||
* added `RedisSessionHandler` to use Redis as a session storage
|
||||
* The `get()` method of the `AcceptHeader` class now takes into account the
|
||||
`*` and `*/*` default values (if they are present in the Accept HTTP header)
|
||||
when looking for items.
|
||||
* deprecated `Request::getSession()` when no session has been set. Use `Request::hasSession()` instead.
|
||||
* added `CannotWriteFileException`, `ExtensionFileException`, `FormSizeFileException`,
|
||||
`IniSizeFileException`, `NoFileException`, `NoTmpDirFileException`, `PartialFileException` to
|
||||
handle failed `UploadedFile`.
|
||||
* added `MigratingSessionHandler` for migrating between two session handlers without losing sessions
|
||||
* added `HeaderUtils`.
|
||||
|
||||
4.0.0
|
||||
-----
|
||||
|
||||
* the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()`
|
||||
methods have been removed
|
||||
* the `Request::HEADER_CLIENT_IP` constant has been removed, use
|
||||
`Request::HEADER_X_FORWARDED_FOR` instead
|
||||
* the `Request::HEADER_CLIENT_HOST` constant has been removed, use
|
||||
`Request::HEADER_X_FORWARDED_HOST` instead
|
||||
* the `Request::HEADER_CLIENT_PROTO` constant has been removed, use
|
||||
`Request::HEADER_X_FORWARDED_PROTO` instead
|
||||
* the `Request::HEADER_CLIENT_PORT` constant has been removed, use
|
||||
`Request::HEADER_X_FORWARDED_PORT` instead
|
||||
* checking for cacheable HTTP methods using the `Request::isMethodSafe()`
|
||||
method (by not passing `false` as its argument) is not supported anymore and
|
||||
throws a `\BadMethodCallException`
|
||||
* the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes have been removed
|
||||
* setting session save handlers that do not implement `\SessionHandlerInterface` in
|
||||
`NativeSessionStorage::setSaveHandler()` is not supported anymore and throws a
|
||||
`\TypeError`
|
||||
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
|
|
@ -21,7 +93,7 @@ CHANGELOG
|
|||
-----
|
||||
|
||||
* the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument,
|
||||
see http://symfony.com/doc/current/components/http_foundation/trusting_proxies.html for more info,
|
||||
see https://symfony.com/doc/current/deployment/proxies.html for more info,
|
||||
* deprecated the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` methods,
|
||||
* added `File\Stream`, to be passed to `BinaryFileResponse` when the size of the served file is unknown,
|
||||
disabling `Range` and `Content-Length` handling, switching to chunked encoding instead
|
||||
|
|
@ -144,10 +216,10 @@ CHANGELOG
|
|||
* Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This
|
||||
implementation is ESI compatible.
|
||||
* Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire
|
||||
behaviour of messages auto expiring after one page page load. Messages must
|
||||
behavior of messages auto expiring after one page page load. Messages must
|
||||
be retrieved by `get()` or `all()`.
|
||||
* Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate
|
||||
attributes storage behaviour from 2.0.x (default).
|
||||
attributes storage behavior from 2.0.x (default).
|
||||
* Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for
|
||||
namespace session attributes.
|
||||
* Flash API can stores messages in an array so there may be multiple messages
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ namespace Symfony\Component\HttpFoundation;
|
|||
*/
|
||||
class Cookie
|
||||
{
|
||||
const SAMESITE_NONE = 'none';
|
||||
const SAMESITE_LAX = 'lax';
|
||||
const SAMESITE_STRICT = 'strict';
|
||||
|
||||
protected $name;
|
||||
protected $value;
|
||||
protected $domain;
|
||||
|
|
@ -25,11 +29,14 @@ class Cookie
|
|||
protected $path;
|
||||
protected $secure;
|
||||
protected $httpOnly;
|
||||
|
||||
private $raw;
|
||||
private $sameSite;
|
||||
private $secureDefault = false;
|
||||
|
||||
const SAMESITE_LAX = 'lax';
|
||||
const SAMESITE_STRICT = 'strict';
|
||||
private static $reservedCharsList = "=,; \t\r\n\v\f";
|
||||
private static $reservedCharsFrom = ['=', ',', ';', ' ', "\t", "\r", "\n", "\v", "\f"];
|
||||
private static $reservedCharsTo = ['%3D', '%2C', '%3B', '%20', '%09', '%0D', '%0A', '%0B', '%0C'];
|
||||
|
||||
/**
|
||||
* Creates cookie from raw header string.
|
||||
|
|
@ -41,7 +48,7 @@ class Cookie
|
|||
*/
|
||||
public static function fromString($cookie, $decode = false)
|
||||
{
|
||||
$data = array(
|
||||
$data = [
|
||||
'expires' => 0,
|
||||
'path' => '/',
|
||||
'domain' => null,
|
||||
|
|
@ -49,35 +56,26 @@ class Cookie
|
|||
'httponly' => false,
|
||||
'raw' => !$decode,
|
||||
'samesite' => null,
|
||||
);
|
||||
foreach (explode(';', $cookie) as $part) {
|
||||
if (false === strpos($part, '=')) {
|
||||
$key = trim($part);
|
||||
$value = true;
|
||||
} else {
|
||||
list($key, $value) = explode('=', trim($part), 2);
|
||||
$key = trim($key);
|
||||
$value = trim($value);
|
||||
}
|
||||
if (!isset($data['name'])) {
|
||||
$data['name'] = $decode ? urldecode($key) : $key;
|
||||
$data['value'] = true === $value ? null : ($decode ? urldecode($value) : $value);
|
||||
continue;
|
||||
}
|
||||
switch ($key = strtolower($key)) {
|
||||
case 'name':
|
||||
case 'value':
|
||||
break;
|
||||
case 'max-age':
|
||||
$data['expires'] = time() + (int) $value;
|
||||
break;
|
||||
default:
|
||||
$data[$key] = $value;
|
||||
break;
|
||||
}
|
||||
];
|
||||
|
||||
$parts = HeaderUtils::split($cookie, ';=');
|
||||
$part = array_shift($parts);
|
||||
|
||||
$name = $decode ? urldecode($part[0]) : $part[0];
|
||||
$value = isset($part[1]) ? ($decode ? urldecode($part[1]) : $part[1]) : null;
|
||||
|
||||
$data = HeaderUtils::combine($parts) + $data;
|
||||
|
||||
if (isset($data['max-age'])) {
|
||||
$data['expires'] = time() + (int) $data['max-age'];
|
||||
}
|
||||
|
||||
return new static($data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
|
||||
return new static($name, $value, $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
|
||||
}
|
||||
|
||||
public static function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = self::SAMESITE_LAX): self
|
||||
{
|
||||
return new self($name, $value, $expire, $path, $domain, $secure, $httpOnly, $raw, $sameSite);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,17 +84,21 @@ class Cookie
|
|||
* @param int|string|\DateTimeInterface $expire The time the cookie expires
|
||||
* @param string $path The path on the server in which the cookie will be available on
|
||||
* @param string|null $domain The domain that the cookie is available to
|
||||
* @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client
|
||||
* @param bool|null $secure Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
|
||||
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
|
||||
* @param bool $raw Whether the cookie value should be sent with no url encoding
|
||||
* @param string|null $sameSite Whether the cookie will be available for cross-site requests
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null)
|
||||
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null)
|
||||
{
|
||||
if (9 > \func_num_args()) {
|
||||
@trigger_error(sprintf('The default value of the "$secure" and "$samesite" arguments of "%s"\'s constructor will respectively change from "false" to "null" and from "null" to "lax" in Symfony 5.0, you should define their values explicitly or use "Cookie::create()" instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
// from PHP source code
|
||||
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
|
||||
if ($raw && false !== strpbrk($name, self::$reservedCharsList)) {
|
||||
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
|
||||
}
|
||||
|
||||
|
|
@ -120,15 +122,17 @@ class Cookie
|
|||
$this->domain = $domain;
|
||||
$this->expire = 0 < $expire ? (int) $expire : 0;
|
||||
$this->path = empty($path) ? '/' : $path;
|
||||
$this->secure = (bool) $secure;
|
||||
$this->httpOnly = (bool) $httpOnly;
|
||||
$this->raw = (bool) $raw;
|
||||
$this->secure = $secure;
|
||||
$this->httpOnly = $httpOnly;
|
||||
$this->raw = $raw;
|
||||
|
||||
if (null !== $sameSite) {
|
||||
if ('' === $sameSite) {
|
||||
$sameSite = null;
|
||||
} elseif (null !== $sameSite) {
|
||||
$sameSite = strtolower($sameSite);
|
||||
}
|
||||
|
||||
if (!\in_array($sameSite, array(self::SAMESITE_LAX, self::SAMESITE_STRICT, null), true)) {
|
||||
if (!\in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE, null], true)) {
|
||||
throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +146,13 @@ class Cookie
|
|||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
|
||||
if ($this->isRaw()) {
|
||||
$str = $this->getName();
|
||||
} else {
|
||||
$str = str_replace(self::$reservedCharsFrom, self::$reservedCharsTo, $this->getName());
|
||||
}
|
||||
|
||||
$str .= '=';
|
||||
|
||||
if ('' === (string) $this->getValue()) {
|
||||
$str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0';
|
||||
|
|
@ -246,7 +256,7 @@ class Cookie
|
|||
*/
|
||||
public function isSecure()
|
||||
{
|
||||
return $this->secure;
|
||||
return $this->secure ?? $this->secureDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -288,4 +298,12 @@ class Cookie
|
|||
{
|
||||
return $this->sameSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $default The default value of the "secure" flag when it is set to null
|
||||
*/
|
||||
public function setSecureDefault(bool $default): void
|
||||
{
|
||||
$this->secureDefault = $default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ class ExpressionRequestMatcher extends RequestMatcher
|
|||
throw new \LogicException('Unable to match the request as the expression language is not available.');
|
||||
}
|
||||
|
||||
return $this->language->evaluate($this->expression, array(
|
||||
return $this->language->evaluate($this->expression, [
|
||||
'request' => $request,
|
||||
'method' => $request->getMethod(),
|
||||
'path' => rawurldecode($request->getPathInfo()),
|
||||
'host' => $request->getHost(),
|
||||
'ip' => $request->getClientIp(),
|
||||
'attributes' => $request->attributes->all(),
|
||||
)) && parent::matches($request);
|
||||
]) && parent::matches($request);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ namespace Symfony\Component\HttpFoundation\File\Exception;
|
|||
*/
|
||||
class AccessDeniedException extends FileException
|
||||
{
|
||||
/**
|
||||
* @param string $path The path to the accessed file
|
||||
*/
|
||||
public function __construct($path)
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(sprintf('The file %s could not be accessed', $path));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ 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($path)
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(sprintf('The file "%s" does not exist', $path));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Symfony\Component\HttpFoundation\File\Exception;
|
|||
|
||||
class UnexpectedTypeException extends FileException
|
||||
{
|
||||
public function __construct($value, $expectedType)
|
||||
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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ namespace Symfony\Component\HttpFoundation\File;
|
|||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
/**
|
||||
* A file in the file system.
|
||||
|
|
@ -31,7 +30,7 @@ class File extends \SplFileInfo
|
|||
*
|
||||
* @throws FileNotFoundException If the given path is not a file
|
||||
*/
|
||||
public function __construct($path, $checkPath = true)
|
||||
public function __construct(string $path, bool $checkPath = true)
|
||||
{
|
||||
if ($checkPath && !is_file($path)) {
|
||||
throw new FileNotFoundException($path);
|
||||
|
|
@ -50,33 +49,28 @@ class File extends \SplFileInfo
|
|||
*
|
||||
* @return string|null The guessed extension or null if it cannot be guessed
|
||||
*
|
||||
* @see ExtensionGuesser
|
||||
* @see MimeTypes
|
||||
* @see getMimeType()
|
||||
*/
|
||||
public function guessExtension()
|
||||
{
|
||||
$type = $this->getMimeType();
|
||||
$guesser = ExtensionGuesser::getInstance();
|
||||
|
||||
return $guesser->guess($type);
|
||||
return MimeTypes::getDefault()->getExtensions($this->getMimeType())[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime type of the file.
|
||||
*
|
||||
* The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(),
|
||||
* mime_content_type() and the system binary "file" (in this order), depending on
|
||||
* which of those are available.
|
||||
* The mime type is guessed using a MimeTypeGuesserInterface instance,
|
||||
* which uses finfo_file() then the "file" system binary,
|
||||
* depending on which of those are available.
|
||||
*
|
||||
* @return string|null The guessed mime type (e.g. "application/pdf")
|
||||
*
|
||||
* @see MimeTypeGuesser
|
||||
* @see MimeTypes
|
||||
*/
|
||||
public function getMimeType()
|
||||
{
|
||||
$guesser = MimeTypeGuesser::getInstance();
|
||||
|
||||
return $guesser->guess($this->getPathname());
|
||||
return MimeTypes::getDefault()->guessMimeType($this->getPathname());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -105,6 +99,9 @@ class File extends \SplFileInfo
|
|||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
protected function getTargetFile($directory, $name = null)
|
||||
{
|
||||
if (!is_dir($directory)) {
|
||||
|
|
@ -125,7 +122,7 @@ class File extends \SplFileInfo
|
|||
*
|
||||
* @param string $name The new file name
|
||||
*
|
||||
* @return string containing
|
||||
* @return string
|
||||
*/
|
||||
protected function getName($name)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', ExtensionGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* A singleton mime type to file extension guesser.
|
||||
*
|
||||
|
|
@ -22,6 +26,8 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
|||
* $guesser->register(new MyCustomExtensionGuesser());
|
||||
*
|
||||
* The last registered guesser is preferred over previously registered ones.
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link MimeTypes} instead
|
||||
*/
|
||||
class ExtensionGuesser implements ExtensionGuesserInterface
|
||||
{
|
||||
|
|
@ -37,7 +43,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guessers = array();
|
||||
protected $guessers = [];
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
|
|
@ -90,5 +96,7 @@ class ExtensionGuesser implements ExtensionGuesserInterface
|
|||
return $extension;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,12 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\Mime\MimeTypesInterface;
|
||||
|
||||
/**
|
||||
* Guesses the file extension corresponding to a given mime type.
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead
|
||||
*/
|
||||
interface ExtensionGuesserInterface
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,11 +13,16 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
|||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\Mime\FileBinaryMimeTypeGuesser as NewFileBinaryMimeTypeGuesser;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileBinaryMimeTypeGuesser::class, NewFileBinaryMimeTypeGuesser::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Guesses the mime type with the binary "file" (only available on *nix).
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link NewFileBinaryMimeTypeGuesser} instead
|
||||
*/
|
||||
class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
{
|
||||
|
|
@ -31,7 +36,7 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
|||
*
|
||||
* @param string $cmd The command to run to get the mime type of a file
|
||||
*/
|
||||
public function __construct($cmd = 'file -b --mime %s 2>/dev/null')
|
||||
public function __construct(string $cmd = 'file -b --mime -- %s 2>/dev/null')
|
||||
{
|
||||
$this->cmd = $cmd;
|
||||
}
|
||||
|
|
@ -74,24 +79,24 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
|||
}
|
||||
|
||||
if (!self::isSupported()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
// need to use --mime instead of -i. see #6641
|
||||
passthru(sprintf($this->cmd, escapeshellarg($path)), $return);
|
||||
passthru(sprintf($this->cmd, escapeshellarg((0 === strpos($path, '-') ? './' : '').$path)), $return);
|
||||
if ($return > 0) {
|
||||
ob_end_clean();
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = trim(ob_get_clean());
|
||||
|
||||
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
|
||||
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) {
|
||||
// it's not a type, but an error message
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $match[1];
|
||||
|
|
|
|||
|
|
@ -13,11 +13,16 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
|||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\Mime\FileinfoMimeTypeGuesser as NewFileinfoMimeTypeGuesser;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', FileinfoMimeTypeGuesser::class, NewFileinfoMimeTypeGuesser::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Guesses the mime type using the PECL extension FileInfo.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link NewFileinfoMimeTypeGuesser} instead
|
||||
*/
|
||||
class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
{
|
||||
|
|
@ -26,9 +31,9 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
|
|||
/**
|
||||
* @param string $magicFile A magic file to use with the finfo instance
|
||||
*
|
||||
* @see http://www.php.net/manual/en/function.finfo-open.php
|
||||
* @see https://php.net/finfo-open
|
||||
*/
|
||||
public function __construct($magicFile = null)
|
||||
public function __construct(string $magicFile = null)
|
||||
{
|
||||
$this->magicFile = $magicFile;
|
||||
}
|
||||
|
|
@ -57,11 +62,11 @@ class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
|
|||
}
|
||||
|
||||
if (!self::isSupported()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $finfo->file($path);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,14 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation\File\MimeType;
|
||||
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeExtensionGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Provides a best-guess mapping of mime type to file extension.
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link MimeTypes} instead
|
||||
*/
|
||||
class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
||||
{
|
||||
|
|
@ -20,11 +26,11 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
|||
* A map of mime types and their default extensions.
|
||||
*
|
||||
* This list has been placed under the public domain by the Apache HTTPD project.
|
||||
* This list has been updated from upstream on 2013-04-23.
|
||||
* This list has been updated from upstream on 2019-01-14.
|
||||
*
|
||||
* @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
* @see https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
*/
|
||||
protected $defaultExtensions = array(
|
||||
protected $defaultExtensions = [
|
||||
'application/andrew-inset' => 'ez',
|
||||
'application/applixware' => 'aw',
|
||||
'application/atom+xml' => 'atom',
|
||||
|
|
@ -618,7 +624,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
|||
'audio/adpcm' => 'adp',
|
||||
'audio/basic' => 'au',
|
||||
'audio/midi' => 'mid',
|
||||
'audio/mp4' => 'mp4a',
|
||||
'audio/mp4' => 'm4a',
|
||||
'audio/mpeg' => 'mpga',
|
||||
'audio/ogg' => 'oga',
|
||||
'audio/s3m' => 's3m',
|
||||
|
|
@ -639,6 +645,7 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
|||
'audio/x-aiff' => 'aif',
|
||||
'audio/x-caf' => 'caf',
|
||||
'audio/x-flac' => 'flac',
|
||||
'audio/x-hx-aac-adts' => 'aac',
|
||||
'audio/x-matroska' => 'mka',
|
||||
'audio/x-mpegurl' => 'm3u',
|
||||
'audio/x-ms-wax' => 'wax',
|
||||
|
|
@ -653,6 +660,11 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
|||
'chemical/x-cml' => 'cml',
|
||||
'chemical/x-csml' => 'csml',
|
||||
'chemical/x-xyz' => 'xyz',
|
||||
'font/collection' => 'ttc',
|
||||
'font/otf' => 'otf',
|
||||
'font/ttf' => 'ttf',
|
||||
'font/woff' => 'woff',
|
||||
'font/woff2' => 'woff2',
|
||||
'image/bmp' => 'bmp',
|
||||
'image/x-ms-bmp' => 'bmp',
|
||||
'image/cgm' => 'cgm',
|
||||
|
|
@ -669,8 +681,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
|||
'image/tiff' => 'tiff',
|
||||
'image/vnd.adobe.photoshop' => 'psd',
|
||||
'image/vnd.dece.graphic' => 'uvi',
|
||||
'image/vnd.dvb.subtitle' => 'sub',
|
||||
'image/vnd.djvu' => 'djvu',
|
||||
'image/vnd.dvb.subtitle' => 'sub',
|
||||
'image/vnd.dwg' => 'dwg',
|
||||
'image/vnd.dxf' => 'dxf',
|
||||
'image/vnd.fastbidsheet' => 'fbs',
|
||||
|
|
@ -732,8 +744,8 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
|||
'text/vcard' => 'vcard',
|
||||
'text/vnd.curl' => 'curl',
|
||||
'text/vnd.curl.dcurl' => 'dcurl',
|
||||
'text/vnd.curl.scurl' => 'scurl',
|
||||
'text/vnd.curl.mcurl' => 'mcurl',
|
||||
'text/vnd.curl.scurl' => 'scurl',
|
||||
'text/vnd.dvb.subtitle' => 'sub',
|
||||
'text/vnd.fly' => 'fly',
|
||||
'text/vnd.fmi.flexstor' => 'flx',
|
||||
|
|
@ -747,10 +759,10 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
|||
'text/x-asm' => 's',
|
||||
'text/x-c' => 'c',
|
||||
'text/x-fortran' => 'f',
|
||||
'text/x-pascal' => 'p',
|
||||
'text/x-java-source' => 'java',
|
||||
'text/x-opml' => 'opml',
|
||||
'text/x-nfo' => 'nfo',
|
||||
'text/x-opml' => 'opml',
|
||||
'text/x-pascal' => 'p',
|
||||
'text/x-setext' => 'etx',
|
||||
'text/x-sfv' => 'sfv',
|
||||
'text/x-uuencode' => 'uu',
|
||||
|
|
@ -796,13 +808,19 @@ class MimeTypeExtensionGuesser implements ExtensionGuesserInterface
|
|||
'video/x-sgi-movie' => 'movie',
|
||||
'video/x-smv' => 'smv',
|
||||
'x-conference/x-cooltalk' => 'ice',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function guess($mimeType)
|
||||
{
|
||||
return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null;
|
||||
if (isset($this->defaultExtensions[$mimeType])) {
|
||||
return $this->defaultExtensions[$mimeType];
|
||||
}
|
||||
|
||||
$lcMimeType = strtolower($mimeType);
|
||||
|
||||
return isset($this->defaultExtensions[$lcMimeType]) ? $this->defaultExtensions[$lcMimeType] : null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
|||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', MimeTypeGuesser::class, MimeTypes::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* A singleton mime type guesser.
|
||||
|
|
@ -51,7 +54,7 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guessers = array();
|
||||
protected $guessers = [];
|
||||
|
||||
/**
|
||||
* Returns the singleton instance.
|
||||
|
|
@ -129,5 +132,7 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface
|
|||
if (2 === \count($this->guessers) && !FileBinaryMimeTypeGuesser::isSupported() && !FileinfoMimeTypeGuesser::isSupported()) {
|
||||
throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,14 @@ namespace Symfony\Component\HttpFoundation\File\MimeType;
|
|||
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
||||
use Symfony\Component\Mime\MimeTypesInterface;
|
||||
|
||||
/**
|
||||
* Guesses the mime type of a file.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use {@link MimeTypesInterface} instead
|
||||
*/
|
||||
interface MimeTypeGuesserInterface
|
||||
{
|
||||
|
|
@ -26,7 +29,7 @@ interface MimeTypeGuesserInterface
|
|||
*
|
||||
* @param string $path The path to the file
|
||||
*
|
||||
* @return string The mime type or NULL, if none could be guessed
|
||||
* @return string|null The mime type or NULL, if none could be guessed
|
||||
*
|
||||
* @throws FileNotFoundException If the file does not exist
|
||||
* @throws AccessDeniedException If the file could not be read
|
||||
|
|
|
|||
|
|
@ -11,9 +11,16 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation\File;
|
||||
|
||||
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\FileNotFoundException;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser;
|
||||
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\Mime\MimeTypes;
|
||||
|
||||
/**
|
||||
* A file uploaded through a form.
|
||||
|
|
@ -27,7 +34,6 @@ class UploadedFile extends File
|
|||
private $test = false;
|
||||
private $originalName;
|
||||
private $mimeType;
|
||||
private $size;
|
||||
private $error;
|
||||
|
||||
/**
|
||||
|
|
@ -47,7 +53,6 @@ class UploadedFile extends File
|
|||
* @param string $path The full temporary path to the file
|
||||
* @param string $originalName The original file name of the uploaded file
|
||||
* @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream
|
||||
* @param int|null $size The file size provided by the uploader
|
||||
* @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
|
||||
* @param bool $test Whether the test mode is active
|
||||
* Local files are used in test mode hence the code should not enforce HTTP uploads
|
||||
|
|
@ -55,13 +60,19 @@ class UploadedFile extends File
|
|||
* @throws FileException If file_uploads is disabled
|
||||
* @throws FileNotFoundException If the file does not exist
|
||||
*/
|
||||
public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false)
|
||||
public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false)
|
||||
{
|
||||
$this->originalName = $this->getName($originalName);
|
||||
$this->mimeType = $mimeType ?: 'application/octet-stream';
|
||||
$this->size = $size;
|
||||
|
||||
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->test = (bool) $test;
|
||||
$this->test = $test;
|
||||
|
||||
parent::__construct($path, UPLOAD_ERR_OK === $this->error);
|
||||
}
|
||||
|
|
@ -129,10 +140,7 @@ class UploadedFile extends File
|
|||
*/
|
||||
public function guessClientExtension()
|
||||
{
|
||||
$type = $this->getClientMimeType();
|
||||
$guesser = ExtensionGuesser::getInstance();
|
||||
|
||||
return $guesser->guess($type);
|
||||
return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -141,11 +149,15 @@ 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 int|null The file size
|
||||
* @deprecated since Symfony 4.1, use getSize() instead.
|
||||
*
|
||||
* @return int|null The file sizes
|
||||
*/
|
||||
public function getClientSize()
|
||||
{
|
||||
return $this->size;
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return $this->getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -204,6 +216,23 @@ class UploadedFile extends File
|
|||
return $target;
|
||||
}
|
||||
|
||||
switch ($this->error) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
throw new IniSizeFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
throw new FormSizeFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
throw new PartialFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
throw new NoFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_CANT_WRITE:
|
||||
throw new CannotWriteFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
throw new NoTmpDirFileException($this->getErrorMessage());
|
||||
case UPLOAD_ERR_EXTENSION:
|
||||
throw new ExtensionFileException($this->getErrorMessage());
|
||||
}
|
||||
|
||||
throw new FileException($this->getErrorMessage());
|
||||
}
|
||||
|
||||
|
|
@ -214,13 +243,24 @@ class UploadedFile extends File
|
|||
*/
|
||||
public static function getMaxFilesize()
|
||||
{
|
||||
$iniMax = strtolower(ini_get('upload_max_filesize'));
|
||||
$sizePostMax = self::parseFilesize(ini_get('post_max_size'));
|
||||
$sizeUploadMax = self::parseFilesize(ini_get('upload_max_filesize'));
|
||||
|
||||
if ('' === $iniMax) {
|
||||
return PHP_INT_MAX;
|
||||
return min($sizePostMax ?: PHP_INT_MAX, $sizeUploadMax ?: PHP_INT_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given size from an ini value in bytes.
|
||||
*/
|
||||
private static function parseFilesize($size): int
|
||||
{
|
||||
if ('' === $size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$max = ltrim($iniMax, '+');
|
||||
$size = strtolower($size);
|
||||
|
||||
$max = ltrim($size, '+');
|
||||
if (0 === strpos($max, '0x')) {
|
||||
$max = \intval($max, 16);
|
||||
} elseif (0 === strpos($max, '0')) {
|
||||
|
|
@ -229,7 +269,7 @@ class UploadedFile extends File
|
|||
$max = (int) $max;
|
||||
}
|
||||
|
||||
switch (substr($iniMax, -1)) {
|
||||
switch (substr($size, -1)) {
|
||||
case 't': $max *= 1024;
|
||||
// no break
|
||||
case 'g': $max *= 1024;
|
||||
|
|
@ -249,7 +289,7 @@ class UploadedFile extends File
|
|||
*/
|
||||
public function getErrorMessage()
|
||||
{
|
||||
static $errors = array(
|
||||
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.',
|
||||
|
|
@ -257,7 +297,7 @@ class UploadedFile extends File
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
|
|||
*/
|
||||
class FileBag extends ParameterBag
|
||||
{
|
||||
private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type');
|
||||
private static $fileKeys = ['error', 'name', 'size', 'tmp_name', 'type'];
|
||||
|
||||
/**
|
||||
* @param array $parameters An array of HTTP files
|
||||
* @param array|UploadedFile[] $parameters An array of HTTP files
|
||||
*/
|
||||
public function __construct(array $parameters = array())
|
||||
public function __construct(array $parameters = [])
|
||||
{
|
||||
$this->replace($parameters);
|
||||
}
|
||||
|
|
@ -34,9 +34,9 @@ class FileBag extends ParameterBag
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function replace(array $files = array())
|
||||
public function replace(array $files = [])
|
||||
{
|
||||
$this->parameters = array();
|
||||
$this->parameters = [];
|
||||
$this->add($files);
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ class FileBag extends ParameterBag
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(array $files = array())
|
||||
public function add(array $files = [])
|
||||
{
|
||||
foreach ($files as $key => $file) {
|
||||
$this->set($key, $file);
|
||||
|
|
@ -75,8 +75,8 @@ class FileBag extends ParameterBag
|
|||
return $file;
|
||||
}
|
||||
|
||||
$file = $this->fixPhpFilesArray($file);
|
||||
if (\is_array($file)) {
|
||||
$file = $this->fixPhpFilesArray($file);
|
||||
$keys = array_keys($file);
|
||||
sort($keys);
|
||||
|
||||
|
|
@ -84,10 +84,10 @@ class FileBag extends ParameterBag
|
|||
if (UPLOAD_ERR_NO_FILE == $file['error']) {
|
||||
$file = null;
|
||||
} else {
|
||||
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']);
|
||||
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error'], false);
|
||||
}
|
||||
} else {
|
||||
$file = array_map(array($this, 'convertFileInformation'), $file);
|
||||
$file = array_map([$this, 'convertFileInformation'], $file);
|
||||
if (array_keys($keys) === $keys) {
|
||||
$file = array_filter($file);
|
||||
}
|
||||
|
|
@ -109,14 +109,12 @@ class FileBag extends ParameterBag
|
|||
* It's safe to pass an already converted array, in which case this method
|
||||
* just returns the original array unmodified.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function fixPhpFilesArray($data)
|
||||
{
|
||||
if (!\is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$keys = array_keys($data);
|
||||
sort($keys);
|
||||
|
||||
|
|
@ -130,13 +128,13 @@ class FileBag extends ParameterBag
|
|||
}
|
||||
|
||||
foreach ($data['name'] as $key => $name) {
|
||||
$files[$key] = $this->fixPhpFilesArray(array(
|
||||
$files[$key] = $this->fixPhpFilesArray([
|
||||
'error' => $data['error'][$key],
|
||||
'name' => $name,
|
||||
'type' => $data['type'][$key],
|
||||
'tmp_name' => $data['tmp_name'][$key],
|
||||
'size' => $data['size'][$key],
|
||||
));
|
||||
]);
|
||||
}
|
||||
|
||||
return $files;
|
||||
|
|
|
|||
104
pfosten/vendor/symfony/http-foundation/HeaderBag.php
vendored
104
pfosten/vendor/symfony/http-foundation/HeaderBag.php
vendored
|
|
@ -18,13 +18,13 @@ namespace Symfony\Component\HttpFoundation;
|
|||
*/
|
||||
class HeaderBag implements \IteratorAggregate, \Countable
|
||||
{
|
||||
protected $headers = array();
|
||||
protected $cacheControl = array();
|
||||
protected const UPPER = '_ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
protected const LOWER = '-abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
/**
|
||||
* @param array $headers An array of HTTP headers
|
||||
*/
|
||||
public function __construct(array $headers = array())
|
||||
protected $headers = [];
|
||||
protected $cacheControl = [];
|
||||
|
||||
public function __construct(array $headers = [])
|
||||
{
|
||||
foreach ($headers as $key => $values) {
|
||||
$this->set($key, $values);
|
||||
|
|
@ -46,7 +46,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
$max = max(array_map('strlen', array_keys($headers))) + 1;
|
||||
$content = '';
|
||||
foreach ($headers as $name => $values) {
|
||||
$name = implode('-', array_map('ucfirst', explode('-', $name)));
|
||||
$name = ucwords($name, '-');
|
||||
foreach ($values as $value) {
|
||||
$content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
|
||||
}
|
||||
|
|
@ -58,10 +58,16 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
/**
|
||||
* Returns the headers.
|
||||
*
|
||||
* @param string|null $key The name of the headers to return or null to get them all
|
||||
*
|
||||
* @return array An array of headers
|
||||
*/
|
||||
public function all()
|
||||
public function all(/*string $key = null*/)
|
||||
{
|
||||
if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) {
|
||||
return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? [];
|
||||
}
|
||||
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
|
|
@ -77,19 +83,15 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
|
||||
/**
|
||||
* Replaces the current HTTP headers by a new set.
|
||||
*
|
||||
* @param array $headers An array of HTTP headers
|
||||
*/
|
||||
public function replace(array $headers = array())
|
||||
public function replace(array $headers = [])
|
||||
{
|
||||
$this->headers = array();
|
||||
$this->headers = [];
|
||||
$this->add($headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new headers the current HTTP headers set.
|
||||
*
|
||||
* @param array $headers An array of HTTP headers
|
||||
*/
|
||||
public function add(array $headers)
|
||||
{
|
||||
|
|
@ -101,30 +103,31 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
/**
|
||||
* Returns a header value by name.
|
||||
*
|
||||
* @param string $key The header name
|
||||
* @param string|string[]|null $default The default value
|
||||
* @param bool $first Whether to return the first value or all header values
|
||||
* @param string $key The header name
|
||||
* @param string|null $default The default value
|
||||
*
|
||||
* @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise
|
||||
* @return string|null The first header value or default value
|
||||
*/
|
||||
public function get($key, $default = null, $first = true)
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$headers = $this->all();
|
||||
$headers = $this->all((string) $key);
|
||||
if (2 < \func_num_args()) {
|
||||
@trigger_error(sprintf('Passing a third argument to "%s()" is deprecated since Symfony 4.4, use method "all()" instead', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
if (!array_key_exists($key, $headers)) {
|
||||
if (null === $default) {
|
||||
return $first ? null : array();
|
||||
if (!func_get_arg(2)) {
|
||||
return $headers;
|
||||
}
|
||||
|
||||
return $first ? $default : array($default);
|
||||
}
|
||||
|
||||
if ($first) {
|
||||
return \count($headers[$key]) ? $headers[$key][0] : $default;
|
||||
if (!$headers) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $headers[$key];
|
||||
if (null === $headers[0]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) $headers[0];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -136,7 +139,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function set($key, $values, $replace = true)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$key = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
if (\is_array($values)) {
|
||||
$values = array_values($values);
|
||||
|
|
@ -148,7 +151,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
}
|
||||
} else {
|
||||
if (true === $replace || !isset($this->headers[$key])) {
|
||||
$this->headers[$key] = array($values);
|
||||
$this->headers[$key] = [$values];
|
||||
} else {
|
||||
$this->headers[$key][] = $values;
|
||||
}
|
||||
|
|
@ -168,7 +171,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists(str_replace('_', '-', strtolower($key)), $this->all());
|
||||
return \array_key_exists(strtr($key, self::UPPER, self::LOWER), $this->all());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -181,7 +184,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function contains($key, $value)
|
||||
{
|
||||
return \in_array($value, $this->get($key, null, false));
|
||||
return \in_array($value, $this->all((string) $key));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -191,22 +194,21 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
$key = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
unset($this->headers[$key]);
|
||||
|
||||
if ('cache-control' === $key) {
|
||||
$this->cacheControl = array();
|
||||
$this->cacheControl = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP header value converted to a date.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param \DateTime $default The default value
|
||||
* @param string $key The parameter key
|
||||
*
|
||||
* @return null|\DateTime The parsed DateTime or the default value if the header does not exist
|
||||
* @return \DateTimeInterface|null The parsed DateTime or the default value if the header does not exist
|
||||
*
|
||||
* @throws \RuntimeException When the HTTP header is not parseable
|
||||
*/
|
||||
|
|
@ -245,7 +247,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function hasCacheControlDirective($key)
|
||||
{
|
||||
return array_key_exists($key, $this->cacheControl);
|
||||
return \array_key_exists($key, $this->cacheControl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -257,7 +259,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function getCacheControlDirective($key)
|
||||
{
|
||||
return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
|
||||
return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -294,21 +296,9 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
|
||||
protected function getCacheControlHeader()
|
||||
{
|
||||
$parts = array();
|
||||
ksort($this->cacheControl);
|
||||
foreach ($this->cacheControl as $key => $value) {
|
||||
if (true === $value) {
|
||||
$parts[] = $key;
|
||||
} else {
|
||||
if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
|
||||
$value = '"'.$value.'"';
|
||||
}
|
||||
|
||||
$parts[] = "$key=$value";
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $parts);
|
||||
return HeaderUtils::toString($this->cacheControl, ',');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -320,12 +310,8 @@ class HeaderBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
protected function parseCacheControl($header)
|
||||
{
|
||||
$cacheControl = array();
|
||||
preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $match) {
|
||||
$cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true);
|
||||
}
|
||||
$parts = HeaderUtils::split($header, ',=');
|
||||
|
||||
return $cacheControl;
|
||||
return HeaderUtils::combine($parts);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Symfony\Component\HttpFoundation;
|
|||
*/
|
||||
class IpUtils
|
||||
{
|
||||
private static $checkedIps = array();
|
||||
private static $checkedIps = [];
|
||||
|
||||
/**
|
||||
* This class should not be instantiated.
|
||||
|
|
@ -38,7 +38,7 @@ class IpUtils
|
|||
public static function checkIp($requestIp, $ips)
|
||||
{
|
||||
if (!\is_array($ips)) {
|
||||
$ips = array($ips);
|
||||
$ips = [$ips];
|
||||
}
|
||||
|
||||
$method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
|
||||
|
|
@ -153,4 +153,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
*/
|
||||
|
|
@ -39,7 +39,7 @@ class JsonResponse extends Response
|
|||
* @param array $headers An array of response headers
|
||||
* @param bool $json If the data is already a JSON string
|
||||
*/
|
||||
public function __construct($data = null, $status = 200, $headers = array(), $json = false)
|
||||
public function __construct($data = null, int $status = 200, array $headers = [], bool $json = false)
|
||||
{
|
||||
parent::__construct('', $status, $headers);
|
||||
|
||||
|
|
@ -55,24 +55,35 @@ class JsonResponse extends Response
|
|||
*
|
||||
* Example:
|
||||
*
|
||||
* return JsonResponse::create($data, 200)
|
||||
* return JsonResponse::create(['key' => 'value'])
|
||||
* ->setSharedMaxAge(300);
|
||||
*
|
||||
* @param mixed $data The json response data
|
||||
* @param mixed $data The JSON response data
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($data = null, $status = 200, $headers = array())
|
||||
public static function create($data = null, $status = 200, $headers = [])
|
||||
{
|
||||
return new static($data, $status, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make easier the creation of JsonResponse from raw json.
|
||||
* Factory method for chainability.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* return JsonResponse::fromJsonString('{"key": "value"}')
|
||||
* ->setSharedMaxAge(300);
|
||||
*
|
||||
* @param string|null $data The JSON response string
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function fromJsonString($data = null, $status = 200, $headers = array())
|
||||
public static function fromJsonString($data = null, $status = 200, $headers = [])
|
||||
{
|
||||
return new static($data, $status, $headers, true);
|
||||
}
|
||||
|
|
@ -89,16 +100,16 @@ class JsonResponse extends Response
|
|||
public function setCallback($callback = null)
|
||||
{
|
||||
if (null !== $callback) {
|
||||
// partially taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/
|
||||
// partially taken from https://geekality.net/2011/08/03/valid-javascript-identifier/
|
||||
// partially taken from https://github.com/willdurand/JsonpCallbackValidator
|
||||
// JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details.
|
||||
// (c) William Durand <william.durand1@gmail.com>
|
||||
$pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u';
|
||||
$reserved = array(
|
||||
$reserved = [
|
||||
'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while',
|
||||
'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export',
|
||||
'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false',
|
||||
);
|
||||
];
|
||||
$parts = explode('.', $callback);
|
||||
foreach ($parts as $part) {
|
||||
if (!preg_match($pattern, $part) || \in_array($part, $reserved, true)) {
|
||||
|
|
@ -137,31 +148,19 @@ class JsonResponse extends Response
|
|||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setData($data = array())
|
||||
public function setData($data = [])
|
||||
{
|
||||
if (\defined('HHVM_VERSION')) {
|
||||
// HHVM does not trigger any warnings and let exceptions
|
||||
// thrown from a JsonSerializable object pass through.
|
||||
// If only PHP did the same...
|
||||
try {
|
||||
$data = json_encode($data, $this->encodingOptions);
|
||||
} else {
|
||||
if (!interface_exists('JsonSerializable', false)) {
|
||||
set_error_handler(function () { return false; });
|
||||
try {
|
||||
$data = @json_encode($data, $this->encodingOptions);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$data = json_encode($data, $this->encodingOptions);
|
||||
} catch (\Exception $e) {
|
||||
if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
|
||||
throw $e->getPrevious() ?: $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
|
||||
throw $e->getPrevious() ?: $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 70300 && (JSON_THROW_ON_ERROR & $this->encodingOptions)) {
|
||||
return $this->setJson($data);
|
||||
}
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2004-2018 Fabien Potencier
|
||||
Copyright (c) 2004-2019 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
|
||||
|
|
|
|||
|
|
@ -23,10 +23,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function __construct(array $parameters = array())
|
||||
public function __construct(array $parameters = [])
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
|
@ -53,20 +50,16 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
|||
|
||||
/**
|
||||
* Replaces the current parameters by a new set.
|
||||
*
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function replace(array $parameters = array())
|
||||
public function replace(array $parameters = [])
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds parameters.
|
||||
*
|
||||
* @param array $parameters An array of parameters
|
||||
*/
|
||||
public function add(array $parameters = array())
|
||||
public function add(array $parameters = [])
|
||||
{
|
||||
$this->parameters = array_replace($this->parameters, $parameters);
|
||||
}
|
||||
|
|
@ -81,7 +74,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
|
||||
return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -104,7 +97,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists($key, $this->parameters);
|
||||
return \array_key_exists($key, $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -154,7 +147,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
|||
public function getDigits($key, $default = '')
|
||||
{
|
||||
// we need to remove - and + because they're allowed in the filter
|
||||
return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
|
||||
return str_replace(['-', '+'], '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -174,7 +167,7 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
|||
* Returns the parameter value converted to boolean.
|
||||
*
|
||||
* @param string $key The parameter key
|
||||
* @param mixed $default The default value if the parameter key does not exist
|
||||
* @param bool $default The default value if the parameter key does not exist
|
||||
*
|
||||
* @return bool The filtered value
|
||||
*/
|
||||
|
|
@ -191,17 +184,17 @@ class ParameterBag implements \IteratorAggregate, \Countable
|
|||
* @param int $filter FILTER_* constant
|
||||
* @param mixed $options Filter options
|
||||
*
|
||||
* @see http://php.net/manual/en/function.filter-var.php
|
||||
* @see https://php.net/filter-var
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array())
|
||||
public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = [])
|
||||
{
|
||||
$value = $this->get($key, $default);
|
||||
|
||||
// Always turn $options into an array - this allows filter_var option shortcuts.
|
||||
if (!\is_array($options) && $options) {
|
||||
$options = array('flags' => $options);
|
||||
$options = ['flags' => $options];
|
||||
}
|
||||
|
||||
// Add a convenience check for arrays.
|
||||
|
|
|
|||
|
|
@ -30,10 +30,15 @@ class RedirectResponse extends Response
|
|||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc2616#section-10.3
|
||||
* @see https://tools.ietf.org/html/rfc2616#section-10.3
|
||||
*/
|
||||
public function __construct($url, $status = 302, $headers = array())
|
||||
public function __construct(?string $url, int $status = 302, array $headers = [])
|
||||
{
|
||||
if (null === $url) {
|
||||
@trigger_error(sprintf('Passing a null url when instantiating a "%s" is deprecated since Symfony 4.4.', __CLASS__), E_USER_DEPRECATED);
|
||||
$url = '';
|
||||
}
|
||||
|
||||
parent::__construct('', $status, $headers);
|
||||
|
||||
$this->setTargetUrl($url);
|
||||
|
|
@ -42,7 +47,7 @@ class RedirectResponse extends Response
|
|||
throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
|
||||
}
|
||||
|
||||
if (301 == $status && !array_key_exists('cache-control', $headers)) {
|
||||
if (301 == $status && !\array_key_exists('cache-control', array_change_key_case($headers, \CASE_LOWER))) {
|
||||
$this->headers->remove('cache-control');
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +61,7 @@ class RedirectResponse extends Response
|
|||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($url = '', $status = 302, $headers = array())
|
||||
public static function create($url = '', $status = 302, $headers = [])
|
||||
{
|
||||
return new static($url, $status, $headers);
|
||||
}
|
||||
|
|
@ -82,7 +87,7 @@ class RedirectResponse extends Response
|
|||
*/
|
||||
public function setTargetUrl($url)
|
||||
{
|
||||
if (empty($url)) {
|
||||
if ('' === ($url ?? '')) {
|
||||
throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +98,7 @@ 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>
|
||||
|
|
|
|||
573
pfosten/vendor/symfony/http-foundation/Request.php
vendored
573
pfosten/vendor/symfony/http-foundation/Request.php
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -29,40 +29,43 @@ class RequestMatcher implements RequestMatcherInterface
|
|||
private $host;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @var int|null
|
||||
*/
|
||||
private $methods = array();
|
||||
private $port;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $ips = array();
|
||||
private $methods = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $ips = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $attributes = array();
|
||||
private $attributes = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $schemes = array();
|
||||
private $schemes = [];
|
||||
|
||||
/**
|
||||
* @param string|null $path
|
||||
* @param string|null $host
|
||||
* @param string|string[]|null $methods
|
||||
* @param string|string[]|null $ips
|
||||
* @param array $attributes
|
||||
* @param string|string[]|null $schemes
|
||||
*/
|
||||
public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null)
|
||||
public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null, int $port = null)
|
||||
{
|
||||
$this->matchPath($path);
|
||||
$this->matchHost($host);
|
||||
$this->matchMethod($methods);
|
||||
$this->matchIps($ips);
|
||||
$this->matchScheme($schemes);
|
||||
$this->matchPort($port);
|
||||
|
||||
foreach ($attributes as $k => $v) {
|
||||
$this->matchAttribute($k, $v);
|
||||
|
|
@ -76,7 +79,7 @@ class RequestMatcher implements RequestMatcherInterface
|
|||
*/
|
||||
public function matchScheme($scheme)
|
||||
{
|
||||
$this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : array();
|
||||
$this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -89,6 +92,16 @@ class RequestMatcher implements RequestMatcherInterface
|
|||
$this->host = $regexp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a check for the the URL port.
|
||||
*
|
||||
* @param int|null $port The port number to connect to
|
||||
*/
|
||||
public function matchPort(?int $port)
|
||||
{
|
||||
$this->port = $port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a check for the URL path info.
|
||||
*
|
||||
|
|
@ -116,7 +129,7 @@ class RequestMatcher implements RequestMatcherInterface
|
|||
*/
|
||||
public function matchIps($ips)
|
||||
{
|
||||
$this->ips = null !== $ips ? (array) $ips : array();
|
||||
$this->ips = null !== $ips ? (array) $ips : [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,7 +139,7 @@ class RequestMatcher implements RequestMatcherInterface
|
|||
*/
|
||||
public function matchMethod($method)
|
||||
{
|
||||
$this->methods = null !== $method ? array_map('strtoupper', (array) $method) : array();
|
||||
$this->methods = null !== $method ? array_map('strtoupper', (array) $method) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -167,6 +180,10 @@ class RequestMatcher implements RequestMatcherInterface
|
|||
return false;
|
||||
}
|
||||
|
||||
if (null !== $this->port && 0 < $this->port && $request->getPort() !== $this->port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class RequestStack
|
|||
/**
|
||||
* @var Request[]
|
||||
*/
|
||||
private $requests = array();
|
||||
private $requests = [];
|
||||
|
||||
/**
|
||||
* Pushes a Request on the stack.
|
||||
|
|
@ -47,7 +47,7 @@ class RequestStack
|
|||
public function pop()
|
||||
{
|
||||
if (!$this->requests) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_pop($this->requests);
|
||||
|
|
@ -73,7 +73,7 @@ class RequestStack
|
|||
public function getMasterRequest()
|
||||
{
|
||||
if (!$this->requests) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->requests[0];
|
||||
|
|
@ -95,7 +95,7 @@ class RequestStack
|
|||
$pos = \count($this->requests) - 2;
|
||||
|
||||
if (!isset($this->requests[$pos])) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->requests[$pos];
|
||||
|
|
|
|||
385
pfosten/vendor/symfony/http-foundation/Response.php
vendored
385
pfosten/vendor/symfony/http-foundation/Response.php
vendored
|
|
@ -88,7 +88,7 @@ class Response
|
|||
const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
|
||||
* @var ResponseHeaderBag
|
||||
*/
|
||||
public $headers;
|
||||
|
||||
|
|
@ -121,14 +121,14 @@ class Response
|
|||
* Status codes translation table.
|
||||
*
|
||||
* The list of codes is complete according to the
|
||||
* {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry}
|
||||
* {@link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml Hypertext Transfer Protocol (HTTP) Status Code Registry}
|
||||
* (last updated 2016-03-01).
|
||||
*
|
||||
* Unless otherwise noted, the status code is defined in RFC2616.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $statusTexts = array(
|
||||
public static $statusTexts = [
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
102 => 'Processing', // RFC2518
|
||||
|
|
@ -191,16 +191,12 @@ class Response
|
|||
508 => 'Loop Detected', // RFC5842
|
||||
510 => 'Not Extended', // RFC2774
|
||||
511 => 'Network Authentication Required', // RFC6585
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* @param mixed $content The response content, see setContent()
|
||||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*
|
||||
* @throws \InvalidArgumentException When the HTTP status code is not valid
|
||||
*/
|
||||
public function __construct($content = '', $status = 200, $headers = array())
|
||||
public function __construct($content = '', int $status = 200, array $headers = [])
|
||||
{
|
||||
$this->headers = new ResponseHeaderBag($headers);
|
||||
$this->setContent($content);
|
||||
|
|
@ -222,7 +218,7 @@ class Response
|
|||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($content = '', $status = 200, $headers = array())
|
||||
public static function create($content = '', $status = 200, $headers = [])
|
||||
{
|
||||
return new static($content, $status, $headers);
|
||||
}
|
||||
|
|
@ -274,7 +270,7 @@ class Response
|
|||
} else {
|
||||
// Content-type based on the Request
|
||||
if (!$headers->has('Content-Type')) {
|
||||
$format = $request->getRequestFormat();
|
||||
$format = $request->getPreferredFormat();
|
||||
if (null !== $format && $mimeType = $request->getMimeType($format)) {
|
||||
$headers->set('Content-Type', $mimeType);
|
||||
}
|
||||
|
|
@ -310,13 +306,19 @@ class Response
|
|||
}
|
||||
|
||||
// Check if we need to send extra expire info headers
|
||||
if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) {
|
||||
$this->headers->set('pragma', 'no-cache');
|
||||
$this->headers->set('expires', -1);
|
||||
if ('1.0' == $this->getProtocolVersion() && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
|
||||
$headers->set('pragma', 'no-cache');
|
||||
$headers->set('expires', -1);
|
||||
}
|
||||
|
||||
$this->ensureIEOverSSLCompatibility($request);
|
||||
|
||||
if ($request->isSecure()) {
|
||||
foreach ($headers->getCookies() as $cookie) {
|
||||
$cookie->setSecureDefault(true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
@ -334,14 +336,15 @@ class Response
|
|||
|
||||
// headers
|
||||
foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
|
||||
$replace = 0 === strcasecmp($name, 'Content-Type');
|
||||
foreach ($values as $value) {
|
||||
header($name.': '.$value, false, $this->statusCode);
|
||||
header($name.': '.$value, $replace, $this->statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -374,7 +377,7 @@ class Response
|
|||
|
||||
if (\function_exists('fastcgi_finish_request')) {
|
||||
fastcgi_finish_request();
|
||||
} elseif (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) {
|
||||
} elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
|
||||
static::closeOutputBuffers(0, true);
|
||||
}
|
||||
|
||||
|
|
@ -394,7 +397,7 @@ class Response
|
|||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable(array($content, '__toString'))) {
|
||||
if (null !== $content && !\is_string($content) && !is_numeric($content) && !\is_callable([$content, '__toString'])) {
|
||||
throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', \gettype($content)));
|
||||
}
|
||||
|
||||
|
|
@ -406,7 +409,7 @@ class Response
|
|||
/**
|
||||
* Gets the current response content.
|
||||
*
|
||||
* @return string Content
|
||||
* @return string|false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
|
|
@ -416,13 +419,11 @@ class Response
|
|||
/**
|
||||
* Sets the HTTP protocol version (1.0 or 1.1).
|
||||
*
|
||||
* @param string $version The HTTP protocol version
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setProtocolVersion($version)
|
||||
public function setProtocolVersion(string $version)
|
||||
{
|
||||
$this->version = $version;
|
||||
|
||||
|
|
@ -432,11 +433,9 @@ class Response
|
|||
/**
|
||||
* Gets the HTTP protocol version.
|
||||
*
|
||||
* @return string The HTTP protocol version
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getProtocolVersion()
|
||||
public function getProtocolVersion(): string
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
|
@ -447,18 +446,15 @@ class Response
|
|||
* If the status text is null it will be automatically populated for the known
|
||||
* status codes and left empty otherwise.
|
||||
*
|
||||
* @param int $code HTTP status code
|
||||
* @param mixed $text HTTP status text
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException When the HTTP status code is not valid
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setStatusCode($code, $text = null)
|
||||
public function setStatusCode(int $code, $text = null)
|
||||
{
|
||||
$this->statusCode = $code = (int) $code;
|
||||
$this->statusCode = $code;
|
||||
if ($this->isInvalid()) {
|
||||
throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
|
||||
}
|
||||
|
|
@ -483,11 +479,9 @@ class Response
|
|||
/**
|
||||
* Retrieves the status code for the current web response.
|
||||
*
|
||||
* @return int Status code
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getStatusCode()
|
||||
public function getStatusCode(): int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
|
@ -495,13 +489,11 @@ class Response
|
|||
/**
|
||||
* Sets the response charset.
|
||||
*
|
||||
* @param string $charset Character set
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setCharset($charset)
|
||||
public function setCharset(string $charset)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
|
||||
|
|
@ -511,11 +503,9 @@ class Response
|
|||
/**
|
||||
* Retrieves the response charset.
|
||||
*
|
||||
* @return string Character set
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getCharset()
|
||||
public function getCharset(): ?string
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
|
@ -535,13 +525,11 @@ class Response
|
|||
* can be reused by a cache with heuristic expiration unless otherwise indicated"
|
||||
* (https://tools.ietf.org/html/rfc7231#section-6.1)
|
||||
*
|
||||
* @return bool true if the response is worth caching, false otherwise
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function isCacheable()
|
||||
public function isCacheable(): bool
|
||||
{
|
||||
if (!\in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
|
||||
if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -559,11 +547,9 @@ class Response
|
|||
* origin. A response is considered fresh when it includes a Cache-Control/max-age
|
||||
* indicator or Expires header and the calculated age is less than the freshness lifetime.
|
||||
*
|
||||
* @return bool true if the response is fresh, false otherwise
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function isFresh()
|
||||
public function isFresh(): bool
|
||||
{
|
||||
return $this->getTtl() > 0;
|
||||
}
|
||||
|
|
@ -572,11 +558,9 @@ class Response
|
|||
* Returns true if the response includes headers that can be used to validate
|
||||
* the response with the origin server using a conditional GET request.
|
||||
*
|
||||
* @return bool true if the response is validateable, false otherwise
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function isValidateable()
|
||||
public function isValidateable(): bool
|
||||
{
|
||||
return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
|
||||
}
|
||||
|
|
@ -588,7 +572,7 @@ class Response
|
|||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setPrivate()
|
||||
{
|
||||
|
|
@ -605,7 +589,7 @@ class Response
|
|||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setPublic()
|
||||
{
|
||||
|
|
@ -618,13 +602,11 @@ class Response
|
|||
/**
|
||||
* Marks the response as "immutable".
|
||||
*
|
||||
* @param bool $immutable enables or disables the immutable directive
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function setImmutable($immutable = true)
|
||||
public function setImmutable(bool $immutable = true)
|
||||
{
|
||||
if ($immutable) {
|
||||
$this->headers->addCacheControlDirective('immutable');
|
||||
|
|
@ -638,11 +620,9 @@ class Response
|
|||
/**
|
||||
* Returns true if the response is marked as "immutable".
|
||||
*
|
||||
* @return bool returns true if the response is marked as "immutable"; otherwise false
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
public function isImmutable()
|
||||
public function isImmutable(): bool
|
||||
{
|
||||
return $this->headers->hasCacheControlDirective('immutable');
|
||||
}
|
||||
|
|
@ -655,11 +635,9 @@ class Response
|
|||
* When present, the TTL of the response should not be overridden to be
|
||||
* greater than the value provided by the origin.
|
||||
*
|
||||
* @return bool true if the response must be revalidated by a cache, false otherwise
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function mustRevalidate()
|
||||
public function mustRevalidate(): bool
|
||||
{
|
||||
return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate');
|
||||
}
|
||||
|
|
@ -667,13 +645,11 @@ class Response
|
|||
/**
|
||||
* Returns the Date header as a DateTime instance.
|
||||
*
|
||||
* @return \DateTime A \DateTime instance
|
||||
*
|
||||
* @throws \RuntimeException When the header is not parseable
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getDate()
|
||||
public function getDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->headers->getDate('Date');
|
||||
}
|
||||
|
|
@ -683,30 +659,32 @@ class Response
|
|||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setDate(\DateTime $date)
|
||||
public function setDate(\DateTimeInterface $date)
|
||||
{
|
||||
$date->setTimezone(new \DateTimeZone('UTC'));
|
||||
if ($date instanceof \DateTime) {
|
||||
$date = \DateTimeImmutable::createFromMutable($date);
|
||||
}
|
||||
|
||||
$date = $date->setTimezone(new \DateTimeZone('UTC'));
|
||||
$this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the age of the response.
|
||||
* Returns the age of the response in seconds.
|
||||
*
|
||||
* @return int The age of the response in seconds
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getAge()
|
||||
public function getAge(): int
|
||||
{
|
||||
if (null !== $age = $this->headers->get('Age')) {
|
||||
return (int) $age;
|
||||
}
|
||||
|
||||
return max(time() - $this->getDate()->format('U'), 0);
|
||||
return max(time() - (int) $this->getDate()->format('U'), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -718,6 +696,7 @@ class Response
|
|||
{
|
||||
if ($this->isFresh()) {
|
||||
$this->headers->set('Age', $this->getMaxAge());
|
||||
$this->headers->remove('Expires');
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
|
@ -726,17 +705,15 @@ class Response
|
|||
/**
|
||||
* Returns the value of the Expires header as a DateTime instance.
|
||||
*
|
||||
* @return \DateTime|null A DateTime instance or null if the header does not exist
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getExpires()
|
||||
public function getExpires(): ?\DateTimeInterface
|
||||
{
|
||||
try {
|
||||
return $this->headers->getDate('Expires');
|
||||
} catch (\RuntimeException $e) {
|
||||
// according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past
|
||||
return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
|
||||
return \DateTime::createFromFormat('U', time() - 172800);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -745,22 +722,25 @@ class Response
|
|||
*
|
||||
* Passing null as value will remove the header.
|
||||
*
|
||||
* @param \DateTime|null $date A \DateTime instance or null to remove the header
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setExpires(\DateTime $date = null)
|
||||
public function setExpires(\DateTimeInterface $date = null)
|
||||
{
|
||||
if (null === $date) {
|
||||
$this->headers->remove('Expires');
|
||||
} else {
|
||||
$date = clone $date;
|
||||
$date->setTimezone(new \DateTimeZone('UTC'));
|
||||
$this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($date instanceof \DateTime) {
|
||||
$date = \DateTimeImmutable::createFromMutable($date);
|
||||
}
|
||||
|
||||
$date = $date->setTimezone(new \DateTimeZone('UTC'));
|
||||
$this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
@ -771,11 +751,9 @@ class Response
|
|||
* First, it checks for a s-maxage directive, then a max-age directive, and then it falls
|
||||
* back on an expires header. It returns null when no maximum age can be established.
|
||||
*
|
||||
* @return int|null Number of seconds
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getMaxAge()
|
||||
public function getMaxAge(): ?int
|
||||
{
|
||||
if ($this->headers->hasCacheControlDirective('s-maxage')) {
|
||||
return (int) $this->headers->getCacheControlDirective('s-maxage');
|
||||
|
|
@ -786,8 +764,10 @@ class Response
|
|||
}
|
||||
|
||||
if (null !== $this->getExpires()) {
|
||||
return $this->getExpires()->format('U') - $this->getDate()->format('U');
|
||||
return (int) $this->getExpires()->format('U') - (int) $this->getDate()->format('U');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -795,13 +775,11 @@ class Response
|
|||
*
|
||||
* This methods sets the Cache-Control max-age directive.
|
||||
*
|
||||
* @param int $value Number of seconds
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setMaxAge($value)
|
||||
public function setMaxAge(int $value)
|
||||
{
|
||||
$this->headers->addCacheControlDirective('max-age', $value);
|
||||
|
||||
|
|
@ -813,13 +791,11 @@ class Response
|
|||
*
|
||||
* This methods sets the Cache-Control s-maxage directive.
|
||||
*
|
||||
* @param int $value Number of seconds
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setSharedMaxAge($value)
|
||||
public function setSharedMaxAge(int $value)
|
||||
{
|
||||
$this->setPublic();
|
||||
$this->headers->addCacheControlDirective('s-maxage', $value);
|
||||
|
|
@ -835,29 +811,25 @@ class Response
|
|||
* When the responses TTL is <= 0, the response may not be served from cache without first
|
||||
* revalidating with the origin.
|
||||
*
|
||||
* @return int|null The TTL in seconds
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getTtl()
|
||||
public function getTtl(): ?int
|
||||
{
|
||||
if (null !== $maxAge = $this->getMaxAge()) {
|
||||
return $maxAge - $this->getAge();
|
||||
}
|
||||
$maxAge = $this->getMaxAge();
|
||||
|
||||
return null !== $maxAge ? $maxAge - $this->getAge() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response's time-to-live for shared caches.
|
||||
* Sets the response's time-to-live for shared caches in seconds.
|
||||
*
|
||||
* This method adjusts the Cache-Control/s-maxage directive.
|
||||
*
|
||||
* @param int $seconds Number of seconds
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setTtl($seconds)
|
||||
public function setTtl(int $seconds)
|
||||
{
|
||||
$this->setSharedMaxAge($this->getAge() + $seconds);
|
||||
|
||||
|
|
@ -865,17 +837,15 @@ class Response
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the response's time-to-live for private/client caches.
|
||||
* Sets the response's time-to-live for private/client caches in seconds.
|
||||
*
|
||||
* This method adjusts the Cache-Control/max-age directive.
|
||||
*
|
||||
* @param int $seconds Number of seconds
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setClientTtl($seconds)
|
||||
public function setClientTtl(int $seconds)
|
||||
{
|
||||
$this->setMaxAge($this->getAge() + $seconds);
|
||||
|
||||
|
|
@ -885,13 +855,11 @@ class Response
|
|||
/**
|
||||
* Returns the Last-Modified HTTP header as a DateTime instance.
|
||||
*
|
||||
* @return \DateTime|null A DateTime instance or null if the header does not exist
|
||||
*
|
||||
* @throws \RuntimeException When the HTTP header is not parseable
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getLastModified()
|
||||
public function getLastModified(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->headers->getDate('Last-Modified');
|
||||
}
|
||||
|
|
@ -901,33 +869,34 @@ class Response
|
|||
*
|
||||
* Passing null as value will remove the header.
|
||||
*
|
||||
* @param \DateTime|null $date A \DateTime instance or null to remove the header
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setLastModified(\DateTime $date = null)
|
||||
public function setLastModified(\DateTimeInterface $date = null)
|
||||
{
|
||||
if (null === $date) {
|
||||
$this->headers->remove('Last-Modified');
|
||||
} else {
|
||||
$date = clone $date;
|
||||
$date->setTimezone(new \DateTimeZone('UTC'));
|
||||
$this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($date instanceof \DateTime) {
|
||||
$date = \DateTimeImmutable::createFromMutable($date);
|
||||
}
|
||||
|
||||
$date = $date->setTimezone(new \DateTimeZone('UTC'));
|
||||
$this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the literal value of the ETag HTTP header.
|
||||
*
|
||||
* @return string|null The ETag HTTP header or null if it does not exist
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getEtag()
|
||||
public function getEtag(): ?string
|
||||
{
|
||||
return $this->headers->get('ETag');
|
||||
}
|
||||
|
|
@ -940,9 +909,9 @@ class Response
|
|||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setEtag($etag = null, $weak = false)
|
||||
public function setEtag(string $etag = null, bool $weak = false)
|
||||
{
|
||||
if (null === $etag) {
|
||||
$this->headers->remove('Etag');
|
||||
|
|
@ -962,18 +931,16 @@ class Response
|
|||
*
|
||||
* Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable.
|
||||
*
|
||||
* @param array $options An array of cache options
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function setCache(array $options)
|
||||
{
|
||||
if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'))) {
|
||||
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff))));
|
||||
if ($diff = array_diff(array_keys($options), ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff)));
|
||||
}
|
||||
|
||||
if (isset($options['etag'])) {
|
||||
|
|
@ -1023,9 +990,9 @@ class Response
|
|||
*
|
||||
* @return $this
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc2616#section-10.3.5
|
||||
* @see https://tools.ietf.org/html/rfc2616#section-10.3.5
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function setNotModified()
|
||||
{
|
||||
|
|
@ -1033,7 +1000,7 @@ class Response
|
|||
$this->setContent(null);
|
||||
|
||||
// remove headers that MUST NOT be included with 304 Not Modified responses
|
||||
foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
|
||||
foreach (['Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified'] as $header) {
|
||||
$this->headers->remove($header);
|
||||
}
|
||||
|
||||
|
|
@ -1043,11 +1010,9 @@ class Response
|
|||
/**
|
||||
* Returns true if the response includes a Vary header.
|
||||
*
|
||||
* @return bool true if the response includes a Vary header, false otherwise
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function hasVary()
|
||||
public function hasVary(): bool
|
||||
{
|
||||
return null !== $this->headers->get('Vary');
|
||||
}
|
||||
|
|
@ -1055,17 +1020,15 @@ class Response
|
|||
/**
|
||||
* Returns an array of header names given in the Vary header.
|
||||
*
|
||||
* @return array An array of Vary names
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function getVary()
|
||||
public function getVary(): array
|
||||
{
|
||||
if (!$vary = $this->headers->get('Vary', null, false)) {
|
||||
return array();
|
||||
if (!$vary = $this->headers->all('Vary')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ret = array();
|
||||
$ret = [];
|
||||
foreach ($vary as $item) {
|
||||
$ret = array_merge($ret, preg_split('/[\s,]+/', $item));
|
||||
}
|
||||
|
|
@ -1081,9 +1044,9 @@ class Response
|
|||
*
|
||||
* @return $this
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function setVary($headers, $replace = true)
|
||||
public function setVary($headers, bool $replace = true)
|
||||
{
|
||||
$this->headers->set('Vary', $headers, $replace);
|
||||
|
||||
|
|
@ -1099,9 +1062,9 @@ class Response
|
|||
*
|
||||
* @return bool true if the Response validators match the Request, false otherwise
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function isNotModified(Request $request)
|
||||
public function isNotModified(Request $request): bool
|
||||
{
|
||||
if (!$request->isMethodCacheable()) {
|
||||
return false;
|
||||
|
|
@ -1129,13 +1092,11 @@ class Response
|
|||
/**
|
||||
* Is response invalid?
|
||||
*
|
||||
* @return bool
|
||||
* @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
*
|
||||
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isInvalid()
|
||||
public function isInvalid(): bool
|
||||
{
|
||||
return $this->statusCode < 100 || $this->statusCode >= 600;
|
||||
}
|
||||
|
|
@ -1143,11 +1104,9 @@ class Response
|
|||
/**
|
||||
* Is response informative?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function isInformational()
|
||||
public function isInformational(): bool
|
||||
{
|
||||
return $this->statusCode >= 100 && $this->statusCode < 200;
|
||||
}
|
||||
|
|
@ -1155,11 +1114,9 @@ class Response
|
|||
/**
|
||||
* Is response successful?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isSuccessful()
|
||||
public function isSuccessful(): bool
|
||||
{
|
||||
return $this->statusCode >= 200 && $this->statusCode < 300;
|
||||
}
|
||||
|
|
@ -1167,11 +1124,9 @@ class Response
|
|||
/**
|
||||
* Is the response a redirect?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isRedirection()
|
||||
public function isRedirection(): bool
|
||||
{
|
||||
return $this->statusCode >= 300 && $this->statusCode < 400;
|
||||
}
|
||||
|
|
@ -1179,11 +1134,9 @@ class Response
|
|||
/**
|
||||
* Is there a client error?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isClientError()
|
||||
public function isClientError(): bool
|
||||
{
|
||||
return $this->statusCode >= 400 && $this->statusCode < 500;
|
||||
}
|
||||
|
|
@ -1191,11 +1144,9 @@ class Response
|
|||
/**
|
||||
* Was there a server side error?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public function isServerError()
|
||||
public function isServerError(): bool
|
||||
{
|
||||
return $this->statusCode >= 500 && $this->statusCode < 600;
|
||||
}
|
||||
|
|
@ -1203,11 +1154,9 @@ class Response
|
|||
/**
|
||||
* Is the response OK?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isOk()
|
||||
public function isOk(): bool
|
||||
{
|
||||
return 200 === $this->statusCode;
|
||||
}
|
||||
|
|
@ -1215,11 +1164,9 @@ class Response
|
|||
/**
|
||||
* Is the response forbidden?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isForbidden()
|
||||
public function isForbidden(): bool
|
||||
{
|
||||
return 403 === $this->statusCode;
|
||||
}
|
||||
|
|
@ -1227,11 +1174,9 @@ class Response
|
|||
/**
|
||||
* Is the response a not found error?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isNotFound()
|
||||
public function isNotFound(): bool
|
||||
{
|
||||
return 404 === $this->statusCode;
|
||||
}
|
||||
|
|
@ -1239,27 +1184,21 @@ class Response
|
|||
/**
|
||||
* Is the response a redirect of some form?
|
||||
*
|
||||
* @param string $location
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isRedirect($location = null)
|
||||
public function isRedirect(string $location = null): bool
|
||||
{
|
||||
return \in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
|
||||
return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the response empty?
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @final since version 3.2
|
||||
* @final
|
||||
*/
|
||||
public function isEmpty()
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return \in_array($this->statusCode, array(204, 304));
|
||||
return \in_array($this->statusCode, [204, 304]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1267,17 +1206,13 @@ class Response
|
|||
*
|
||||
* Resulting level can be greater than target level if a non-removable buffer has been encountered.
|
||||
*
|
||||
* @param int $targetLevel The target output buffering level
|
||||
* @param bool $flush Whether to flush or clean the buffers
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @final
|
||||
*/
|
||||
public static function closeOutputBuffers($targetLevel, $flush)
|
||||
public static function closeOutputBuffers(int $targetLevel, bool $flush): void
|
||||
{
|
||||
$status = ob_get_status(true);
|
||||
$level = \count($status);
|
||||
// PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3
|
||||
$flags = \defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1;
|
||||
$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) {
|
||||
|
|
@ -1293,9 +1228,9 @@ class Response
|
|||
*
|
||||
* @see http://support.microsoft.com/kb/323308
|
||||
*
|
||||
* @final since version 3.3
|
||||
* @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 ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) {
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ class ResponseHeaderBag extends HeaderBag
|
|||
const DISPOSITION_ATTACHMENT = 'attachment';
|
||||
const DISPOSITION_INLINE = 'inline';
|
||||
|
||||
protected $computedCacheControl = array();
|
||||
protected $cookies = array();
|
||||
protected $headerNames = array();
|
||||
protected $computedCacheControl = [];
|
||||
protected $cookies = [];
|
||||
protected $headerNames = [];
|
||||
|
||||
public function __construct(array $headers = array())
|
||||
public function __construct(array $headers = [])
|
||||
{
|
||||
parent::__construct($headers);
|
||||
|
||||
|
|
@ -49,9 +49,9 @@ class ResponseHeaderBag extends HeaderBag
|
|||
*/
|
||||
public function allPreserveCase()
|
||||
{
|
||||
$headers = array();
|
||||
$headers = [];
|
||||
foreach ($this->all() as $name => $value) {
|
||||
$headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value;
|
||||
$headers[$this->headerNames[$name] ?? $name] = $value;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
|
|
@ -70,9 +70,9 @@ class ResponseHeaderBag extends HeaderBag
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function replace(array $headers = array())
|
||||
public function replace(array $headers = [])
|
||||
{
|
||||
$this->headerNames = array();
|
||||
$this->headerNames = [];
|
||||
|
||||
parent::replace($headers);
|
||||
|
||||
|
|
@ -87,10 +87,19 @@ class ResponseHeaderBag extends HeaderBag
|
|||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string|null $key The name of the headers to return or null to get them all
|
||||
*/
|
||||
public function all()
|
||||
public function all(/*string $key = null*/)
|
||||
{
|
||||
$headers = parent::all();
|
||||
|
||||
if (1 <= \func_num_args() && null !== $key = func_get_arg(0)) {
|
||||
$key = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
return 'set-cookie' !== $key ? $headers[$key] ?? [] : array_map('strval', $this->getCookies());
|
||||
}
|
||||
|
||||
foreach ($this->getCookies() as $cookie) {
|
||||
$headers['set-cookie'][] = (string) $cookie;
|
||||
}
|
||||
|
|
@ -103,11 +112,11 @@ class ResponseHeaderBag extends HeaderBag
|
|||
*/
|
||||
public function set($key, $values, $replace = true)
|
||||
{
|
||||
$uniqueKey = str_replace('_', '-', strtolower($key));
|
||||
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
|
||||
|
||||
if ('set-cookie' === $uniqueKey) {
|
||||
if ($replace) {
|
||||
$this->cookies = array();
|
||||
$this->cookies = [];
|
||||
}
|
||||
foreach ((array) $values as $cookie) {
|
||||
$this->setCookie(Cookie::fromString($cookie));
|
||||
|
|
@ -122,9 +131,8 @@ class ResponseHeaderBag extends HeaderBag
|
|||
parent::set($key, $values, $replace);
|
||||
|
||||
// ensure the cache-control header has sensible defaults
|
||||
if (\in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'), true)) {
|
||||
$computed = $this->computeCacheControlValue();
|
||||
$this->headers['cache-control'] = array($computed);
|
||||
if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true) && '' !== $computed = $this->computeCacheControlValue()) {
|
||||
$this->headers['cache-control'] = [$computed];
|
||||
$this->headerNames['cache-control'] = 'Cache-Control';
|
||||
$this->computedCacheControl = $this->parseCacheControl($computed);
|
||||
}
|
||||
|
|
@ -135,11 +143,11 @@ class ResponseHeaderBag extends HeaderBag
|
|||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
$uniqueKey = str_replace('_', '-', strtolower($key));
|
||||
$uniqueKey = strtr($key, self::UPPER, self::LOWER);
|
||||
unset($this->headerNames[$uniqueKey]);
|
||||
|
||||
if ('set-cookie' === $uniqueKey) {
|
||||
$this->cookies = array();
|
||||
$this->cookies = [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -147,7 +155,7 @@ class ResponseHeaderBag extends HeaderBag
|
|||
parent::remove($key);
|
||||
|
||||
if ('cache-control' === $uniqueKey) {
|
||||
$this->computedCacheControl = array();
|
||||
$this->computedCacheControl = [];
|
||||
}
|
||||
|
||||
if ('date' === $uniqueKey) {
|
||||
|
|
@ -160,7 +168,7 @@ class ResponseHeaderBag extends HeaderBag
|
|||
*/
|
||||
public function hasCacheControlDirective($key)
|
||||
{
|
||||
return array_key_exists($key, $this->computedCacheControl);
|
||||
return \array_key_exists($key, $this->computedCacheControl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -168,7 +176,7 @@ class ResponseHeaderBag extends HeaderBag
|
|||
*/
|
||||
public function getCacheControlDirective($key)
|
||||
{
|
||||
return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
|
||||
return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
|
||||
}
|
||||
|
||||
public function setCookie(Cookie $cookie)
|
||||
|
|
@ -216,15 +224,15 @@ class ResponseHeaderBag extends HeaderBag
|
|||
*/
|
||||
public function getCookies($format = self::COOKIES_FLAT)
|
||||
{
|
||||
if (!\in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) {
|
||||
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY))));
|
||||
if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) {
|
||||
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY])));
|
||||
}
|
||||
|
||||
if (self::COOKIES_ARRAY === $format) {
|
||||
return $this->cookies;
|
||||
}
|
||||
|
||||
$flattenedCookies = array();
|
||||
$flattenedCookies = [];
|
||||
foreach ($this->cookies as $path) {
|
||||
foreach ($path as $cookies) {
|
||||
foreach ($cookies as $cookie) {
|
||||
|
|
@ -247,56 +255,15 @@ class ResponseHeaderBag extends HeaderBag
|
|||
*/
|
||||
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
|
||||
{
|
||||
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
|
||||
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a HTTP Content-Disposition field-value.
|
||||
*
|
||||
* @param string $disposition One of "inline" or "attachment"
|
||||
* @param string $filename A unicode string
|
||||
* @param string $filenameFallback A string containing only ASCII characters that
|
||||
* is semantically equivalent to $filename. If the filename is already ASCII,
|
||||
* it can be omitted, or just copied from $filename
|
||||
*
|
||||
* @return string A string suitable for use as a Content-Disposition field-value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @see RFC 6266
|
||||
* @see HeaderUtils::makeDisposition()
|
||||
*/
|
||||
public function makeDisposition($disposition, $filename, $filenameFallback = '')
|
||||
{
|
||||
if (!\in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
|
||||
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
|
||||
}
|
||||
|
||||
if ('' == $filenameFallback) {
|
||||
$filenameFallback = $filename;
|
||||
}
|
||||
|
||||
// filenameFallback is not ASCII.
|
||||
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
|
||||
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
|
||||
}
|
||||
|
||||
// percent characters aren't safe in fallback.
|
||||
if (false !== strpos($filenameFallback, '%')) {
|
||||
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
|
||||
}
|
||||
|
||||
// path separators aren't allowed in either.
|
||||
if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
|
||||
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
|
||||
}
|
||||
|
||||
$output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback));
|
||||
|
||||
if ($filename !== $filenameFallback) {
|
||||
$output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename));
|
||||
}
|
||||
|
||||
return $output;
|
||||
return HeaderUtils::makeDisposition((string) $disposition, (string) $filename, (string) $filenameFallback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -309,13 +276,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();
|
||||
|
|
@ -331,7 +298,7 @@ class ResponseHeaderBag extends HeaderBag
|
|||
return $header;
|
||||
}
|
||||
|
||||
private function initDate()
|
||||
private function initDate(): void
|
||||
{
|
||||
$now = \DateTime::createFromFormat('U', time());
|
||||
$now->setTimezone(new \DateTimeZone('UTC'));
|
||||
|
|
|
|||
|
|
@ -27,14 +27,11 @@ class ServerBag extends ParameterBag
|
|||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
$headers = array();
|
||||
$contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
|
||||
$headers = [];
|
||||
foreach ($this->parameters as $key => $value) {
|
||||
if (0 === strpos($key, 'HTTP_')) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
|
@ -46,13 +43,13 @@ class ServerBag extends ParameterBag
|
|||
/*
|
||||
* php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
|
||||
* For this workaround to work, add these lines to your .htaccess file:
|
||||
* RewriteCond %{HTTP:Authorization} ^(.+)$
|
||||
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
* RewriteCond %{HTTP:Authorization} .+
|
||||
* RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
|
||||
*
|
||||
* A sample .htaccess file:
|
||||
* RewriteEngine On
|
||||
* RewriteCond %{HTTP:Authorization} ^(.+)$
|
||||
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
* RewriteCond %{HTTP:Authorization} .+
|
||||
* RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
|
||||
* RewriteCond %{REQUEST_FILENAME} !-f
|
||||
* RewriteRule ^(.*)$ app.php [QSA,L]
|
||||
*/
|
||||
|
|
@ -79,7 +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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
|||
private $name = 'attributes';
|
||||
private $storageKey;
|
||||
|
||||
protected $attributes = array();
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* @param string $storageKey The key used to store attributes in the session
|
||||
*/
|
||||
public function __construct($storageKey = '_sf2_attributes')
|
||||
public function __construct(string $storageKey = '_sf2_attributes')
|
||||
{
|
||||
$this->storageKey = $storageKey;
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
|||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return array_key_exists($name, $this->attributes);
|
||||
return \array_key_exists($name, $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -71,7 +71,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
|||
*/
|
||||
public function get($name, $default = null)
|
||||
{
|
||||
return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
|
||||
return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -95,7 +95,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
|||
*/
|
||||
public function replace(array $attributes)
|
||||
{
|
||||
$this->attributes = array();
|
||||
$this->attributes = [];
|
||||
foreach ($attributes as $key => $value) {
|
||||
$this->set($key, $value);
|
||||
}
|
||||
|
|
@ -107,7 +107,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
|||
public function remove($name)
|
||||
{
|
||||
$retval = null;
|
||||
if (array_key_exists($name, $this->attributes)) {
|
||||
if (\array_key_exists($name, $this->attributes)) {
|
||||
$retval = $this->attributes[$name];
|
||||
unset($this->attributes[$name]);
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Counta
|
|||
public function clear()
|
||||
{
|
||||
$return = $this->attributes;
|
||||
$this->attributes = array();
|
||||
$this->attributes = [];
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,15 +50,10 @@ interface AttributeBagInterface extends SessionBagInterface
|
|||
/**
|
||||
* Returns attributes.
|
||||
*
|
||||
* @return array Attributes
|
||||
* @return array
|
||||
*/
|
||||
public function all();
|
||||
|
||||
/**
|
||||
* Sets attributes.
|
||||
*
|
||||
* @param array $attributes Attributes
|
||||
*/
|
||||
public function replace(array $attributes);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class NamespacedAttributeBag extends AttributeBag
|
|||
* @param string $storageKey Session storage key
|
||||
* @param string $namespaceCharacter Namespace character to use in keys
|
||||
*/
|
||||
public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/')
|
||||
public function __construct(string $storageKey = '_sf2_attributes', string $namespaceCharacter = '/')
|
||||
{
|
||||
$this->namespaceCharacter = $namespaceCharacter;
|
||||
parent::__construct($storageKey);
|
||||
|
|
@ -44,7 +44,7 @@ class NamespacedAttributeBag extends AttributeBag
|
|||
return false;
|
||||
}
|
||||
|
||||
return array_key_exists($name, $attributes);
|
||||
return \array_key_exists($name, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -60,7 +60,7 @@ class NamespacedAttributeBag extends AttributeBag
|
|||
return $default;
|
||||
}
|
||||
|
||||
return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
|
||||
return \array_key_exists($name, $attributes) ? $attributes[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -81,7 +81,7 @@ class NamespacedAttributeBag extends AttributeBag
|
|||
$retval = null;
|
||||
$attributes = &$this->resolveAttributePath($name);
|
||||
$name = $this->resolveKey($name);
|
||||
if (null !== $attributes && array_key_exists($name, $attributes)) {
|
||||
if (null !== $attributes && \array_key_exists($name, $attributes)) {
|
||||
$retval = $attributes[$name];
|
||||
unset($attributes[$name]);
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ class NamespacedAttributeBag extends AttributeBag
|
|||
* @param string $name Key name
|
||||
* @param bool $writeContext Write context, default false
|
||||
*
|
||||
* @return array
|
||||
* @return array|null
|
||||
*/
|
||||
protected function &resolveAttributePath($name, $writeContext = false)
|
||||
{
|
||||
|
|
@ -115,7 +115,7 @@ class NamespacedAttributeBag extends AttributeBag
|
|||
return $array;
|
||||
}
|
||||
|
||||
$array[$parts[0]] = array();
|
||||
$array[$parts[0]] = [];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
|
@ -123,14 +123,14 @@ class NamespacedAttributeBag extends AttributeBag
|
|||
unset($parts[\count($parts) - 1]);
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (null !== $array && !array_key_exists($part, $array)) {
|
||||
if (null !== $array && !\array_key_exists($part, $array)) {
|
||||
if (!$writeContext) {
|
||||
$null = null;
|
||||
|
||||
return $null;
|
||||
}
|
||||
|
||||
$array[$part] = array();
|
||||
$array[$part] = [];
|
||||
}
|
||||
|
||||
$array = &$array[$part];
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ namespace Symfony\Component\HttpFoundation\Session\Flash;
|
|||
class AutoExpireFlashBag implements FlashBagInterface
|
||||
{
|
||||
private $name = 'flashes';
|
||||
private $flashes = array('display' => array(), 'new' => array());
|
||||
private $flashes = ['display' => [], 'new' => []];
|
||||
private $storageKey;
|
||||
|
||||
/**
|
||||
* @param string $storageKey The key used to store flashes in the session
|
||||
*/
|
||||
public function __construct($storageKey = '_symfony_flashes')
|
||||
public function __construct(string $storageKey = '_symfony_flashes')
|
||||
{
|
||||
$this->storageKey = $storageKey;
|
||||
}
|
||||
|
|
@ -53,8 +53,8 @@ class AutoExpireFlashBag implements FlashBagInterface
|
|||
// The logic: messages from the last request will be stored in new, so we move them to previous
|
||||
// This request we will show what is in 'display'. What is placed into 'new' this time round will
|
||||
// be moved to display next time round.
|
||||
$this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array();
|
||||
$this->flashes['new'] = array();
|
||||
$this->flashes['display'] = \array_key_exists('new', $this->flashes) ? $this->flashes['new'] : [];
|
||||
$this->flashes['new'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,7 +68,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function peek($type, array $default = array())
|
||||
public function peek($type, array $default = [])
|
||||
{
|
||||
return $this->has($type) ? $this->flashes['display'][$type] : $default;
|
||||
}
|
||||
|
|
@ -78,13 +78,13 @@ class AutoExpireFlashBag implements FlashBagInterface
|
|||
*/
|
||||
public function peekAll()
|
||||
{
|
||||
return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array();
|
||||
return \array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($type, array $default = array())
|
||||
public function get($type, array $default = [])
|
||||
{
|
||||
$return = $default;
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
|||
public function all()
|
||||
{
|
||||
$return = $this->flashes['display'];
|
||||
$this->flashes['display'] = array();
|
||||
$this->flashes['display'] = [];
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ class AutoExpireFlashBag implements FlashBagInterface
|
|||
*/
|
||||
public function has($type)
|
||||
{
|
||||
return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
|
||||
return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ namespace Symfony\Component\HttpFoundation\Session\Flash;
|
|||
class FlashBag implements FlashBagInterface
|
||||
{
|
||||
private $name = 'flashes';
|
||||
private $flashes = array();
|
||||
private $flashes = [];
|
||||
private $storageKey;
|
||||
|
||||
/**
|
||||
* @param string $storageKey The key used to store flashes in the session
|
||||
*/
|
||||
public function __construct($storageKey = '_symfony_flashes')
|
||||
public function __construct(string $storageKey = '_symfony_flashes')
|
||||
{
|
||||
$this->storageKey = $storageKey;
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ class FlashBag implements FlashBagInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function peek($type, array $default = array())
|
||||
public function peek($type, array $default = [])
|
||||
{
|
||||
return $this->has($type) ? $this->flashes[$type] : $default;
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ class FlashBag implements FlashBagInterface
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($type, array $default = array())
|
||||
public function get($type, array $default = [])
|
||||
{
|
||||
if (!$this->has($type)) {
|
||||
return $default;
|
||||
|
|
@ -97,7 +97,7 @@ class FlashBag implements FlashBagInterface
|
|||
public function all()
|
||||
{
|
||||
$return = $this->peekAll();
|
||||
$this->flashes = array();
|
||||
$this->flashes = [];
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
|
@ -123,7 +123,7 @@ class FlashBag implements FlashBagInterface
|
|||
*/
|
||||
public function has($type)
|
||||
{
|
||||
return array_key_exists($type, $this->flashes) && $this->flashes[$type];
|
||||
return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
|
|||
interface FlashBagInterface extends SessionBagInterface
|
||||
{
|
||||
/**
|
||||
* Adds a flash message for type.
|
||||
* Adds a flash message for the given type.
|
||||
*
|
||||
* @param string $type
|
||||
* @param mixed $message
|
||||
|
|
@ -29,12 +29,12 @@ interface FlashBagInterface extends SessionBagInterface
|
|||
public function add($type, $message);
|
||||
|
||||
/**
|
||||
* Registers a message for a given type.
|
||||
* Registers one or more messages for a given type.
|
||||
*
|
||||
* @param string $type
|
||||
* @param string|array $message
|
||||
* @param string|array $messages
|
||||
*/
|
||||
public function set($type, $message);
|
||||
public function set($type, $messages);
|
||||
|
||||
/**
|
||||
* Gets flash messages for a given type.
|
||||
|
|
@ -44,7 +44,7 @@ interface FlashBagInterface extends SessionBagInterface
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function peek($type, array $default = array());
|
||||
public function peek($type, array $default = []);
|
||||
|
||||
/**
|
||||
* Gets all flash messages.
|
||||
|
|
@ -61,7 +61,7 @@ interface FlashBagInterface extends SessionBagInterface
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get($type, array $default = array());
|
||||
public function get($type, array $default = []);
|
||||
|
||||
/**
|
||||
* Gets and clears flashes from the stack.
|
||||
|
|
|
|||
|
|
@ -28,14 +28,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
|||
|
||||
private $flashName;
|
||||
private $attributeName;
|
||||
private $data = array();
|
||||
private $data = [];
|
||||
private $usageIndex = 0;
|
||||
|
||||
/**
|
||||
* @param SessionStorageInterface $storage A SessionStorageInterface instance
|
||||
* @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag)
|
||||
* @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag)
|
||||
*/
|
||||
public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
|
||||
{
|
||||
$this->storage = $storage ?: new NativeSessionStorage();
|
||||
|
|
@ -134,29 +129,22 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
|||
/**
|
||||
* Returns the number of attributes.
|
||||
*
|
||||
* @return int The number of attributes
|
||||
* @return int
|
||||
*/
|
||||
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;
|
||||
|
|
@ -209,7 +197,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->storage->setId($id);
|
||||
if ($this->storage->getId() !== $id) {
|
||||
$this->storage->setId($id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -251,7 +241,9 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
|
|||
*/
|
||||
public function getBag($name)
|
||||
{
|
||||
return $this->storage->getBag($name)->getBag();
|
||||
$bag = $this->storage->getBag($name);
|
||||
|
||||
return method_exists($bag, 'getBag') ? $bag->getBag() : $bag;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -268,10 +260,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ interface SessionInterface
|
|||
/**
|
||||
* Starts the session storage.
|
||||
*
|
||||
* @return bool True if session started
|
||||
* @return bool
|
||||
*
|
||||
* @throws \RuntimeException if session fails to start
|
||||
*/
|
||||
|
|
@ -32,7 +32,7 @@ interface SessionInterface
|
|||
/**
|
||||
* Returns the session ID.
|
||||
*
|
||||
* @return string The session ID
|
||||
* @return string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ interface SessionInterface
|
|||
/**
|
||||
* Returns the session name.
|
||||
*
|
||||
* @return mixed The session name
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ interface SessionInterface
|
|||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
*
|
||||
* @return bool True if session invalidated, false if error
|
||||
* @return bool
|
||||
*/
|
||||
public function invalidate($lifetime = null);
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ interface SessionInterface
|
|||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
*
|
||||
* @return bool True if session migrated, false if error
|
||||
* @return bool
|
||||
*/
|
||||
public function migrate($destroy = false, $lifetime = null);
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ interface SessionInterface
|
|||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return bool true if the attribute is defined, false otherwise
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name);
|
||||
|
||||
|
|
@ -125,14 +125,12 @@ interface SessionInterface
|
|||
/**
|
||||
* Returns attributes.
|
||||
*
|
||||
* @return array Attributes
|
||||
* @return array
|
||||
*/
|
||||
public function all();
|
||||
|
||||
/**
|
||||
* Sets attributes.
|
||||
*
|
||||
* @param array $attributes Attributes
|
||||
*/
|
||||
public function replace(array $attributes);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
@trigger_error(sprintf('The class %s is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.', MemcacheSessionHandler::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @author Drak <drak@zikula.org>
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.
|
||||
*/
|
||||
class MemcacheSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
private $memcache;
|
||||
|
||||
/**
|
||||
* @var int Time to live in seconds
|
||||
*/
|
||||
private $ttl;
|
||||
|
||||
/**
|
||||
* @var string Key prefix for shared environments
|
||||
*/
|
||||
private $prefix;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* List of available options:
|
||||
* * prefix: The prefix to use for the memcache keys in order to avoid collision
|
||||
* * expiretime: The time to live in seconds
|
||||
*
|
||||
* @param \Memcache $memcache A \Memcache instance
|
||||
* @param array $options An associative array of Memcache options
|
||||
*
|
||||
* @throws \InvalidArgumentException When unsupported options are passed
|
||||
*/
|
||||
public function __construct(\Memcache $memcache, array $options = array())
|
||||
{
|
||||
if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'The following options are not supported "%s"', implode(', ', $diff)
|
||||
));
|
||||
}
|
||||
|
||||
$this->memcache = $memcache;
|
||||
$this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
|
||||
$this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
return $this->memcache->get($this->prefix.$sessionId) ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
return $this->memcache->set($this->prefix.$sessionId, $data, 0, time() + $this->ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
$this->memcache->delete($this->prefix.$sessionId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
// not required here because memcache will auto expire the records anyhow.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Memcache instance.
|
||||
*
|
||||
* @return \Memcache
|
||||
*/
|
||||
protected function getMemcache()
|
||||
{
|
||||
return $this->memcache;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
|||
* Memcached based session storage handler based on the Memcached class
|
||||
* provided by the PHP memcached extension.
|
||||
*
|
||||
* @see http://php.net/memcached
|
||||
* @see https://php.net/memcached
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
|
|
@ -40,19 +40,14 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
|||
* * prefix: The prefix to use for the memcached keys in order to avoid collision
|
||||
* * expiretime: The time to live in seconds.
|
||||
*
|
||||
* @param \Memcached $memcached A \Memcached instance
|
||||
* @param array $options An associative array of Memcached options
|
||||
*
|
||||
* @throws \InvalidArgumentException When unsupported options are passed
|
||||
*/
|
||||
public function __construct(\Memcached $memcached, array $options = array())
|
||||
public function __construct(\Memcached $memcached, array $options = [])
|
||||
{
|
||||
$this->memcached = $memcached;
|
||||
|
||||
if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'The following options are not supported "%s"', implode(', ', $diff)
|
||||
));
|
||||
if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
|
||||
}
|
||||
|
||||
$this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
|
||||
|
|
@ -60,11 +55,11 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
return $this->memcached->quit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,7 +71,7 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
|
|
@ -104,7 +99,7 @@ class MemcachedSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
|||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||
*
|
||||
* @see https://packagist.org/packages/mongodb/mongodb
|
||||
* @see http://php.net/manual/en/set.mongodb.php
|
||||
* @see https://php.net/mongodb
|
||||
*/
|
||||
class MongoDbSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
private $mongo;
|
||||
|
||||
/**
|
||||
* @var \MongoCollection
|
||||
* @var \MongoDB\Collection
|
||||
*/
|
||||
private $collection;
|
||||
|
||||
|
|
@ -56,43 +56,31 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
|||
* { "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 MongoClient or Mongo instance not provided
|
||||
* @throws \InvalidArgumentException When "database" or "collection" not provided
|
||||
*/
|
||||
public function __construct($mongo, array $options)
|
||||
public function __construct(\MongoDB\Client $mongo, array $options)
|
||||
{
|
||||
if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) {
|
||||
@trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
|
||||
throw new \InvalidArgumentException('MongoClient or Mongo instance required');
|
||||
}
|
||||
|
||||
if (!isset($options['database']) || !isset($options['collection'])) {
|
||||
throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler');
|
||||
}
|
||||
|
||||
$this->mongo = $mongo;
|
||||
|
||||
$this->options = array_merge(array(
|
||||
$this->options = array_merge([
|
||||
'id_field' => '_id',
|
||||
'data_field' => 'data',
|
||||
'time_field' => 'time',
|
||||
'expiry_field' => 'expires_at',
|
||||
), $options);
|
||||
], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
|
|
@ -104,25 +92,21 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
|||
*/
|
||||
protected function doDestroy($sessionId)
|
||||
{
|
||||
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
|
||||
|
||||
$this->getCollection()->$methodName(array(
|
||||
$this->getCollection()->deleteOne([
|
||||
$this->options['id_field'] => $sessionId,
|
||||
));
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove';
|
||||
|
||||
$this->getCollection()->$methodName(array(
|
||||
$this->options['expiry_field'] => array('$lt' => $this->createDateTime()),
|
||||
));
|
||||
$this->getCollection()->deleteMany([
|
||||
$this->options['expiry_field'] => ['$lt' => new \MongoDB\BSON\UTCDateTime()],
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -132,55 +116,36 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
|||
*/
|
||||
protected function doWrite($sessionId, $data)
|
||||
{
|
||||
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
|
||||
$fields = array(
|
||||
$this->options['time_field'] => $this->createDateTime(),
|
||||
$fields = [
|
||||
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
);
|
||||
$this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY),
|
||||
];
|
||||
|
||||
$options = array('upsert' => true);
|
||||
|
||||
if ($this->mongo instanceof \MongoDB\Client) {
|
||||
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
|
||||
} else {
|
||||
$fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY);
|
||||
$options['multiple'] = false;
|
||||
}
|
||||
|
||||
$methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update';
|
||||
|
||||
$this->getCollection()->$methodName(
|
||||
array($this->options['id_field'] => $sessionId),
|
||||
array('$set' => $fields),
|
||||
$options
|
||||
$this->getCollection()->updateOne(
|
||||
[$this->options['id_field'] => $sessionId],
|
||||
['$set' => $fields],
|
||||
['upsert' => true]
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
|
||||
if ($this->mongo instanceof \MongoDB\Client) {
|
||||
$methodName = 'updateOne';
|
||||
$options = array();
|
||||
} else {
|
||||
$methodName = 'update';
|
||||
$options = array('multiple' => false);
|
||||
}
|
||||
|
||||
$this->getCollection()->$methodName(
|
||||
array($this->options['id_field'] => $sessionId),
|
||||
array('$set' => array(
|
||||
$this->options['time_field'] => $this->createDateTime(),
|
||||
$this->getCollection()->updateOne(
|
||||
[$this->options['id_field'] => $sessionId],
|
||||
['$set' => [
|
||||
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
)),
|
||||
$options
|
||||
]]
|
||||
);
|
||||
|
||||
return true;
|
||||
|
|
@ -191,28 +156,19 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
|||
*/
|
||||
protected function doRead($sessionId)
|
||||
{
|
||||
$dbData = $this->getCollection()->findOne(array(
|
||||
$dbData = $this->getCollection()->findOne([
|
||||
$this->options['id_field'] => $sessionId,
|
||||
$this->options['expiry_field'] => array('$gte' => $this->createDateTime()),
|
||||
));
|
||||
$this->options['expiry_field'] => ['$gte' => new \MongoDB\BSON\UTCDateTime()],
|
||||
]);
|
||||
|
||||
if (null === $dbData) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) {
|
||||
return $dbData[$this->options['data_field']]->getData();
|
||||
}
|
||||
|
||||
return $dbData[$this->options['data_field']]->bin;
|
||||
return $dbData[$this->options['data_field']]->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a "MongoCollection" instance.
|
||||
*
|
||||
* @return \MongoCollection
|
||||
*/
|
||||
private function getCollection()
|
||||
private function getCollection(): \MongoDB\Collection
|
||||
{
|
||||
if (null === $this->collection) {
|
||||
$this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
|
||||
|
|
@ -222,34 +178,10 @@ class MongoDbSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a Mongo instance.
|
||||
*
|
||||
* @return \Mongo|\MongoClient|\MongoDB\Client
|
||||
* @return \MongoDB\Client
|
||||
*/
|
||||
protected function getMongo()
|
||||
{
|
||||
return $this->mongo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a date object using the class appropriate for the current mongo connection.
|
||||
*
|
||||
* Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime
|
||||
*
|
||||
* @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now.
|
||||
*
|
||||
* @return \MongoDate|\MongoDB\BSON\UTCDateTime
|
||||
*/
|
||||
private function createDateTime($seconds = null)
|
||||
{
|
||||
if (null === $seconds) {
|
||||
$seconds = time();
|
||||
}
|
||||
|
||||
if ($this->mongo instanceof \MongoDB\Client) {
|
||||
return new \MongoDB\BSON\UTCDateTime($seconds * 1000);
|
||||
}
|
||||
|
||||
return new \MongoDate($seconds);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,19 +16,19 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
|||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class NativeFileSessionHandler extends NativeSessionHandler
|
||||
class NativeFileSessionHandler extends \SessionHandler
|
||||
{
|
||||
/**
|
||||
* @param string $savePath Path of directory to save session files
|
||||
* Default null will leave setting as defined by PHP.
|
||||
* '/path', 'N;/path', or 'N;octal-mode;/path
|
||||
*
|
||||
* @see http://php.net/session.configuration.php#ini.session.save-path for further details.
|
||||
* @see https://php.net/session.configuration#ini.session.save-path for further details.
|
||||
*
|
||||
* @throws \InvalidArgumentException On invalid $savePath
|
||||
* @throws \RuntimeException When failing to create the save directory
|
||||
*/
|
||||
public function __construct($savePath = null)
|
||||
public function __construct(string $savePath = null)
|
||||
{
|
||||
if (null === $savePath) {
|
||||
$savePath = ini_get('session.save_path');
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.4, to be removed in 4.0. Use \SessionHandler instead.
|
||||
* @see http://php.net/sessionhandler
|
||||
*/
|
||||
class NativeSessionHandler extends \SessionHandler
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
@trigger_error('The '.__NAMESPACE__.'\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.', E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
|||
class NullSessionHandler extends AbstractSessionHandler
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
|
|
@ -27,7 +27,7 @@ class NullSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
|
|
@ -43,7 +43,7 @@ class NullSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
|
|
@ -67,7 +67,7 @@ class NullSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -65,13 +65,15 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
*/
|
||||
const LOCK_TRANSACTIONAL = 2;
|
||||
|
||||
private const MAX_LIFETIME = 315576000;
|
||||
|
||||
/**
|
||||
* @var \PDO|null PDO instance or null when not connected yet
|
||||
*/
|
||||
private $pdo;
|
||||
|
||||
/**
|
||||
* @var string|null|false DSN string or null for session.save_path or false when lazy connection disabled
|
||||
* @var string|false|null DSN string or null for session.save_path or false when lazy connection disabled
|
||||
*/
|
||||
private $dsn = false;
|
||||
|
||||
|
|
@ -118,7 +120,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
/**
|
||||
* @var array Connection options when lazy-connect
|
||||
*/
|
||||
private $connectionOptions = array();
|
||||
private $connectionOptions = [];
|
||||
|
||||
/**
|
||||
* @var int The strategy for locking, see constants
|
||||
|
|
@ -130,7 +132,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
*
|
||||
* @var \PDOStatement[] An array of statements to release advisory locks
|
||||
*/
|
||||
private $unlockStatements = array();
|
||||
private $unlockStatements = [];
|
||||
|
||||
/**
|
||||
* @var bool True when the current session exists but expired according to session.gc_maxlifetime
|
||||
|
|
@ -161,15 +163,14 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
* * db_time_col: The column where to store the timestamp [default: sess_time]
|
||||
* * db_username: The username when lazy-connect [default: '']
|
||||
* * db_password: The password when lazy-connect [default: '']
|
||||
* * db_connection_options: An array of driver-specific connection options [default: array()]
|
||||
* * db_connection_options: An array of driver-specific connection options [default: []]
|
||||
* * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL]
|
||||
*
|
||||
* @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null
|
||||
* @param array $options An associative array of options
|
||||
*
|
||||
* @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
|
||||
*/
|
||||
public function __construct($pdoOrDsn = null, array $options = array())
|
||||
public function __construct($pdoOrDsn = null, array $options = [])
|
||||
{
|
||||
if ($pdoOrDsn instanceof \PDO) {
|
||||
if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
|
||||
|
|
@ -218,7 +219,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
// - trailing space removal
|
||||
// - case-insensitivity
|
||||
// - language processing like é == e
|
||||
$sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB";
|
||||
$sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_bin, ENGINE = InnoDB";
|
||||
break;
|
||||
case 'sqlite':
|
||||
$sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
|
||||
|
|
@ -238,6 +239,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
|
||||
try {
|
||||
$this->pdo->exec($sql);
|
||||
$this->pdo->exec("CREATE INDEX EXPIRY ON $this->table ($this->lifetimeCol)");
|
||||
} catch (\PDOException $e) {
|
||||
$this->rollback();
|
||||
|
||||
|
|
@ -258,7 +260,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
|
|
@ -272,7 +274,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
|
|
@ -286,7 +288,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
|
|
@ -365,18 +367,18 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
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,7 +391,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
|
|
@ -403,14 +405,21 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
$this->gcCalled = false;
|
||||
|
||||
// delete the session records that have expired
|
||||
if ('mysql' === $this->driver) {
|
||||
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time";
|
||||
} else {
|
||||
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time - $this->timeCol";
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time AND $this->lifetimeCol > :min";
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
// to be removed in 6.0
|
||||
if ('mysql' === $this->driver) {
|
||||
$legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol + $this->timeCol < :time";
|
||||
} else {
|
||||
$legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol < :time - $this->timeCol";
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare($legacySql);
|
||||
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
|
|
@ -423,10 +432,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 +443,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);
|
||||
|
|
@ -468,13 +471,13 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler');
|
||||
}
|
||||
|
||||
$driverAliasMap = array(
|
||||
$driverAliasMap = [
|
||||
'mssql' => 'sqlsrv',
|
||||
'mysql2' => 'mysql', // Amazon RDS, for some weird reason
|
||||
'postgres' => 'pgsql',
|
||||
'postgresql' => 'pgsql',
|
||||
'sqlite3' => 'sqlite',
|
||||
);
|
||||
];
|
||||
|
||||
$driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme'];
|
||||
|
||||
|
|
@ -538,10 +541,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 +562,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
/**
|
||||
* Helper method to commit a transaction.
|
||||
*/
|
||||
private function commit()
|
||||
private function commit(): void
|
||||
{
|
||||
if ($this->inTransaction) {
|
||||
try {
|
||||
|
|
@ -581,7 +584,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
|
||||
|
|
@ -623,7 +626,12 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
$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 +645,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.');
|
||||
}
|
||||
|
||||
if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
||||
if (!filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN) && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
||||
// In strict mode, session fixation is not possible: new sessions always start with a unique
|
||||
// random id, so that concurrency is not possible and this code path can be skipped.
|
||||
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
|
||||
|
|
@ -668,8 +676,6 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
/**
|
||||
* Executes an application-level lock on the database.
|
||||
*
|
||||
* @param string $sessionId Session ID
|
||||
*
|
||||
* @return \PDOStatement The statement that needs to be executed later to release the lock
|
||||
*
|
||||
* @throws \DomainException When an unsupported PDO driver is used
|
||||
|
|
@ -678,12 +684,12 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
* - for oci using DBMS_LOCK.REQUEST
|
||||
* - for sqlsrv using sp_getapplock with LockOwner = Session
|
||||
*/
|
||||
private function doAdvisoryLock($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)');
|
||||
|
|
@ -733,12 +739,8 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
* Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer.
|
||||
*
|
||||
* Keep in mind, PHP integers are signed.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function convertStringToInt($string)
|
||||
private function convertStringToInt(string $string): int
|
||||
{
|
||||
if (4 === \PHP_INT_SIZE) {
|
||||
return (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]);
|
||||
|
|
@ -753,15 +755,14 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
/**
|
||||
* Return a locking or nonlocking SQL query to read session information.
|
||||
*
|
||||
* @return string The SQL string
|
||||
*
|
||||
* @throws \DomainException When an unsupported PDO driver is used
|
||||
*/
|
||||
private function getSelectSql()
|
||||
private function getSelectSql(): string
|
||||
{
|
||||
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':
|
||||
|
|
@ -782,32 +783,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;
|
||||
|
|
@ -815,32 +810,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;
|
||||
|
|
@ -848,36 +837,30 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||
|
||||
/**
|
||||
* Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
|
||||
*
|
||||
* @param string $sessionId Session ID
|
||||
* @param string $data Encoded session data
|
||||
* @param int $maxlifetime session.gc_maxlifetime
|
||||
*
|
||||
* @return \PDOStatement|null The merge statement or null when not supported
|
||||
*/
|
||||
private function getMergeStatement($sessionId, $data, $maxlifetime)
|
||||
private function getMergeStatement(string $sessionId, string $data, int $maxlifetime): ?\PDOStatement
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -887,15 +870,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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
/**
|
||||
* Wraps another SessionHandlerInterface to only write the session when it has been modified.
|
||||
*
|
||||
* @author Adrien Brault <adrien.brault@gmail.com>
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.
|
||||
*/
|
||||
class WriteCheckSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
private $wrappedSessionHandler;
|
||||
|
||||
/**
|
||||
* @var array sessionId => session
|
||||
*/
|
||||
private $readSessions;
|
||||
|
||||
public function __construct(\SessionHandlerInterface $wrappedSessionHandler)
|
||||
{
|
||||
@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', self::class), E_USER_DEPRECATED);
|
||||
|
||||
$this->wrappedSessionHandler = $wrappedSessionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return $this->wrappedSessionHandler->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return $this->wrappedSessionHandler->destroy($sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return $this->wrappedSessionHandler->gc($maxlifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return $this->wrappedSessionHandler->open($savePath, $sessionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
$session = $this->wrappedSessionHandler->read($sessionId);
|
||||
|
||||
$this->readSessions[$sessionId] = $session;
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
if (isset($this->readSessions[$sessionId]) && $data === $this->readSessions[$sessionId]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->wrappedSessionHandler->write($sessionId, $data);
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ class MetadataBag implements SessionBagInterface
|
|||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0);
|
||||
protected $meta = [self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0];
|
||||
|
||||
/**
|
||||
* Unix timestamp.
|
||||
|
|
@ -57,7 +57,7 @@ class MetadataBag implements SessionBagInterface
|
|||
* @param string $storageKey The key used to store bag in the session
|
||||
* @param int $updateThreshold The time to wait between two UPDATED updates
|
||||
*/
|
||||
public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0)
|
||||
public function __construct(string $storageKey = '_sf2_meta', int $updateThreshold = 0)
|
||||
{
|
||||
$this->storageKey = $storageKey;
|
||||
$this->updateThreshold = $updateThreshold;
|
||||
|
|
@ -159,7 +159,7 @@ class MetadataBag implements SessionBagInterface
|
|||
$this->name = $name;
|
||||
}
|
||||
|
||||
private function stampCreated($lifetime = null)
|
||||
private function stampCreated(int $lifetime = null): void
|
||||
{
|
||||
$timeStamp = time();
|
||||
$this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
|||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $data = array();
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* @var MetadataBag
|
||||
|
|
@ -60,13 +60,9 @@ class MockArraySessionStorage implements SessionStorageInterface
|
|||
/**
|
||||
* @var array|SessionBagInterface[]
|
||||
*/
|
||||
protected $bags = array();
|
||||
protected $bags = [];
|
||||
|
||||
/**
|
||||
* @param string $name Session name
|
||||
* @param MetadataBag $metaBag MetadataBag instance
|
||||
*/
|
||||
public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null)
|
||||
public function __construct(string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->setMetadataBag($metaBag);
|
||||
|
|
@ -170,7 +166,7 @@ class MockArraySessionStorage implements SessionStorageInterface
|
|||
}
|
||||
|
||||
// clear out the session
|
||||
$this->data = array();
|
||||
$this->data = [];
|
||||
|
||||
// reconnect the bags to the session
|
||||
$this->loadSession();
|
||||
|
|
@ -242,11 +238,11 @@ class MockArraySessionStorage implements SessionStorageInterface
|
|||
|
||||
protected function loadSession()
|
||||
{
|
||||
$bags = array_merge($this->bags, array($this->metadataBag));
|
||||
$bags = array_merge($this->bags, [$this->metadataBag]);
|
||||
|
||||
foreach ($bags as $bag) {
|
||||
$key = $bag->getStorageKey();
|
||||
$this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array();
|
||||
$this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : [];
|
||||
$bag->initialize($this->data[$key]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,10 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
|||
private $savePath;
|
||||
|
||||
/**
|
||||
* @param string $savePath Path of directory to save session files
|
||||
* @param string $name Session name
|
||||
* @param MetadataBag $metaBag MetadataBag instance
|
||||
* @param string $savePath Path of directory to save session files
|
||||
* @param string $name Session name
|
||||
*/
|
||||
public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null)
|
||||
public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
|
||||
{
|
||||
if (null === $savePath) {
|
||||
$savePath = sys_get_temp_dir();
|
||||
|
|
@ -98,7 +97,7 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
|||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($data)) {
|
||||
if ([$key = $this->metadataBag->getStorageKey()] === array_keys($data)) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +121,7 @@ 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())) {
|
||||
unlink($this->getFilePath());
|
||||
|
|
@ -131,10 +130,8 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
|||
|
||||
/**
|
||||
* Calculate path to file.
|
||||
*
|
||||
* @return string File path
|
||||
*/
|
||||
private function getFilePath()
|
||||
private function getFilePath(): string
|
||||
{
|
||||
return $this->savePath.'/'.$this->id.'.mocksess';
|
||||
}
|
||||
|
|
@ -142,10 +139,10 @@ class MockFileSessionStorage extends MockArraySessionStorage
|
|||
/**
|
||||
* Reads session from storage and loads session.
|
||||
*/
|
||||
private function read()
|
||||
private function read(): void
|
||||
{
|
||||
$filePath = $this->getFilePath();
|
||||
$this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array();
|
||||
$this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : [];
|
||||
|
||||
$this->loadSession();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
namespace Symfony\Component\HttpFoundation\Session\Storage;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionUtils;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||
|
|
@ -26,7 +27,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
/**
|
||||
* @var SessionBagInterface[]
|
||||
*/
|
||||
protected $bags = array();
|
||||
protected $bags = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
|
@ -48,13 +49,18 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
*/
|
||||
protected $metadataBag;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $emulateSameSite;
|
||||
|
||||
/**
|
||||
* Depending on how you want the storage driver to behave you probably
|
||||
* want to override this constructor entirely.
|
||||
*
|
||||
* List of options for $options array with their defaults.
|
||||
*
|
||||
* @see http://php.net/session.configuration for options
|
||||
* @see https://php.net/session.configuration for options
|
||||
* but we omit 'session.' from the beginning of the keys for convenience.
|
||||
*
|
||||
* ("auto_start", is not supported as it tells PHP to start a session before
|
||||
|
|
@ -67,13 +73,10 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
* cookie_lifetime, "0"
|
||||
* cookie_path, "/"
|
||||
* cookie_secure, ""
|
||||
* entropy_file, ""
|
||||
* entropy_length, "0"
|
||||
* cookie_samesite, null
|
||||
* gc_divisor, "100"
|
||||
* gc_maxlifetime, "1440"
|
||||
* gc_probability, "1"
|
||||
* hash_bits_per_character, "4"
|
||||
* hash_function, "0"
|
||||
* lazy_write, "1"
|
||||
* name, "PHPSESSID"
|
||||
* referer_check, ""
|
||||
|
|
@ -94,18 +97,21 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
* trans_sid_hosts, $_SERVER['HTTP_HOST']
|
||||
* trans_sid_tags, "a=href,area=href,frame=src,form="
|
||||
*
|
||||
* @param array $options Session configuration options
|
||||
* @param \SessionHandlerInterface|null $handler
|
||||
* @param MetadataBag $metaBag MetadataBag
|
||||
* @param AbstractProxy|\SessionHandlerInterface|null $handler
|
||||
*/
|
||||
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
|
||||
public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null)
|
||||
{
|
||||
$options += array(
|
||||
if (!\extension_loaded('session')) {
|
||||
throw new \LogicException('PHP extension "session" is required.');
|
||||
}
|
||||
|
||||
$options += [
|
||||
'cache_limiter' => '',
|
||||
'cache_expire' => 0,
|
||||
'use_cookies' => 1,
|
||||
'lazy_write' => 1,
|
||||
);
|
||||
'use_strict_mode' => 1,
|
||||
];
|
||||
|
||||
session_register_shutdown();
|
||||
|
||||
|
|
@ -137,7 +143,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
throw new \RuntimeException('Failed to start the session: already started by PHP.');
|
||||
}
|
||||
|
||||
if (ini_get('session.use_cookies') && headers_sent($file, $line)) {
|
||||
if (filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN) && headers_sent($file, $line)) {
|
||||
throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +152,13 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
throw new \RuntimeException('Failed to start the session');
|
||||
}
|
||||
|
||||
if (null !== $this->emulateSameSite) {
|
||||
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
|
||||
if (null !== $originalCookie) {
|
||||
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadSession();
|
||||
|
||||
return true;
|
||||
|
|
@ -208,9 +221,16 @@ 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
|
||||
// @see https://bugs.php.net/70013
|
||||
$this->loadSession();
|
||||
|
||||
if (null !== $this->emulateSameSite) {
|
||||
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
|
||||
if (null !== $originalCookie) {
|
||||
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
|
||||
}
|
||||
}
|
||||
|
||||
return $isRegenerated;
|
||||
}
|
||||
|
||||
|
|
@ -219,6 +239,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) {
|
||||
|
|
@ -226,32 +247,29 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
unset($_SESSION[$key]);
|
||||
}
|
||||
}
|
||||
if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) {
|
||||
if ([$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) {
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
|
||||
// Register custom error handler to catch a possible failure warning during session write
|
||||
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
|
||||
throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline);
|
||||
}, E_WARNING);
|
||||
|
||||
try {
|
||||
$e = null;
|
||||
session_write_close();
|
||||
} catch (\ErrorException $e) {
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
$_SESSION = $session;
|
||||
}
|
||||
if (null !== $e) {
|
||||
// The default PHP error message is not very helpful, as it does not give any information on the current save handler.
|
||||
// Therefore, we catch this error and trigger a warning with a better error message
|
||||
$handler = $this->getSaveHandler();
|
||||
if ($handler instanceof SessionHandlerProxy) {
|
||||
$handler = $handler->getHandler();
|
||||
// Register error handler to add information about the current save handler
|
||||
$previousHandler = set_error_handler(function ($type, $msg, $file, $line) use (&$previousHandler) {
|
||||
if (E_WARNING === $type && 0 === strpos($msg, 'session_write_close():')) {
|
||||
$handler = $this->saveHandler instanceof SessionHandlerProxy ? $this->saveHandler->getHandler() : $this->saveHandler;
|
||||
$msg = sprintf('session_write_close(): Failed to write session data with "%s" handler', \get_class($handler));
|
||||
}
|
||||
|
||||
trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', \get_class($handler)), E_USER_WARNING);
|
||||
return $previousHandler ? $previousHandler($type, $msg, $file, $line) : false;
|
||||
});
|
||||
|
||||
try {
|
||||
session_write_close();
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
|
||||
// Restore only if not empty
|
||||
if ($_SESSION) {
|
||||
$_SESSION = $session;
|
||||
}
|
||||
}
|
||||
|
||||
$this->closed = true;
|
||||
|
|
@ -269,7 +287,7 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
}
|
||||
|
||||
// clear out the session
|
||||
$_SESSION = array();
|
||||
$_SESSION = [];
|
||||
|
||||
// reconnect the bags to the session
|
||||
$this->loadSession();
|
||||
|
|
@ -338,9 +356,9 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
* For convenience we omit 'session.' from the beginning of the keys.
|
||||
* Explicitly ignores other ini keys.
|
||||
*
|
||||
* @param array $options Session ini directives array(key => value)
|
||||
* @param array $options Session ini directives [key => value]
|
||||
*
|
||||
* @see http://php.net/session.configuration
|
||||
* @see https://php.net/session.configuration
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
|
|
@ -348,21 +366,26 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
return;
|
||||
}
|
||||
|
||||
$validOptions = array_flip(array(
|
||||
$validOptions = array_flip([
|
||||
'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
|
||||
'cookie_lifetime', 'cookie_path', 'cookie_secure',
|
||||
'entropy_file', 'entropy_length', 'gc_divisor',
|
||||
'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
|
||||
'hash_function', 'lazy_write', 'name', 'referer_check',
|
||||
'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite',
|
||||
'gc_divisor', 'gc_maxlifetime', 'gc_probability',
|
||||
'lazy_write', 'name', 'referer_check',
|
||||
'serialize_handler', 'use_strict_mode', 'use_cookies',
|
||||
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
|
||||
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
|
||||
'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags',
|
||||
'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
|
||||
));
|
||||
]);
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
if (isset($validOptions[$key])) {
|
||||
if ('cookie_samesite' === $key && \PHP_VERSION_ID < 70300) {
|
||||
// PHP < 7.3 does not support same_site cookies. We will emulate it in
|
||||
// the start() method instead.
|
||||
$this->emulateSameSite = $value;
|
||||
continue;
|
||||
}
|
||||
ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
|
||||
}
|
||||
}
|
||||
|
|
@ -378,15 +401,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
|
||||
*/
|
||||
|
|
@ -429,11 +450,11 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||
$session = &$_SESSION;
|
||||
}
|
||||
|
||||
$bags = array_merge($this->bags, array($this->metadataBag));
|
||||
$bags = array_merge($this->bags, [$this->metadataBag]);
|
||||
|
||||
foreach ($bags as $bag) {
|
||||
$key = $bag->getStorageKey();
|
||||
$session[$key] = isset($session[$key]) ? $session[$key] : array();
|
||||
$session[$key] = isset($session[$key]) && \is_array($session[$key]) ? $session[$key] : [];
|
||||
$bag->initialize($session[$key]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
|
||||
|
||||
/**
|
||||
* Allows session to be started by PHP and managed by Symfony.
|
||||
*
|
||||
|
|
@ -19,11 +21,14 @@ namespace Symfony\Component\HttpFoundation\Session\Storage;
|
|||
class PhpBridgeSessionStorage extends NativeSessionStorage
|
||||
{
|
||||
/**
|
||||
* @param \SessionHandlerInterface|null $handler
|
||||
* @param MetadataBag $metaBag MetadataBag
|
||||
* @param AbstractProxy|\SessionHandlerInterface|null $handler
|
||||
*/
|
||||
public function __construct($handler = null, MetadataBag $metaBag = null)
|
||||
{
|
||||
if (!\extension_loaded('session')) {
|
||||
throw new \LogicException('PHP extension "session" is required.');
|
||||
}
|
||||
|
||||
$this->setMetadataBag($metaBag);
|
||||
$this->setSaveHandler($handler);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ abstract class AbstractProxy
|
|||
/**
|
||||
* Gets the session.save_handler name.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSaveHandlerName()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
|
||||
|
||||
@trigger_error('The '.__NAMESPACE__.'\NativeProxy class is deprecated since Symfony 3.4 and will be removed in 4.0. Use your session handler implementation directly.', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* This proxy is built-in session handlers in PHP 5.3.x.
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0. Use your session handler implementation directly.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class NativeProxy extends AbstractProxy
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// this makes an educated guess as to what the handler is since it should already be set.
|
||||
$this->saveHandlerName = ini_get('session.save_handler');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
|
||||
*
|
||||
* @return bool False
|
||||
*/
|
||||
public function isWrapper()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
|||
// \SessionHandlerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
|
|
@ -44,7 +44,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
|
|
@ -52,7 +52,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
|
|
@ -60,7 +60,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
|
|
@ -68,7 +68,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
|
|
@ -76,7 +76,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
|
|
@ -84,7 +84,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function validateId($sessionId)
|
||||
{
|
||||
|
|
@ -92,7 +92,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
public function updateTimestamp($sessionId, $data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ interface SessionStorageInterface
|
|||
* only delete the session data from persistent storage.
|
||||
*
|
||||
* Care: When regenerating the session ID no locking is involved in PHP's
|
||||
* session design. See https://bugs.php.net/bug.php?id=61470 for a discussion.
|
||||
* session design. See https://bugs.php.net/61470 for a discussion.
|
||||
* So you must make sure the regenerated session is saved BEFORE sending the
|
||||
* headers with the new ID. Symfony's HttpKernel offers a listener for this.
|
||||
* See Symfony\Component\HttpKernel\EventListener\SaveSessionListener.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Symfony\Component\HttpFoundation;
|
|||
* A StreamedResponse uses a callback for its content.
|
||||
*
|
||||
* The callback should use the standard PHP functions like echo
|
||||
* to stream the response back to the client. The flush() method
|
||||
* to stream the response back to the client. The flush() function
|
||||
* can also be used if needed.
|
||||
*
|
||||
* @see flush()
|
||||
|
|
@ -35,7 +35,7 @@ class StreamedResponse extends Response
|
|||
* @param int $status The response status code
|
||||
* @param array $headers An array of response headers
|
||||
*/
|
||||
public function __construct(callable $callback = null, $status = 200, $headers = array())
|
||||
public function __construct(callable $callback = null, int $status = 200, array $headers = [])
|
||||
{
|
||||
parent::__construct(null, $status, $headers);
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ class StreamedResponse extends Response
|
|||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create($callback = null, $status = 200, $headers = array())
|
||||
public static function create($callback = null, $status = 200, $headers = [])
|
||||
{
|
||||
return new static($callback, $status, $headers);
|
||||
}
|
||||
|
|
@ -63,8 +63,6 @@ class StreamedResponse extends Response
|
|||
/**
|
||||
* Sets the PHP callback associated with this Response.
|
||||
*
|
||||
* @param callable $callback A valid PHP callback
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCallback(callable $callback)
|
||||
|
|
@ -111,7 +109,7 @@ class StreamedResponse extends Response
|
|||
throw new \LogicException('The Response callback must not be null.');
|
||||
}
|
||||
|
||||
\call_user_func($this->callback);
|
||||
($this->callback)();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -129,28 +127,16 @@ class StreamedResponse extends Response
|
|||
throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
|
||||
}
|
||||
|
||||
$this->streamed = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNotModified()
|
||||
{
|
||||
$this->setCallback(function () {});
|
||||
|
||||
return parent::setNotModified();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
|
||||
|
||||
class AcceptHeaderItemTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideFromStringData
|
||||
*/
|
||||
public function testFromString($string, $value, array $attributes)
|
||||
{
|
||||
$item = AcceptHeaderItem::fromString($string);
|
||||
$this->assertEquals($value, $item->getValue());
|
||||
$this->assertEquals($attributes, $item->getAttributes());
|
||||
}
|
||||
|
||||
public function provideFromStringData()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'text/html',
|
||||
'text/html', array(),
|
||||
),
|
||||
array(
|
||||
'"this;should,not=matter"',
|
||||
'this;should,not=matter', array(),
|
||||
),
|
||||
array(
|
||||
"text/plain; charset=utf-8;param=\"this;should,not=matter\";\tfootnotes=true",
|
||||
'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'),
|
||||
),
|
||||
array(
|
||||
'"this;should,not=matter";charset=utf-8',
|
||||
'this;should,not=matter', array('charset' => 'utf-8'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideToStringData
|
||||
*/
|
||||
public function testToString($value, array $attributes, $string)
|
||||
{
|
||||
$item = new AcceptHeaderItem($value, $attributes);
|
||||
$this->assertEquals($string, (string) $item);
|
||||
}
|
||||
|
||||
public function provideToStringData()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'text/html', array(),
|
||||
'text/html',
|
||||
),
|
||||
array(
|
||||
'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'),
|
||||
'text/plain;charset=utf-8;param="this;should,not=matter";footnotes=true',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testValue()
|
||||
{
|
||||
$item = new AcceptHeaderItem('value', array());
|
||||
$this->assertEquals('value', $item->getValue());
|
||||
|
||||
$item->setValue('new value');
|
||||
$this->assertEquals('new value', $item->getValue());
|
||||
|
||||
$item->setValue(1);
|
||||
$this->assertEquals('1', $item->getValue());
|
||||
}
|
||||
|
||||
public function testQuality()
|
||||
{
|
||||
$item = new AcceptHeaderItem('value', array());
|
||||
$this->assertEquals(1.0, $item->getQuality());
|
||||
|
||||
$item->setQuality(0.5);
|
||||
$this->assertEquals(0.5, $item->getQuality());
|
||||
|
||||
$item->setAttribute('q', 0.75);
|
||||
$this->assertEquals(0.75, $item->getQuality());
|
||||
$this->assertFalse($item->hasAttribute('q'));
|
||||
}
|
||||
|
||||
public function testAttribute()
|
||||
{
|
||||
$item = new AcceptHeaderItem('value', array());
|
||||
$this->assertEquals(array(), $item->getAttributes());
|
||||
$this->assertFalse($item->hasAttribute('test'));
|
||||
$this->assertNull($item->getAttribute('test'));
|
||||
$this->assertEquals('default', $item->getAttribute('test', 'default'));
|
||||
|
||||
$item->setAttribute('test', 'value');
|
||||
$this->assertEquals(array('test' => 'value'), $item->getAttributes());
|
||||
$this->assertTrue($item->hasAttribute('test'));
|
||||
$this->assertEquals('value', $item->getAttribute('test'));
|
||||
$this->assertEquals('value', $item->getAttribute('test', 'default'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\AcceptHeader;
|
||||
use Symfony\Component\HttpFoundation\AcceptHeaderItem;
|
||||
|
||||
class AcceptHeaderTest extends TestCase
|
||||
{
|
||||
public function testFirst()
|
||||
{
|
||||
$header = AcceptHeader::fromString('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c');
|
||||
$this->assertSame('text/html', $header->first()->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFromStringData
|
||||
*/
|
||||
public function testFromString($string, array $items)
|
||||
{
|
||||
$header = AcceptHeader::fromString($string);
|
||||
$parsed = array_values($header->all());
|
||||
// reset index since the fixtures don't have them set
|
||||
foreach ($parsed as $item) {
|
||||
$item->setIndex(0);
|
||||
}
|
||||
$this->assertEquals($items, $parsed);
|
||||
}
|
||||
|
||||
public function provideFromStringData()
|
||||
{
|
||||
return array(
|
||||
array('', array()),
|
||||
array('gzip', array(new AcceptHeaderItem('gzip'))),
|
||||
array('gzip,deflate,sdch', array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))),
|
||||
array("gzip, deflate\t,sdch", array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))),
|
||||
array('"this;should,not=matter"', array(new AcceptHeaderItem('this;should,not=matter'))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideToStringData
|
||||
*/
|
||||
public function testToString(array $items, $string)
|
||||
{
|
||||
$header = new AcceptHeader($items);
|
||||
$this->assertEquals($string, (string) $header);
|
||||
}
|
||||
|
||||
public function provideToStringData()
|
||||
{
|
||||
return array(
|
||||
array(array(), ''),
|
||||
array(array(new AcceptHeaderItem('gzip')), 'gzip'),
|
||||
array(array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')), 'gzip,deflate,sdch'),
|
||||
array(array(new AcceptHeaderItem('this;should,not=matter')), 'this;should,not=matter'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFilterData
|
||||
*/
|
||||
public function testFilter($string, $filter, array $values)
|
||||
{
|
||||
$header = AcceptHeader::fromString($string)->filter($filter);
|
||||
$this->assertEquals($values, array_keys($header->all()));
|
||||
}
|
||||
|
||||
public function provideFilterData()
|
||||
{
|
||||
return array(
|
||||
array('fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', array('fr-FR', 'fr')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideSortingData
|
||||
*/
|
||||
public function testSorting($string, array $values)
|
||||
{
|
||||
$header = AcceptHeader::fromString($string);
|
||||
$this->assertEquals($values, array_keys($header->all()));
|
||||
}
|
||||
|
||||
public function provideSortingData()
|
||||
{
|
||||
return array(
|
||||
'quality has priority' => array('*;q=0.3,ISO-8859-1,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')),
|
||||
'order matters when q is equal' => array('*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')),
|
||||
'order matters when q is equal2' => array('*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', array('utf-8', 'ISO-8859-1', '*')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\ApacheRequest;
|
||||
|
||||
class ApacheRequestTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideServerVars
|
||||
*/
|
||||
public function testUriMethods($server, $expectedRequestUri, $expectedBaseUrl, $expectedPathInfo)
|
||||
{
|
||||
$request = new ApacheRequest();
|
||||
$request->server->replace($server);
|
||||
|
||||
$this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
|
||||
$this->assertEquals($expectedBaseUrl, $request->getBaseUrl(), '->getBaseUrl() is correct');
|
||||
$this->assertEquals($expectedPathInfo, $request->getPathInfo(), '->getPathInfo() is correct');
|
||||
}
|
||||
|
||||
public function provideServerVars()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/foo/app_dev.php/bar',
|
||||
'SCRIPT_NAME' => '/foo/app_dev.php',
|
||||
'PATH_INFO' => '/bar',
|
||||
),
|
||||
'/foo/app_dev.php/bar',
|
||||
'/foo/app_dev.php',
|
||||
'/bar',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/foo/bar',
|
||||
'SCRIPT_NAME' => '/foo/app_dev.php',
|
||||
),
|
||||
'/foo/bar',
|
||||
'/foo',
|
||||
'/bar',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/app_dev.php/foo/bar',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
'PATH_INFO' => '/foo/bar',
|
||||
),
|
||||
'/app_dev.php/foo/bar',
|
||||
'/app_dev.php',
|
||||
'/foo/bar',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/foo/bar',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
),
|
||||
'/foo/bar',
|
||||
'',
|
||||
'/foo/bar',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/app_dev.php',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
),
|
||||
'/app_dev.php',
|
||||
'/app_dev.php',
|
||||
'/',
|
||||
),
|
||||
array(
|
||||
array(
|
||||
'REQUEST_URI' => '/',
|
||||
'SCRIPT_NAME' => '/app_dev.php',
|
||||
),
|
||||
'/',
|
||||
'',
|
||||
'/',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,352 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\File\Stream;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\HttpFoundation\Tests\File\FakeFile;
|
||||
|
||||
class BinaryFileResponseTest extends ResponseTestCase
|
||||
{
|
||||
public function testConstruction()
|
||||
{
|
||||
$file = __DIR__.'/../README.md';
|
||||
$response = new BinaryFileResponse($file, 404, array('X-Header' => 'Foo'), true, null, true, true);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$this->assertEquals('Foo', $response->headers->get('X-Header'));
|
||||
$this->assertTrue($response->headers->has('ETag'));
|
||||
$this->assertTrue($response->headers->has('Last-Modified'));
|
||||
$this->assertFalse($response->headers->has('Content-Disposition'));
|
||||
|
||||
$response = BinaryFileResponse::create($file, 404, array(), true, ResponseHeaderBag::DISPOSITION_INLINE);
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$this->assertFalse($response->headers->has('ETag'));
|
||||
$this->assertEquals('inline; filename="README.md"', $response->headers->get('Content-Disposition'));
|
||||
}
|
||||
|
||||
public function testConstructWithNonAsciiFilename()
|
||||
{
|
||||
touch(sys_get_temp_dir().'/fööö.html');
|
||||
|
||||
$response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, array(), true, 'attachment');
|
||||
|
||||
@unlink(sys_get_temp_dir().'/fööö.html');
|
||||
|
||||
$this->assertSame('fööö.html', $response->getFile()->getFilename());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testSetContent()
|
||||
{
|
||||
$response = new BinaryFileResponse(__FILE__);
|
||||
$response->setContent('foo');
|
||||
}
|
||||
|
||||
public function testGetContent()
|
||||
{
|
||||
$response = new BinaryFileResponse(__FILE__);
|
||||
$this->assertFalse($response->getContent());
|
||||
}
|
||||
|
||||
public function testSetContentDispositionGeneratesSafeFallbackFilename()
|
||||
{
|
||||
$response = new BinaryFileResponse(__FILE__);
|
||||
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'föö.html');
|
||||
|
||||
$this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition'));
|
||||
}
|
||||
|
||||
public function testSetContentDispositionGeneratesSafeFallbackFilenameForWronglyEncodedFilename()
|
||||
{
|
||||
$response = new BinaryFileResponse(__FILE__);
|
||||
|
||||
$iso88591EncodedFilename = utf8_decode('föö.html');
|
||||
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $iso88591EncodedFilename);
|
||||
|
||||
// the parameter filename* is invalid in this case (rawurldecode('f%F6%F6') does not provide a UTF-8 string but an ISO-8859-1 encoded one)
|
||||
$this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%F6%F6.html', $response->headers->get('Content-Disposition'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideRanges
|
||||
*/
|
||||
public function testRequests($requestRange, $offset, $length, $responseRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
||||
|
||||
// do a request to get the ETag
|
||||
$request = Request::create('/');
|
||||
$response->prepare($request);
|
||||
$etag = $response->headers->get('ETag');
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('If-Range', $etag);
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
||||
fseek($file, $offset);
|
||||
$data = fread($file, $length);
|
||||
fclose($file);
|
||||
|
||||
$this->expectOutputString($data);
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(206, $response->getStatusCode());
|
||||
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
|
||||
$this->assertSame($length, $response->headers->get('Content-Length'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideRanges
|
||||
*/
|
||||
public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
|
||||
// do a request to get the LastModified
|
||||
$request = Request::create('/');
|
||||
$response->prepare($request);
|
||||
$lastModified = $response->headers->get('Last-Modified');
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('If-Range', $lastModified);
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
||||
fseek($file, $offset);
|
||||
$data = fread($file, $length);
|
||||
fclose($file);
|
||||
|
||||
$this->expectOutputString($data);
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(206, $response->getStatusCode());
|
||||
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
public function provideRanges()
|
||||
{
|
||||
return array(
|
||||
array('bytes=1-4', 1, 4, 'bytes 1-4/35'),
|
||||
array('bytes=-5', 30, 5, 'bytes 30-34/35'),
|
||||
array('bytes=30-', 30, 5, 'bytes 30-34/35'),
|
||||
array('bytes=30-30', 30, 1, 'bytes 30-30/35'),
|
||||
array('bytes=30-34', 30, 5, 'bytes 30-34/35'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testRangeRequestsWithoutLastModifiedDate()
|
||||
{
|
||||
// prevent auto last modified
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'), true, null, false, false);
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT');
|
||||
$request->headers->set('Range', 'bytes=1-4');
|
||||
|
||||
$this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif'));
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertNull($response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFullFileRanges
|
||||
*/
|
||||
public function testFullFileRequests($requestRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
||||
$data = fread($file, 35);
|
||||
fclose($file);
|
||||
|
||||
$this->expectOutputString($data);
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function provideFullFileRanges()
|
||||
{
|
||||
return array(
|
||||
array('bytes=0-'),
|
||||
array('bytes=0-34'),
|
||||
array('bytes=-35'),
|
||||
// Syntactical invalid range-request should also return the full resource
|
||||
array('bytes=20-10'),
|
||||
array('bytes=50-40'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidRanges
|
||||
*/
|
||||
public function testInvalidRequests($requestRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(416, $response->getStatusCode());
|
||||
$this->assertEquals('bytes */35', $response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
public function provideInvalidRanges()
|
||||
{
|
||||
return array(
|
||||
array('bytes=-40'),
|
||||
array('bytes=30-40'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideXSendfileFiles
|
||||
*/
|
||||
public function testXSendfile($file)
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('X-Sendfile-Type', 'X-Sendfile');
|
||||
|
||||
BinaryFileResponse::trustXSendfileTypeHeader();
|
||||
$response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$response->prepare($request);
|
||||
|
||||
$this->expectOutputString('');
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertContains('README.md', $response->headers->get('X-Sendfile'));
|
||||
}
|
||||
|
||||
public function provideXSendfileFiles()
|
||||
{
|
||||
return array(
|
||||
array(__DIR__.'/../README.md'),
|
||||
array('file://'.__DIR__.'/../README.md'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getSampleXAccelMappings
|
||||
*/
|
||||
public function testXAccelMapping($realpath, $mapping, $virtual)
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect');
|
||||
$request->headers->set('X-Accel-Mapping', $mapping);
|
||||
|
||||
$file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test');
|
||||
|
||||
BinaryFileResponse::trustXSendfileTypeHeader();
|
||||
$response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$reflection = new \ReflectionObject($response);
|
||||
$property = $reflection->getProperty('file');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($response, $file);
|
||||
|
||||
$response->prepare($request);
|
||||
$this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect'));
|
||||
}
|
||||
|
||||
public function testDeleteFileAfterSend()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
|
||||
$path = __DIR__.'/File/Fixtures/to_delete';
|
||||
touch($path);
|
||||
$realPath = realpath($path);
|
||||
$this->assertFileExists($realPath);
|
||||
|
||||
$response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$response->deleteFileAfterSend(true);
|
||||
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertFileNotExists($path);
|
||||
}
|
||||
|
||||
public function testAcceptRangeOnUnsafeMethods()
|
||||
{
|
||||
$request = Request::create('/', 'POST');
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertEquals('none', $response->headers->get('Accept-Ranges'));
|
||||
}
|
||||
|
||||
public function testAcceptRangeNotOverriden()
|
||||
{
|
||||
$request = Request::create('/', 'POST');
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
$response->headers->set('Accept-Ranges', 'foo');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertEquals('foo', $response->headers->get('Accept-Ranges'));
|
||||
}
|
||||
|
||||
public function getSampleXAccelMappings()
|
||||
{
|
||||
return array(
|
||||
array('/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'),
|
||||
array('/home/foo/bar.txt', '/var/www/=/files/,/home/foo/=/baz/', '/baz/bar.txt'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testStream()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, array('Content-Type' => 'text/plain'));
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertNull($response->headers->get('Content-Length'));
|
||||
}
|
||||
|
||||
protected function provideResponse()
|
||||
{
|
||||
return new BinaryFileResponse(__DIR__.'/../README.md', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
$path = __DIR__.'/../Fixtures/to_delete';
|
||||
if (file_exists($path)) {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
/**
|
||||
* CookieTest.
|
||||
*
|
||||
* @author John Kary <john@johnkary.net>
|
||||
* @author Hugo Hamon <hugo.hamon@sensio.com>
|
||||
*
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class CookieTest extends TestCase
|
||||
{
|
||||
public function invalidNames()
|
||||
{
|
||||
return array(
|
||||
array(''),
|
||||
array(',MyName'),
|
||||
array(';MyName'),
|
||||
array(' MyName'),
|
||||
array("\tMyName"),
|
||||
array("\rMyName"),
|
||||
array("\nMyName"),
|
||||
array("\013MyName"),
|
||||
array("\014MyName"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidNames
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
|
||||
{
|
||||
new Cookie($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidExpiration()
|
||||
{
|
||||
new Cookie('MyCookie', 'foo', 'bar');
|
||||
}
|
||||
|
||||
public function testNegativeExpirationIsNotPossible()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', -100);
|
||||
|
||||
$this->assertSame(0, $cookie->getExpiresTime());
|
||||
}
|
||||
|
||||
public function testGetValue()
|
||||
{
|
||||
$value = 'MyValue';
|
||||
$cookie = new Cookie('MyCookie', $value);
|
||||
|
||||
$this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value');
|
||||
}
|
||||
|
||||
public function testGetPath()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar');
|
||||
|
||||
$this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path');
|
||||
}
|
||||
|
||||
public function testGetExpiresTime()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar');
|
||||
|
||||
$this->assertEquals(0, $cookie->getExpiresTime(), '->getExpiresTime() returns the default expire date');
|
||||
|
||||
$cookie = new Cookie('foo', 'bar', $expire = time() + 3600);
|
||||
|
||||
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
|
||||
}
|
||||
|
||||
public function testGetExpiresTimeIsCastToInt()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', 3600.9);
|
||||
|
||||
$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
|
||||
}
|
||||
|
||||
public function testConstructorWithDateTime()
|
||||
{
|
||||
$expire = new \DateTime();
|
||||
$cookie = new Cookie('foo', 'bar', $expire);
|
||||
|
||||
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 5.5
|
||||
*/
|
||||
public function testConstructorWithDateTimeImmutable()
|
||||
{
|
||||
$expire = new \DateTimeImmutable();
|
||||
$cookie = new Cookie('foo', 'bar', $expire);
|
||||
|
||||
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
|
||||
}
|
||||
|
||||
public function testGetExpiresTimeWithStringValue()
|
||||
{
|
||||
$value = '+1 day';
|
||||
$cookie = new Cookie('foo', 'bar', $value);
|
||||
$expire = strtotime($value);
|
||||
|
||||
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1);
|
||||
}
|
||||
|
||||
public function testGetDomain()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com');
|
||||
|
||||
$this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
|
||||
}
|
||||
|
||||
public function testIsSecure()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', true);
|
||||
|
||||
$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
|
||||
}
|
||||
|
||||
public function testIsHttpOnly()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
|
||||
|
||||
$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
|
||||
}
|
||||
|
||||
public function testCookieIsNotCleared()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', time() + 3600 * 24);
|
||||
|
||||
$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
|
||||
}
|
||||
|
||||
public function testCookieIsCleared()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', time() - 20);
|
||||
|
||||
$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
|
||||
|
||||
$cookie = new Cookie('foo', 'bar');
|
||||
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
|
||||
$cookie = new Cookie('foo', 'bar', 0);
|
||||
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
|
||||
$cookie = new Cookie('foo', 'bar', -1);
|
||||
|
||||
$this->assertFalse($cookie->isCleared());
|
||||
}
|
||||
|
||||
public function testToString()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
|
||||
$this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
|
||||
|
||||
$cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
|
||||
$this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
|
||||
|
||||
$cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com');
|
||||
$this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
|
||||
|
||||
$cookie = new Cookie('foo', 'bar', 0, '/', '');
|
||||
$this->assertEquals('foo=bar; path=/; httponly', (string) $cookie);
|
||||
}
|
||||
|
||||
public function testRawCookie()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false);
|
||||
$this->assertFalse($cookie->isRaw());
|
||||
$this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie);
|
||||
|
||||
$cookie = new Cookie('foo', 'b+a+r', 0, '/', null, false, false, true);
|
||||
$this->assertTrue($cookie->isRaw());
|
||||
$this->assertEquals('foo=b+a+r; path=/', (string) $cookie);
|
||||
}
|
||||
|
||||
public function testGetMaxAge()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar');
|
||||
$this->assertEquals(0, $cookie->getMaxAge());
|
||||
|
||||
$cookie = new Cookie('foo', 'bar', $expire = time() + 100);
|
||||
$this->assertEquals($expire - time(), $cookie->getMaxAge());
|
||||
|
||||
$cookie = new Cookie('foo', 'bar', $expire = time() - 100);
|
||||
$this->assertEquals(0, $cookie->getMaxAge());
|
||||
}
|
||||
|
||||
public function testFromString()
|
||||
{
|
||||
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
|
||||
$this->assertEquals(new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true), $cookie);
|
||||
|
||||
$cookie = Cookie::fromString('foo=bar', true);
|
||||
$this->assertEquals(new Cookie('foo', 'bar', 0, '/', null, false, false), $cookie);
|
||||
}
|
||||
|
||||
public function testFromStringWithHttpOnly()
|
||||
{
|
||||
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
|
||||
$this->assertTrue($cookie->isHttpOnly());
|
||||
|
||||
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure');
|
||||
$this->assertFalse($cookie->isHttpOnly());
|
||||
}
|
||||
|
||||
public function testSameSiteAttributeIsCaseInsensitive()
|
||||
{
|
||||
$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, 'Lax');
|
||||
$this->assertEquals('lax', $cookie->getSameSite());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\HttpFoundation\ExpressionRequestMatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class ExpressionRequestMatcherTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testWhenNoExpressionIsSet()
|
||||
{
|
||||
$expressionRequestMatcher = new ExpressionRequestMatcher();
|
||||
$expressionRequestMatcher->matches(new Request());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideExpressions
|
||||
*/
|
||||
public function testMatchesWhenParentMatchesIsTrue($expression, $expected)
|
||||
{
|
||||
$request = Request::create('/foo');
|
||||
$expressionRequestMatcher = new ExpressionRequestMatcher();
|
||||
|
||||
$expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
|
||||
$this->assertSame($expected, $expressionRequestMatcher->matches($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideExpressions
|
||||
*/
|
||||
public function testMatchesWhenParentMatchesIsFalse($expression)
|
||||
{
|
||||
$request = Request::create('/foo');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$expressionRequestMatcher = new ExpressionRequestMatcher();
|
||||
$expressionRequestMatcher->matchAttribute('foo', 'bar');
|
||||
|
||||
$expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression);
|
||||
$this->assertFalse($expressionRequestMatcher->matches($request));
|
||||
}
|
||||
|
||||
public function provideExpressions()
|
||||
{
|
||||
return array(
|
||||
array('request.getMethod() == method', true),
|
||||
array('request.getPathInfo() == path', true),
|
||||
array('request.getHost() == host', true),
|
||||
array('request.getClientIp() == ip', true),
|
||||
array('request.attributes.all() == attributes', true),
|
||||
array('request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', true),
|
||||
array('request.getMethod() != method', false),
|
||||
array('request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\File;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\File as OrigFile;
|
||||
|
||||
class FakeFile extends OrigFile
|
||||
{
|
||||
private $realpath;
|
||||
|
||||
public function __construct($realpath, $path)
|
||||
{
|
||||
$this->realpath = $realpath;
|
||||
parent::__construct($path, false);
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRealpath()
|
||||
{
|
||||
return $this->realpath;
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
public function getMTime()
|
||||
{
|
||||
return time();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\File;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
|
||||
class FileTest extends TestCase
|
||||
{
|
||||
protected $file;
|
||||
|
||||
public function testGetMimeTypeUsesMimeTypeGuessers()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/test.gif');
|
||||
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
|
||||
|
||||
MimeTypeGuesser::getInstance()->register($guesser);
|
||||
|
||||
$this->assertEquals('image/gif', $file->getMimeType());
|
||||
}
|
||||
|
||||
public function testGuessExtensionWithoutGuesser()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/directory/.empty');
|
||||
|
||||
$this->assertNull($file->guessExtension());
|
||||
}
|
||||
|
||||
public function testGuessExtensionIsBasedOnMimeType()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/test');
|
||||
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
|
||||
|
||||
MimeTypeGuesser::getInstance()->register($guesser);
|
||||
|
||||
$this->assertEquals('gif', $file->guessExtension());
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension fileinfo
|
||||
*/
|
||||
public function testGuessExtensionWithReset()
|
||||
{
|
||||
$file = new File(__DIR__.'/Fixtures/other-file.example');
|
||||
$guesser = $this->createMockGuesser($file->getPathname(), 'image/gif');
|
||||
MimeTypeGuesser::getInstance()->register($guesser);
|
||||
|
||||
$this->assertEquals('gif', $file->guessExtension());
|
||||
|
||||
MimeTypeGuesser::reset();
|
||||
|
||||
$this->assertNull($file->guessExtension());
|
||||
}
|
||||
|
||||
public function testConstructWhenFileNotExists()
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
|
||||
|
||||
new File(__DIR__.'/Fixtures/not_here');
|
||||
}
|
||||
|
||||
public function testMove()
|
||||
{
|
||||
$path = __DIR__.'/Fixtures/test.copy.gif';
|
||||
$targetDir = __DIR__.'/Fixtures/directory';
|
||||
$targetPath = $targetDir.'/test.copy.gif';
|
||||
@unlink($path);
|
||||
@unlink($targetPath);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $path);
|
||||
|
||||
$file = new File($path);
|
||||
$movedFile = $file->move($targetDir);
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($path);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($targetPath);
|
||||
}
|
||||
|
||||
public function testMoveWithNewName()
|
||||
{
|
||||
$path = __DIR__.'/Fixtures/test.copy.gif';
|
||||
$targetDir = __DIR__.'/Fixtures/directory';
|
||||
$targetPath = $targetDir.'/test.newname.gif';
|
||||
@unlink($path);
|
||||
@unlink($targetPath);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $path);
|
||||
|
||||
$file = new File($path);
|
||||
$movedFile = $file->move($targetDir, 'test.newname.gif');
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($path);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($targetPath);
|
||||
}
|
||||
|
||||
public function getFilenameFixtures()
|
||||
{
|
||||
return array(
|
||||
array('original.gif', 'original.gif'),
|
||||
array('..\\..\\original.gif', 'original.gif'),
|
||||
array('../../original.gif', 'original.gif'),
|
||||
array('файлfile.gif', 'файлfile.gif'),
|
||||
array('..\\..\\файлfile.gif', 'файлfile.gif'),
|
||||
array('../../файлfile.gif', 'файлfile.gif'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getFilenameFixtures
|
||||
*/
|
||||
public function testMoveWithNonLatinName($filename, $sanitizedFilename)
|
||||
{
|
||||
$path = __DIR__.'/Fixtures/'.$sanitizedFilename;
|
||||
$targetDir = __DIR__.'/Fixtures/directory/';
|
||||
$targetPath = $targetDir.$sanitizedFilename;
|
||||
@unlink($path);
|
||||
@unlink($targetPath);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $path);
|
||||
|
||||
$file = new File($path);
|
||||
$movedFile = $file->move($targetDir, $filename);
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile);
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($path);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($targetPath);
|
||||
}
|
||||
|
||||
public function testMoveToAnUnexistentDirectory()
|
||||
{
|
||||
$sourcePath = __DIR__.'/Fixtures/test.copy.gif';
|
||||
$targetDir = __DIR__.'/Fixtures/directory/sub';
|
||||
$targetPath = $targetDir.'/test.copy.gif';
|
||||
@unlink($sourcePath);
|
||||
@unlink($targetPath);
|
||||
@rmdir($targetDir);
|
||||
copy(__DIR__.'/Fixtures/test.gif', $sourcePath);
|
||||
|
||||
$file = new File($sourcePath);
|
||||
$movedFile = $file->move($targetDir);
|
||||
|
||||
$this->assertFileExists($targetPath);
|
||||
$this->assertFileNotExists($sourcePath);
|
||||
$this->assertEquals(realpath($targetPath), $movedFile->getRealPath());
|
||||
|
||||
@unlink($sourcePath);
|
||||
@unlink($targetPath);
|
||||
@rmdir($targetDir);
|
||||
}
|
||||
|
||||
protected function createMockGuesser($path, $mimeType)
|
||||
{
|
||||
$guesser = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface')->getMock();
|
||||
$guesser
|
||||
->expects($this->once())
|
||||
->method('guess')
|
||||
->with($this->equalTo($path))
|
||||
->will($this->returnValue($mimeType))
|
||||
;
|
||||
|
||||
return $guesser;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
f
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 B |
Binary file not shown.
|
Before Width: | Height: | Size: 35 B |
|
|
@ -1,90 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\File\MimeType;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
|
||||
/**
|
||||
* @requires extension fileinfo
|
||||
*/
|
||||
class MimeTypeTest extends TestCase
|
||||
{
|
||||
protected $path;
|
||||
|
||||
public function testGuessImageWithoutExtension()
|
||||
{
|
||||
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
|
||||
}
|
||||
|
||||
public function testGuessImageWithDirectory()
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
|
||||
|
||||
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory');
|
||||
}
|
||||
|
||||
public function testGuessImageWithFileBinaryMimeTypeGuesser()
|
||||
{
|
||||
$guesser = MimeTypeGuesser::getInstance();
|
||||
$guesser->register(new FileBinaryMimeTypeGuesser());
|
||||
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test'));
|
||||
}
|
||||
|
||||
public function testGuessImageWithKnownExtension()
|
||||
{
|
||||
$this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif'));
|
||||
}
|
||||
|
||||
public function testGuessFileWithUnknownExtension()
|
||||
{
|
||||
$this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension'));
|
||||
}
|
||||
|
||||
public function testGuessWithIncorrectPath()
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
|
||||
MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here');
|
||||
}
|
||||
|
||||
public function testGuessWithNonReadablePath()
|
||||
{
|
||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||
$this->markTestSkipped('Can not verify chmod operations on Windows');
|
||||
}
|
||||
|
||||
if (!getenv('USER') || 'root' === getenv('USER')) {
|
||||
$this->markTestSkipped('This test will fail if run under superuser');
|
||||
}
|
||||
|
||||
$path = __DIR__.'/../Fixtures/to_delete';
|
||||
touch($path);
|
||||
@chmod($path, 0333);
|
||||
|
||||
if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) {
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException');
|
||||
MimeTypeGuesser::getInstance()->guess($path);
|
||||
} else {
|
||||
$this->markTestSkipped('Can not verify chmod operations, change of file permissions failed');
|
||||
}
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
$path = __DIR__.'/../Fixtures/to_delete';
|
||||
if (file_exists($path)) {
|
||||
@chmod($path, 0666);
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,273 +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\UploadedFile;
|
||||
|
||||
class UploadedFileTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
if (!ini_get('file_uploads')) {
|
||||
$this->markTestSkipped('file_uploads is disabled in php.ini');
|
||||
}
|
||||
}
|
||||
|
||||
public function testConstructWhenFileNotExists()
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException');
|
||||
|
||||
new UploadedFile(
|
||||
__DIR__.'/Fixtures/not_here',
|
||||
'original.gif',
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public function testFileUploadsWithNoMimeType()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
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,
|
||||
filesize(__DIR__.'/Fixtures/.unknownextension'),
|
||||
UPLOAD_ERR_OK
|
||||
);
|
||||
|
||||
$this->assertEquals('application/octet-stream', $file->getClientMimeType());
|
||||
}
|
||||
|
||||
public function testGuessClientExtension()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('gif', $file->guessClientExtension());
|
||||
}
|
||||
|
||||
public function testGuessClientExtensionWithIncorrectMimeType()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/jpeg',
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('jpeg', $file->guessClientExtension());
|
||||
}
|
||||
|
||||
public function testErrorIsOkByDefault()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals(UPLOAD_ERR_OK, $file->getError());
|
||||
}
|
||||
|
||||
public function testGetClientOriginalName()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('original.gif', $file->getClientOriginalName());
|
||||
}
|
||||
|
||||
public function testGetClientOriginalExtension()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
filesize(__DIR__.'/Fixtures/test.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',
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
UPLOAD_ERR_OK
|
||||
);
|
||||
|
||||
$movedFile = $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',
|
||||
filesize($path),
|
||||
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',
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('original.gif', $file->getClientOriginalName());
|
||||
}
|
||||
|
||||
public function testGetSize()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
'image/gif',
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
null
|
||||
);
|
||||
|
||||
$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());
|
||||
}
|
||||
|
||||
public function testGetExtension()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null
|
||||
);
|
||||
|
||||
$this->assertEquals('gif', $file->getExtension());
|
||||
}
|
||||
|
||||
public function testIsValid()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
UPLOAD_ERR_OK,
|
||||
true
|
||||
);
|
||||
|
||||
$this->assertTrue($file->isValid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider uploadedFileErrorProvider
|
||||
*/
|
||||
public function testIsInvalidOnUploadError($error)
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
$error
|
||||
);
|
||||
|
||||
$this->assertFalse($file->isValid());
|
||||
}
|
||||
|
||||
public function uploadedFileErrorProvider()
|
||||
{
|
||||
return array(
|
||||
array(UPLOAD_ERR_INI_SIZE),
|
||||
array(UPLOAD_ERR_FORM_SIZE),
|
||||
array(UPLOAD_ERR_PARTIAL),
|
||||
array(UPLOAD_ERR_NO_TMP_DIR),
|
||||
array(UPLOAD_ERR_EXTENSION),
|
||||
);
|
||||
}
|
||||
|
||||
public function testIsInvalidIfNotHttpUpload()
|
||||
{
|
||||
$file = new UploadedFile(
|
||||
__DIR__.'/Fixtures/test.gif',
|
||||
'original.gif',
|
||||
null,
|
||||
filesize(__DIR__.'/Fixtures/test.gif'),
|
||||
UPLOAD_ERR_OK
|
||||
);
|
||||
|
||||
$this->assertFalse($file->isValid());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\FileBag;
|
||||
|
||||
/**
|
||||
* FileBagTest.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
|
||||
*/
|
||||
class FileBagTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testFileMustBeAnArrayOrUploadedFile()
|
||||
{
|
||||
new FileBag(array('file' => 'foo'));
|
||||
}
|
||||
|
||||
public function testShouldConvertsUploadedFiles()
|
||||
{
|
||||
$tmpFile = $this->createTempFile();
|
||||
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
|
||||
|
||||
$bag = new FileBag(array('file' => array(
|
||||
'name' => basename($tmpFile),
|
||||
'type' => 'text/plain',
|
||||
'tmp_name' => $tmpFile,
|
||||
'error' => 0,
|
||||
'size' => 100,
|
||||
)));
|
||||
|
||||
$this->assertEquals($file, $bag->get('file'));
|
||||
}
|
||||
|
||||
public function testShouldSetEmptyUploadedFilesToNull()
|
||||
{
|
||||
$bag = new FileBag(array('file' => array(
|
||||
'name' => '',
|
||||
'type' => '',
|
||||
'tmp_name' => '',
|
||||
'error' => UPLOAD_ERR_NO_FILE,
|
||||
'size' => 0,
|
||||
)));
|
||||
|
||||
$this->assertNull($bag->get('file'));
|
||||
}
|
||||
|
||||
public function testShouldRemoveEmptyUploadedFilesForMultiUpload()
|
||||
{
|
||||
$bag = new FileBag(array('files' => array(
|
||||
'name' => array(''),
|
||||
'type' => array(''),
|
||||
'tmp_name' => array(''),
|
||||
'error' => array(UPLOAD_ERR_NO_FILE),
|
||||
'size' => array(0),
|
||||
)));
|
||||
|
||||
$this->assertSame(array(), $bag->get('files'));
|
||||
}
|
||||
|
||||
public function testShouldNotRemoveEmptyUploadedFilesForAssociativeArray()
|
||||
{
|
||||
$bag = new FileBag(array('files' => array(
|
||||
'name' => array('file1' => ''),
|
||||
'type' => array('file1' => ''),
|
||||
'tmp_name' => array('file1' => ''),
|
||||
'error' => array('file1' => UPLOAD_ERR_NO_FILE),
|
||||
'size' => array('file1' => 0),
|
||||
)));
|
||||
|
||||
$this->assertSame(array('file1' => null), $bag->get('files'));
|
||||
}
|
||||
|
||||
public function testShouldConvertUploadedFilesWithPhpBug()
|
||||
{
|
||||
$tmpFile = $this->createTempFile();
|
||||
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
|
||||
|
||||
$bag = new FileBag(array(
|
||||
'child' => array(
|
||||
'name' => array(
|
||||
'file' => basename($tmpFile),
|
||||
),
|
||||
'type' => array(
|
||||
'file' => 'text/plain',
|
||||
),
|
||||
'tmp_name' => array(
|
||||
'file' => $tmpFile,
|
||||
),
|
||||
'error' => array(
|
||||
'file' => 0,
|
||||
),
|
||||
'size' => array(
|
||||
'file' => 100,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$files = $bag->all();
|
||||
$this->assertEquals($file, $files['child']['file']);
|
||||
}
|
||||
|
||||
public function testShouldConvertNestedUploadedFilesWithPhpBug()
|
||||
{
|
||||
$tmpFile = $this->createTempFile();
|
||||
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
|
||||
|
||||
$bag = new FileBag(array(
|
||||
'child' => array(
|
||||
'name' => array(
|
||||
'sub' => array('file' => basename($tmpFile)),
|
||||
),
|
||||
'type' => array(
|
||||
'sub' => array('file' => 'text/plain'),
|
||||
),
|
||||
'tmp_name' => array(
|
||||
'sub' => array('file' => $tmpFile),
|
||||
),
|
||||
'error' => array(
|
||||
'sub' => array('file' => 0),
|
||||
),
|
||||
'size' => array(
|
||||
'sub' => array('file' => 100),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$files = $bag->all();
|
||||
$this->assertEquals($file, $files['child']['sub']['file']);
|
||||
}
|
||||
|
||||
public function testShouldNotConvertNestedUploadedFiles()
|
||||
{
|
||||
$tmpFile = $this->createTempFile();
|
||||
$file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0);
|
||||
$bag = new FileBag(array('image' => array('file' => $file)));
|
||||
|
||||
$files = $bag->all();
|
||||
$this->assertEquals($file, $files['image']['file']);
|
||||
}
|
||||
|
||||
protected function createTempFile()
|
||||
{
|
||||
return tempnam(sys_get_temp_dir().'/form_test', 'FormTest');
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
mkdir(sys_get_temp_dir().'/form_test', 0777, true);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
rmdir(sys_get_temp_dir().'/form_test');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,205 +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\HeaderBag;
|
||||
|
||||
class HeaderBagTest extends TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'bar'));
|
||||
$this->assertTrue($bag->has('foo'));
|
||||
}
|
||||
|
||||
public function testToStringNull()
|
||||
{
|
||||
$bag = new HeaderBag();
|
||||
$this->assertEquals('', $bag->__toString());
|
||||
}
|
||||
|
||||
public function testToStringNotNull()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'bar'));
|
||||
$this->assertEquals("Foo: bar\r\n", $bag->__toString());
|
||||
}
|
||||
|
||||
public function testKeys()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'bar'));
|
||||
$keys = $bag->keys();
|
||||
$this->assertEquals('foo', $keys[0]);
|
||||
}
|
||||
|
||||
public function testGetDate()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'Tue, 4 Sep 2012 20:00:00 +0200'));
|
||||
$headerDate = $bag->getDate('foo');
|
||||
$this->assertInstanceOf('DateTime', $headerDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testGetDateException()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'Tue'));
|
||||
$headerDate = $bag->getDate('foo');
|
||||
}
|
||||
|
||||
public function testGetCacheControlHeader()
|
||||
{
|
||||
$bag = new HeaderBag();
|
||||
$bag->addCacheControlDirective('public', '#a');
|
||||
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
||||
$this->assertEquals('#a', $bag->getCacheControlDirective('public'));
|
||||
}
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'bar'));
|
||||
$this->assertEquals(array('foo' => array('bar')), $bag->all(), '->all() gets all the input');
|
||||
|
||||
$bag = new HeaderBag(array('FOO' => 'BAR'));
|
||||
$this->assertEquals(array('foo' => array('BAR')), $bag->all(), '->all() gets all the input key are lower case');
|
||||
}
|
||||
|
||||
public function testReplace()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'bar'));
|
||||
|
||||
$bag->replace(array('NOPE' => 'BAR'));
|
||||
$this->assertEquals(array('nope' => array('BAR')), $bag->all(), '->replace() replaces the input with the argument');
|
||||
$this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input');
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz'));
|
||||
$this->assertEquals('bar', $bag->get('foo'), '->get return current value');
|
||||
$this->assertEquals('bar', $bag->get('FoO'), '->get key in case insensitive');
|
||||
$this->assertEquals(array('bar'), $bag->get('foo', 'nope', false), '->get return the value as array');
|
||||
|
||||
// defaults
|
||||
$this->assertNull($bag->get('none'), '->get unknown values returns null');
|
||||
$this->assertEquals('default', $bag->get('none', 'default'), '->get unknown values returns default');
|
||||
$this->assertEquals(array('default'), $bag->get('none', 'default', false), '->get unknown values returns default as array');
|
||||
|
||||
$bag->set('foo', 'bor', false);
|
||||
$this->assertEquals('bar', $bag->get('foo'), '->get return first value');
|
||||
$this->assertEquals(array('bar', 'bor'), $bag->get('foo', 'nope', false), '->get return all values as array');
|
||||
}
|
||||
|
||||
public function testSetAssociativeArray()
|
||||
{
|
||||
$bag = new HeaderBag();
|
||||
$bag->set('foo', array('bad-assoc-index' => 'value'));
|
||||
$this->assertSame('value', $bag->get('foo'));
|
||||
$this->assertEquals(array('value'), $bag->get('foo', 'nope', false), 'assoc indices of multi-valued headers are ignored');
|
||||
}
|
||||
|
||||
public function testContains()
|
||||
{
|
||||
$bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz'));
|
||||
$this->assertTrue($bag->contains('foo', 'bar'), '->contains first value');
|
||||
$this->assertTrue($bag->contains('fuzz', 'bizz'), '->contains second value');
|
||||
$this->assertFalse($bag->contains('nope', 'nope'), '->contains unknown value');
|
||||
$this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value');
|
||||
|
||||
// Multiple values
|
||||
$bag->set('foo', 'bor', false);
|
||||
$this->assertTrue($bag->contains('foo', 'bar'), '->contains first value');
|
||||
$this->assertTrue($bag->contains('foo', 'bor'), '->contains second value');
|
||||
$this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value');
|
||||
}
|
||||
|
||||
public function testCacheControlDirectiveAccessors()
|
||||
{
|
||||
$bag = new HeaderBag();
|
||||
$bag->addCacheControlDirective('public');
|
||||
|
||||
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
||||
$this->assertTrue($bag->getCacheControlDirective('public'));
|
||||
$this->assertEquals('public', $bag->get('cache-control'));
|
||||
|
||||
$bag->addCacheControlDirective('max-age', 10);
|
||||
$this->assertTrue($bag->hasCacheControlDirective('max-age'));
|
||||
$this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
|
||||
$this->assertEquals('max-age=10, public', $bag->get('cache-control'));
|
||||
|
||||
$bag->removeCacheControlDirective('max-age');
|
||||
$this->assertFalse($bag->hasCacheControlDirective('max-age'));
|
||||
}
|
||||
|
||||
public function testCacheControlDirectiveParsing()
|
||||
{
|
||||
$bag = new HeaderBag(array('cache-control' => 'public, max-age=10'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
||||
$this->assertTrue($bag->getCacheControlDirective('public'));
|
||||
|
||||
$this->assertTrue($bag->hasCacheControlDirective('max-age'));
|
||||
$this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
|
||||
|
||||
$bag->addCacheControlDirective('s-maxage', 100);
|
||||
$this->assertEquals('max-age=10, public, s-maxage=100', $bag->get('cache-control'));
|
||||
}
|
||||
|
||||
public function testCacheControlDirectiveParsingQuotedZero()
|
||||
{
|
||||
$bag = new HeaderBag(array('cache-control' => 'max-age="0"'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('max-age'));
|
||||
$this->assertEquals(0, $bag->getCacheControlDirective('max-age'));
|
||||
}
|
||||
|
||||
public function testCacheControlDirectiveOverrideWithReplace()
|
||||
{
|
||||
$bag = new HeaderBag(array('cache-control' => 'private, max-age=100'));
|
||||
$bag->replace(array('cache-control' => 'public, max-age=10'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
||||
$this->assertTrue($bag->getCacheControlDirective('public'));
|
||||
|
||||
$this->assertTrue($bag->hasCacheControlDirective('max-age'));
|
||||
$this->assertEquals(10, $bag->getCacheControlDirective('max-age'));
|
||||
}
|
||||
|
||||
public function testCacheControlClone()
|
||||
{
|
||||
$headers = array('foo' => 'bar');
|
||||
$bag1 = new HeaderBag($headers);
|
||||
$bag2 = new HeaderBag($bag1->all());
|
||||
|
||||
$this->assertEquals($bag1->all(), $bag2->all());
|
||||
}
|
||||
|
||||
public function testGetIterator()
|
||||
{
|
||||
$headers = array('foo' => 'bar', 'hello' => 'world', 'third' => 'charm');
|
||||
$headerBag = new HeaderBag($headers);
|
||||
|
||||
$i = 0;
|
||||
foreach ($headerBag as $key => $val) {
|
||||
++$i;
|
||||
$this->assertEquals(array($headers[$key]), $val);
|
||||
}
|
||||
|
||||
$this->assertEquals(\count($headers), $i);
|
||||
}
|
||||
|
||||
public function testCount()
|
||||
{
|
||||
$headers = array('foo' => 'bar', 'HELLO' => 'WORLD');
|
||||
$headerBag = new HeaderBag($headers);
|
||||
|
||||
$this->assertCount(\count($headers), $headerBag);
|
||||
}
|
||||
}
|
||||
|
|
@ -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\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\IpUtils;
|
||||
|
||||
class IpUtilsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getIpv4Data
|
||||
*/
|
||||
public function testIpv4($matches, $remoteAddr, $cidr)
|
||||
{
|
||||
$this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
|
||||
}
|
||||
|
||||
public function getIpv4Data()
|
||||
{
|
||||
return array(
|
||||
array(true, '192.168.1.1', '192.168.1.1'),
|
||||
array(true, '192.168.1.1', '192.168.1.1/1'),
|
||||
array(true, '192.168.1.1', '192.168.1.0/24'),
|
||||
array(false, '192.168.1.1', '1.2.3.4/1'),
|
||||
array(false, '192.168.1.1', '192.168.1.1/33'), // invalid subnet
|
||||
array(true, '192.168.1.1', array('1.2.3.4/1', '192.168.1.0/24')),
|
||||
array(true, '192.168.1.1', array('192.168.1.0/24', '1.2.3.4/1')),
|
||||
array(false, '192.168.1.1', array('1.2.3.4/1', '4.3.2.1/1')),
|
||||
array(true, '1.2.3.4', '0.0.0.0/0'),
|
||||
array(true, '1.2.3.4', '192.168.1.0/0'),
|
||||
array(false, '1.2.3.4', '256.256.256/0'), // invalid CIDR notation
|
||||
array(false, 'an_invalid_ip', '192.168.1.0/24'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getIpv6Data
|
||||
*/
|
||||
public function testIpv6($matches, $remoteAddr, $cidr)
|
||||
{
|
||||
if (!\defined('AF_INET6')) {
|
||||
$this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".');
|
||||
}
|
||||
|
||||
$this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
|
||||
}
|
||||
|
||||
public function getIpv6Data()
|
||||
{
|
||||
return array(
|
||||
array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
|
||||
array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
|
||||
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
|
||||
array(true, '0:0:0:0:0:0:0:1', '::1'),
|
||||
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
|
||||
array(true, '0:0:603:0:396e:4789:8e99:0001', '::/0'),
|
||||
array(true, '0:0:603:0:396e:4789:8e99:0001', '2a01:198:603:0::/0'),
|
||||
array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '2a01:198:603:0::/65')),
|
||||
array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('2a01:198:603:0::/65', '::1')),
|
||||
array(false, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '1a01:198:603:0::/65')),
|
||||
array(false, '}__test|O:21:"JDatabaseDriverMysqli":3:{s:2', '::1'),
|
||||
array(false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @requires extension sockets
|
||||
*/
|
||||
public function testAnIpv6WithOptionDisabledIpv6()
|
||||
{
|
||||
if (\defined('AF_INET6')) {
|
||||
$this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
|
||||
}
|
||||
|
||||
IpUtils::checkIp('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidIpAddressData
|
||||
*/
|
||||
public function testInvalidIpAddressesDoNotMatch($requestIp, $proxyIp)
|
||||
{
|
||||
$this->assertFalse(IpUtils::checkIp4($requestIp, $proxyIp));
|
||||
}
|
||||
|
||||
public function invalidIpAddressData()
|
||||
{
|
||||
return array(
|
||||
'invalid proxy wildcard' => array('192.168.20.13', '*'),
|
||||
'invalid proxy missing netmask' => array('192.168.20.13', '0.0.0.0'),
|
||||
'invalid request IP with invalid proxy wildcard' => array('0.0.0.0', '*'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,266 +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\JsonResponse;
|
||||
|
||||
class JsonResponseTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
if (!\defined('HHVM_VERSION')) {
|
||||
$this->iniSet('serialize_precision', 14);
|
||||
}
|
||||
}
|
||||
|
||||
public function testConstructorEmptyCreatesJsonObject()
|
||||
{
|
||||
$response = new JsonResponse();
|
||||
$this->assertSame('{}', $response->getContent());
|
||||
}
|
||||
|
||||
public function testConstructorWithArrayCreatesJsonArray()
|
||||
{
|
||||
$response = new JsonResponse(array(0, 1, 2, 3));
|
||||
$this->assertSame('[0,1,2,3]', $response->getContent());
|
||||
}
|
||||
|
||||
public function testConstructorWithAssocArrayCreatesJsonObject()
|
||||
{
|
||||
$response = new JsonResponse(array('foo' => 'bar'));
|
||||
$this->assertSame('{"foo":"bar"}', $response->getContent());
|
||||
}
|
||||
|
||||
public function testConstructorWithSimpleTypes()
|
||||
{
|
||||
$response = new JsonResponse('foo');
|
||||
$this->assertSame('"foo"', $response->getContent());
|
||||
|
||||
$response = new JsonResponse(0);
|
||||
$this->assertSame('0', $response->getContent());
|
||||
|
||||
$response = new JsonResponse(0.1);
|
||||
$this->assertSame('0.1', $response->getContent());
|
||||
|
||||
$response = new JsonResponse(true);
|
||||
$this->assertSame('true', $response->getContent());
|
||||
}
|
||||
|
||||
public function testConstructorWithCustomStatus()
|
||||
{
|
||||
$response = new JsonResponse(array(), 202);
|
||||
$this->assertSame(202, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testConstructorAddsContentTypeHeader()
|
||||
{
|
||||
$response = new JsonResponse();
|
||||
$this->assertSame('application/json', $response->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
public function testConstructorWithCustomHeaders()
|
||||
{
|
||||
$response = new JsonResponse(array(), 200, array('ETag' => 'foo'));
|
||||
$this->assertSame('application/json', $response->headers->get('Content-Type'));
|
||||
$this->assertSame('foo', $response->headers->get('ETag'));
|
||||
}
|
||||
|
||||
public function testConstructorWithCustomContentType()
|
||||
{
|
||||
$headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json');
|
||||
|
||||
$response = new JsonResponse(array(), 200, $headers);
|
||||
$this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
public function testSetJson()
|
||||
{
|
||||
$response = new JsonResponse('1', 200, array(), true);
|
||||
$this->assertEquals('1', $response->getContent());
|
||||
|
||||
$response = new JsonResponse('[1]', 200, array(), true);
|
||||
$this->assertEquals('[1]', $response->getContent());
|
||||
|
||||
$response = new JsonResponse(null, 200, array());
|
||||
$response->setJson('true');
|
||||
$this->assertEquals('true', $response->getContent());
|
||||
}
|
||||
|
||||
public function testCreate()
|
||||
{
|
||||
$response = JsonResponse::create(array('foo' => 'bar'), 204);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
|
||||
$this->assertEquals('{"foo":"bar"}', $response->getContent());
|
||||
$this->assertEquals(204, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testStaticCreateEmptyJsonObject()
|
||||
{
|
||||
$response = JsonResponse::create();
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
|
||||
$this->assertSame('{}', $response->getContent());
|
||||
}
|
||||
|
||||
public function testStaticCreateJsonArray()
|
||||
{
|
||||
$response = JsonResponse::create(array(0, 1, 2, 3));
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
|
||||
$this->assertSame('[0,1,2,3]', $response->getContent());
|
||||
}
|
||||
|
||||
public function testStaticCreateJsonObject()
|
||||
{
|
||||
$response = JsonResponse::create(array('foo' => 'bar'));
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
|
||||
$this->assertSame('{"foo":"bar"}', $response->getContent());
|
||||
}
|
||||
|
||||
public function testStaticCreateWithSimpleTypes()
|
||||
{
|
||||
$response = JsonResponse::create('foo');
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
|
||||
$this->assertSame('"foo"', $response->getContent());
|
||||
|
||||
$response = JsonResponse::create(0);
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
|
||||
$this->assertSame('0', $response->getContent());
|
||||
|
||||
$response = JsonResponse::create(0.1);
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
|
||||
$this->assertSame('0.1', $response->getContent());
|
||||
|
||||
$response = JsonResponse::create(true);
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
|
||||
$this->assertSame('true', $response->getContent());
|
||||
}
|
||||
|
||||
public function testStaticCreateWithCustomStatus()
|
||||
{
|
||||
$response = JsonResponse::create(array(), 202);
|
||||
$this->assertSame(202, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testStaticCreateAddsContentTypeHeader()
|
||||
{
|
||||
$response = JsonResponse::create();
|
||||
$this->assertSame('application/json', $response->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
public function testStaticCreateWithCustomHeaders()
|
||||
{
|
||||
$response = JsonResponse::create(array(), 200, array('ETag' => 'foo'));
|
||||
$this->assertSame('application/json', $response->headers->get('Content-Type'));
|
||||
$this->assertSame('foo', $response->headers->get('ETag'));
|
||||
}
|
||||
|
||||
public function testStaticCreateWithCustomContentType()
|
||||
{
|
||||
$headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json');
|
||||
|
||||
$response = JsonResponse::create(array(), 200, $headers);
|
||||
$this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
public function testSetCallback()
|
||||
{
|
||||
$response = JsonResponse::create(array('foo' => 'bar'))->setCallback('callback');
|
||||
|
||||
$this->assertEquals('/**/callback({"foo":"bar"});', $response->getContent());
|
||||
$this->assertEquals('text/javascript', $response->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
public function testJsonEncodeFlags()
|
||||
{
|
||||
$response = new JsonResponse('<>\'&"');
|
||||
|
||||
$this->assertEquals('"\u003C\u003E\u0027\u0026\u0022"', $response->getContent());
|
||||
}
|
||||
|
||||
public function testGetEncodingOptions()
|
||||
{
|
||||
$response = new JsonResponse();
|
||||
|
||||
$this->assertEquals(JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT, $response->getEncodingOptions());
|
||||
}
|
||||
|
||||
public function testSetEncodingOptions()
|
||||
{
|
||||
$response = new JsonResponse();
|
||||
$response->setData(array(array(1, 2, 3)));
|
||||
|
||||
$this->assertEquals('[[1,2,3]]', $response->getContent());
|
||||
|
||||
$response->setEncodingOptions(JSON_FORCE_OBJECT);
|
||||
|
||||
$this->assertEquals('{"0":{"0":1,"1":2,"2":3}}', $response->getContent());
|
||||
}
|
||||
|
||||
public function testItAcceptsJsonAsString()
|
||||
{
|
||||
$response = JsonResponse::fromJsonString('{"foo":"bar"}');
|
||||
$this->assertSame('{"foo":"bar"}', $response->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testSetCallbackInvalidIdentifier()
|
||||
{
|
||||
$response = new JsonResponse('foo');
|
||||
$response->setCallback('+invalid');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testSetContent()
|
||||
{
|
||||
JsonResponse::create("\xB1\x31");
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage This error is expected
|
||||
*/
|
||||
public function testSetContentJsonSerializeError()
|
||||
{
|
||||
if (!interface_exists('JsonSerializable', false)) {
|
||||
$this->markTestSkipped('JsonSerializable is required.');
|
||||
}
|
||||
|
||||
$serializable = new JsonSerializableObject();
|
||||
|
||||
JsonResponse::create($serializable);
|
||||
}
|
||||
|
||||
public function testSetComplexCallback()
|
||||
{
|
||||
$response = JsonResponse::create(array('foo' => 'bar'));
|
||||
$response->setCallback('ಠ_ಠ["foo"].bar[0]');
|
||||
|
||||
$this->assertEquals('/**/ಠ_ಠ["foo"].bar[0]({"foo":"bar"});', $response->getContent());
|
||||
}
|
||||
}
|
||||
|
||||
if (interface_exists('JsonSerializable', false)) {
|
||||
class JsonSerializableObject implements \JsonSerializable
|
||||
{
|
||||
public function jsonSerialize()
|
||||
{
|
||||
throw new \Exception('This error is expected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,194 +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\ParameterBag;
|
||||
|
||||
class ParameterBagTest extends TestCase
|
||||
{
|
||||
public function testConstructor()
|
||||
{
|
||||
$this->testAll();
|
||||
}
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
$this->assertEquals(array('foo' => 'bar'), $bag->all(), '->all() gets all the input');
|
||||
}
|
||||
|
||||
public function testKeys()
|
||||
{
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
$this->assertEquals(array('foo'), $bag->keys());
|
||||
}
|
||||
|
||||
public function testAdd()
|
||||
{
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
$bag->add(array('bar' => 'bas'));
|
||||
$this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all());
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
{
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
$bag->add(array('bar' => 'bas'));
|
||||
$this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all());
|
||||
$bag->remove('bar');
|
||||
$this->assertEquals(array('foo' => 'bar'), $bag->all());
|
||||
}
|
||||
|
||||
public function testReplace()
|
||||
{
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
|
||||
$bag->replace(array('FOO' => 'BAR'));
|
||||
$this->assertEquals(array('FOO' => 'BAR'), $bag->all(), '->replace() replaces the input with the argument');
|
||||
$this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input');
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$bag = new ParameterBag(array('foo' => 'bar', 'null' => null));
|
||||
|
||||
$this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter');
|
||||
$this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined');
|
||||
$this->assertNull($bag->get('null', 'default'), '->get() returns null if null is set');
|
||||
}
|
||||
|
||||
public function testGetDoesNotUseDeepByDefault()
|
||||
{
|
||||
$bag = new ParameterBag(array('foo' => array('bar' => 'moo')));
|
||||
|
||||
$this->assertNull($bag->get('foo[bar]'));
|
||||
}
|
||||
|
||||
public function testSet()
|
||||
{
|
||||
$bag = new ParameterBag(array());
|
||||
|
||||
$bag->set('foo', 'bar');
|
||||
$this->assertEquals('bar', $bag->get('foo'), '->set() sets the value of parameter');
|
||||
|
||||
$bag->set('foo', 'baz');
|
||||
$this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter');
|
||||
}
|
||||
|
||||
public function testHas()
|
||||
{
|
||||
$bag = new ParameterBag(array('foo' => 'bar'));
|
||||
|
||||
$this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined');
|
||||
$this->assertFalse($bag->has('unknown'), '->has() return false if a parameter is not defined');
|
||||
}
|
||||
|
||||
public function testGetAlpha()
|
||||
{
|
||||
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
|
||||
|
||||
$this->assertEquals('fooBAR', $bag->getAlpha('word'), '->getAlpha() gets only alphabetic characters');
|
||||
$this->assertEquals('', $bag->getAlpha('unknown'), '->getAlpha() returns empty string if a parameter is not defined');
|
||||
}
|
||||
|
||||
public function testGetAlnum()
|
||||
{
|
||||
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
|
||||
|
||||
$this->assertEquals('fooBAR012', $bag->getAlnum('word'), '->getAlnum() gets only alphanumeric characters');
|
||||
$this->assertEquals('', $bag->getAlnum('unknown'), '->getAlnum() returns empty string if a parameter is not defined');
|
||||
}
|
||||
|
||||
public function testGetDigits()
|
||||
{
|
||||
$bag = new ParameterBag(array('word' => 'foo_BAR_012'));
|
||||
|
||||
$this->assertEquals('012', $bag->getDigits('word'), '->getDigits() gets only digits as string');
|
||||
$this->assertEquals('', $bag->getDigits('unknown'), '->getDigits() returns empty string if a parameter is not defined');
|
||||
}
|
||||
|
||||
public function testGetInt()
|
||||
{
|
||||
$bag = new ParameterBag(array('digits' => '0123'));
|
||||
|
||||
$this->assertEquals(123, $bag->getInt('digits'), '->getInt() gets a value of parameter as integer');
|
||||
$this->assertEquals(0, $bag->getInt('unknown'), '->getInt() returns zero if a parameter is not defined');
|
||||
}
|
||||
|
||||
public function testFilter()
|
||||
{
|
||||
$bag = new ParameterBag(array(
|
||||
'digits' => '0123ab',
|
||||
'email' => 'example@example.com',
|
||||
'url' => 'http://example.com/foo',
|
||||
'dec' => '256',
|
||||
'hex' => '0x100',
|
||||
'array' => array('bang'),
|
||||
));
|
||||
|
||||
$this->assertEmpty($bag->filter('nokey'), '->filter() should return empty by default if no key is found');
|
||||
|
||||
$this->assertEquals('0123', $bag->filter('digits', '', FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters');
|
||||
|
||||
$this->assertEquals('example@example.com', $bag->filter('email', '', FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email');
|
||||
|
||||
$this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, array('flags' => FILTER_FLAG_PATH_REQUIRED)), '->filter() gets a value of parameter as URL with a path');
|
||||
|
||||
// This test is repeated for code-coverage
|
||||
$this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as URL with a path');
|
||||
|
||||
$this->assertFalse($bag->filter('dec', '', FILTER_VALIDATE_INT, array(
|
||||
'flags' => FILTER_FLAG_ALLOW_HEX,
|
||||
'options' => array('min_range' => 1, 'max_range' => 0xff),
|
||||
)), '->filter() gets a value of parameter as integer between boundaries');
|
||||
|
||||
$this->assertFalse($bag->filter('hex', '', FILTER_VALIDATE_INT, array(
|
||||
'flags' => FILTER_FLAG_ALLOW_HEX,
|
||||
'options' => array('min_range' => 1, 'max_range' => 0xff),
|
||||
)), '->filter() gets a value of parameter as integer between boundaries');
|
||||
|
||||
$this->assertEquals(array('bang'), $bag->filter('array', ''), '->filter() gets a value of parameter as an array');
|
||||
}
|
||||
|
||||
public function testGetIterator()
|
||||
{
|
||||
$parameters = array('foo' => 'bar', 'hello' => 'world');
|
||||
$bag = new ParameterBag($parameters);
|
||||
|
||||
$i = 0;
|
||||
foreach ($bag as $key => $val) {
|
||||
++$i;
|
||||
$this->assertEquals($parameters[$key], $val);
|
||||
}
|
||||
|
||||
$this->assertEquals(\count($parameters), $i);
|
||||
}
|
||||
|
||||
public function testCount()
|
||||
{
|
||||
$parameters = array('foo' => 'bar', 'hello' => 'world');
|
||||
$bag = new ParameterBag($parameters);
|
||||
|
||||
$this->assertCount(\count($parameters), $bag);
|
||||
}
|
||||
|
||||
public function testGetBoolean()
|
||||
{
|
||||
$parameters = array('string_true' => 'true', 'string_false' => 'false');
|
||||
$bag = new ParameterBag($parameters);
|
||||
|
||||
$this->assertTrue($bag->getBoolean('string_true'), '->getBoolean() gets the string true as boolean true');
|
||||
$this->assertFalse($bag->getBoolean('string_false'), '->getBoolean() gets the string false as boolean false');
|
||||
$this->assertFalse($bag->getBoolean('unknown'), '->getBoolean() returns false if a parameter is not defined');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,97 +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\RedirectResponse;
|
||||
|
||||
class RedirectResponseTest extends TestCase
|
||||
{
|
||||
public function testGenerateMetaRedirect()
|
||||
{
|
||||
$response = new RedirectResponse('foo.bar');
|
||||
|
||||
$this->assertEquals(1, preg_match(
|
||||
'#<meta http-equiv="refresh" content="\d+;url=foo\.bar" />#',
|
||||
preg_replace(array('/\s+/', '/\'/'), array(' ', '"'), $response->getContent())
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testRedirectResponseConstructorNullUrl()
|
||||
{
|
||||
$response = new RedirectResponse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testRedirectResponseConstructorWrongStatusCode()
|
||||
{
|
||||
$response = new RedirectResponse('foo.bar', 404);
|
||||
}
|
||||
|
||||
public function testGenerateLocationHeader()
|
||||
{
|
||||
$response = new RedirectResponse('foo.bar');
|
||||
|
||||
$this->assertTrue($response->headers->has('Location'));
|
||||
$this->assertEquals('foo.bar', $response->headers->get('Location'));
|
||||
}
|
||||
|
||||
public function testGetTargetUrl()
|
||||
{
|
||||
$response = new RedirectResponse('foo.bar');
|
||||
|
||||
$this->assertEquals('foo.bar', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public function testSetTargetUrl()
|
||||
{
|
||||
$response = new RedirectResponse('foo.bar');
|
||||
$response->setTargetUrl('baz.beep');
|
||||
|
||||
$this->assertEquals('baz.beep', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testSetTargetUrlNull()
|
||||
{
|
||||
$response = new RedirectResponse('foo.bar');
|
||||
$response->setTargetUrl(null);
|
||||
}
|
||||
|
||||
public function testCreate()
|
||||
{
|
||||
$response = RedirectResponse::create('foo', 301);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
|
||||
$this->assertEquals(301, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testCacheHeaders()
|
||||
{
|
||||
$response = new RedirectResponse('foo.bar', 301);
|
||||
$this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
|
||||
|
||||
$response = new RedirectResponse('foo.bar', 301, array('cache-control' => 'max-age=86400'));
|
||||
$this->assertFalse($response->headers->hasCacheControlDirective('no-cache'));
|
||||
$this->assertTrue($response->headers->hasCacheControlDirective('max-age'));
|
||||
|
||||
$response = new RedirectResponse('foo.bar', 302);
|
||||
$this->assertTrue($response->headers->hasCacheControlDirective('no-cache'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,151 +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\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestMatcher;
|
||||
|
||||
class RequestMatcherTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getMethodData
|
||||
*/
|
||||
public function testMethod($requestMethod, $matcherMethod, $isMatch)
|
||||
{
|
||||
$matcher = new RequestMatcher();
|
||||
$matcher->matchMethod($matcherMethod);
|
||||
$request = Request::create('', $requestMethod);
|
||||
$this->assertSame($isMatch, $matcher->matches($request));
|
||||
|
||||
$matcher = new RequestMatcher(null, null, $matcherMethod);
|
||||
$request = Request::create('', $requestMethod);
|
||||
$this->assertSame($isMatch, $matcher->matches($request));
|
||||
}
|
||||
|
||||
public function getMethodData()
|
||||
{
|
||||
return array(
|
||||
array('get', 'get', true),
|
||||
array('get', array('get', 'post'), true),
|
||||
array('get', 'post', false),
|
||||
array('get', 'GET', true),
|
||||
array('get', array('GET', 'POST'), true),
|
||||
array('get', 'POST', false),
|
||||
);
|
||||
}
|
||||
|
||||
public function testScheme()
|
||||
{
|
||||
$httpRequest = $request = $request = Request::create('');
|
||||
$httpsRequest = $request = $request = Request::create('', 'get', array(), array(), array(), array('HTTPS' => 'on'));
|
||||
|
||||
$matcher = new RequestMatcher();
|
||||
$matcher->matchScheme('https');
|
||||
$this->assertFalse($matcher->matches($httpRequest));
|
||||
$this->assertTrue($matcher->matches($httpsRequest));
|
||||
|
||||
$matcher->matchScheme('http');
|
||||
$this->assertFalse($matcher->matches($httpsRequest));
|
||||
$this->assertTrue($matcher->matches($httpRequest));
|
||||
|
||||
$matcher = new RequestMatcher();
|
||||
$this->assertTrue($matcher->matches($httpsRequest));
|
||||
$this->assertTrue($matcher->matches($httpRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getHostData
|
||||
*/
|
||||
public function testHost($pattern, $isMatch)
|
||||
{
|
||||
$matcher = new RequestMatcher();
|
||||
$request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com'));
|
||||
|
||||
$matcher->matchHost($pattern);
|
||||
$this->assertSame($isMatch, $matcher->matches($request));
|
||||
|
||||
$matcher = new RequestMatcher(null, $pattern);
|
||||
$this->assertSame($isMatch, $matcher->matches($request));
|
||||
}
|
||||
|
||||
public function getHostData()
|
||||
{
|
||||
return array(
|
||||
array('.*\.example\.com', true),
|
||||
array('\.example\.com$', true),
|
||||
array('^.*\.example\.com$', true),
|
||||
array('.*\.sensio\.com', false),
|
||||
array('.*\.example\.COM', true),
|
||||
array('\.example\.COM$', true),
|
||||
array('^.*\.example\.COM$', true),
|
||||
array('.*\.sensio\.COM', false),
|
||||
);
|
||||
}
|
||||
|
||||
public function testPath()
|
||||
{
|
||||
$matcher = new RequestMatcher();
|
||||
|
||||
$request = Request::create('/admin/foo');
|
||||
|
||||
$matcher->matchPath('/admin/.*');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
|
||||
$matcher->matchPath('/admin');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
|
||||
$matcher->matchPath('^/admin/.*$');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
|
||||
$matcher->matchMethod('/blog/.*');
|
||||
$this->assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testPathWithLocaleIsNotSupported()
|
||||
{
|
||||
$matcher = new RequestMatcher();
|
||||
$request = Request::create('/en/login');
|
||||
$request->setLocale('en');
|
||||
|
||||
$matcher->matchPath('^/{_locale}/login$');
|
||||
$this->assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testPathWithEncodedCharacters()
|
||||
{
|
||||
$matcher = new RequestMatcher();
|
||||
$request = Request::create('/admin/fo%20o');
|
||||
$matcher->matchPath('^/admin/fo o*$');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testAttributes()
|
||||
{
|
||||
$matcher = new RequestMatcher();
|
||||
|
||||
$request = Request::create('/admin/foo');
|
||||
$request->attributes->set('foo', 'foo_bar');
|
||||
|
||||
$matcher->matchAttribute('foo', 'foo_.*');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
|
||||
$matcher->matchAttribute('foo', 'foo');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
|
||||
$matcher->matchAttribute('foo', '^foo_bar$');
|
||||
$this->assertTrue($matcher->matches($request));
|
||||
|
||||
$matcher->matchAttribute('foo', 'babar');
|
||||
$this->assertFalse($matcher->matches($request));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +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\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
class RequestStackTest extends TestCase
|
||||
{
|
||||
public function testGetCurrentRequest()
|
||||
{
|
||||
$requestStack = new RequestStack();
|
||||
$this->assertNull($requestStack->getCurrentRequest());
|
||||
|
||||
$request = Request::create('/foo');
|
||||
|
||||
$requestStack->push($request);
|
||||
$this->assertSame($request, $requestStack->getCurrentRequest());
|
||||
|
||||
$this->assertSame($request, $requestStack->pop());
|
||||
$this->assertNull($requestStack->getCurrentRequest());
|
||||
|
||||
$this->assertNull($requestStack->pop());
|
||||
}
|
||||
|
||||
public function testGetMasterRequest()
|
||||
{
|
||||
$requestStack = new RequestStack();
|
||||
$this->assertNull($requestStack->getMasterRequest());
|
||||
|
||||
$masterRequest = Request::create('/foo');
|
||||
$subRequest = Request::create('/bar');
|
||||
|
||||
$requestStack->push($masterRequest);
|
||||
$requestStack->push($subRequest);
|
||||
|
||||
$this->assertSame($masterRequest, $requestStack->getMasterRequest());
|
||||
}
|
||||
|
||||
public function testGetParentRequest()
|
||||
{
|
||||
$requestStack = new RequestStack();
|
||||
$this->assertNull($requestStack->getParentRequest());
|
||||
|
||||
$masterRequest = Request::create('/foo');
|
||||
|
||||
$requestStack->push($masterRequest);
|
||||
$this->assertNull($requestStack->getParentRequest());
|
||||
|
||||
$firstSubRequest = Request::create('/bar');
|
||||
|
||||
$requestStack->push($firstSubRequest);
|
||||
$this->assertSame($masterRequest, $requestStack->getParentRequest());
|
||||
|
||||
$secondSubRequest = Request::create('/baz');
|
||||
|
||||
$requestStack->push($secondSubRequest);
|
||||
$this->assertSame($firstSubRequest, $requestStack->getParentRequest());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,363 +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;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class ResponseHeaderBagTest extends TestCase
|
||||
{
|
||||
public function testAllPreserveCase()
|
||||
{
|
||||
$headers = array(
|
||||
'fOo' => 'BAR',
|
||||
'ETag' => 'xyzzy',
|
||||
'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==',
|
||||
'P3P' => 'CP="CAO PSA OUR"',
|
||||
'WWW-Authenticate' => 'Basic realm="WallyWorld"',
|
||||
'X-UA-Compatible' => 'IE=edge,chrome=1',
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
);
|
||||
|
||||
$bag = new ResponseHeaderBag($headers);
|
||||
$allPreservedCase = $bag->allPreserveCase();
|
||||
|
||||
foreach (array_keys($headers) as $headerName) {
|
||||
$this->assertArrayHasKey($headerName, $allPreservedCase, '->allPreserveCase() gets all input keys in original case');
|
||||
}
|
||||
}
|
||||
|
||||
public function testCacheControlHeader()
|
||||
{
|
||||
$bag = new ResponseHeaderBag(array());
|
||||
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('Cache-Control' => 'public'));
|
||||
$this->assertEquals('public', $bag->get('Cache-Control'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('ETag' => 'abcde'));
|
||||
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('private'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('must-revalidate'));
|
||||
$this->assertFalse($bag->hasCacheControlDirective('max-age'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT'));
|
||||
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array(
|
||||
'Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT',
|
||||
'Cache-Control' => 'max-age=3600',
|
||||
));
|
||||
$this->assertEquals('max-age=3600, private', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('Last-Modified' => 'abcde'));
|
||||
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('Etag' => 'abcde', 'Last-Modified' => 'abcde'));
|
||||
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('cache-control' => 'max-age=100'));
|
||||
$this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('cache-control' => 's-maxage=100'));
|
||||
$this->assertEquals('s-maxage=100', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('cache-control' => 'private, max-age=100'));
|
||||
$this->assertEquals('max-age=100, private', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag(array('cache-control' => 'public, max-age=100'));
|
||||
$this->assertEquals('max-age=100, public', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag();
|
||||
$bag->set('Last-Modified', 'abcde');
|
||||
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag();
|
||||
$bag->set('Cache-Control', array('public', 'must-revalidate'));
|
||||
$this->assertCount(1, $bag->get('Cache-Control', null, false));
|
||||
$this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
|
||||
|
||||
$bag = new ResponseHeaderBag();
|
||||
$bag->set('Cache-Control', 'public');
|
||||
$bag->set('Cache-Control', 'must-revalidate', false);
|
||||
$this->assertCount(1, $bag->get('Cache-Control', null, false));
|
||||
$this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
|
||||
}
|
||||
|
||||
public function testCacheControlClone()
|
||||
{
|
||||
$headers = array('foo' => 'bar');
|
||||
$bag1 = new ResponseHeaderBag($headers);
|
||||
$bag2 = new ResponseHeaderBag($bag1->allPreserveCase());
|
||||
$this->assertEquals($bag1->allPreserveCase(), $bag2->allPreserveCase());
|
||||
}
|
||||
|
||||
public function testToStringIncludesCookieHeaders()
|
||||
{
|
||||
$bag = new ResponseHeaderBag(array());
|
||||
$bag->setCookie(new Cookie('foo', 'bar'));
|
||||
|
||||
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
|
||||
|
||||
$bag->clearCookie('foo');
|
||||
|
||||
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; httponly', $bag);
|
||||
}
|
||||
|
||||
public function testClearCookieSecureNotHttpOnly()
|
||||
{
|
||||
$bag = new ResponseHeaderBag(array());
|
||||
|
||||
$bag->clearCookie('foo', '/', null, true, false);
|
||||
|
||||
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag);
|
||||
}
|
||||
|
||||
public function testReplace()
|
||||
{
|
||||
$bag = new ResponseHeaderBag(array());
|
||||
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
|
||||
|
||||
$bag->replace(array('Cache-Control' => 'public'));
|
||||
$this->assertEquals('public', $bag->get('Cache-Control'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('public'));
|
||||
}
|
||||
|
||||
public function testReplaceWithRemove()
|
||||
{
|
||||
$bag = new ResponseHeaderBag(array());
|
||||
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
|
||||
|
||||
$bag->remove('Cache-Control');
|
||||
$bag->replace(array());
|
||||
$this->assertEquals('no-cache, private', $bag->get('Cache-Control'));
|
||||
$this->assertTrue($bag->hasCacheControlDirective('no-cache'));
|
||||
}
|
||||
|
||||
public function testCookiesWithSameNames()
|
||||
{
|
||||
$bag = new ResponseHeaderBag();
|
||||
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
|
||||
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar'));
|
||||
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo'));
|
||||
$bag->setCookie(new Cookie('foo', 'bar'));
|
||||
|
||||
$this->assertCount(4, $bag->getCookies());
|
||||
$this->assertEquals('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag->get('set-cookie'));
|
||||
$this->assertEquals(array(
|
||||
'foo=bar; path=/path/foo; domain=foo.bar; httponly',
|
||||
'foo=bar; path=/path/bar; domain=foo.bar; httponly',
|
||||
'foo=bar; path=/path/bar; domain=bar.foo; httponly',
|
||||
'foo=bar; path=/; httponly',
|
||||
), $bag->get('set-cookie', null, false));
|
||||
|
||||
$this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag);
|
||||
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly', $bag);
|
||||
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly', $bag);
|
||||
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
|
||||
|
||||
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||
|
||||
$this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/foo']);
|
||||
$this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/bar']);
|
||||
$this->assertArrayHasKey('foo', $cookies['bar.foo']['/path/bar']);
|
||||
$this->assertArrayHasKey('foo', $cookies['']['/']);
|
||||
}
|
||||
|
||||
public function testRemoveCookie()
|
||||
{
|
||||
$bag = new ResponseHeaderBag();
|
||||
$this->assertFalse($bag->has('set-cookie'));
|
||||
|
||||
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
|
||||
$bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar'));
|
||||
$this->assertTrue($bag->has('set-cookie'));
|
||||
|
||||
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||
$this->assertArrayHasKey('/path/foo', $cookies['foo.bar']);
|
||||
|
||||
$bag->removeCookie('foo', '/path/foo', 'foo.bar');
|
||||
$this->assertTrue($bag->has('set-cookie'));
|
||||
|
||||
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||
$this->assertArrayNotHasKey('/path/foo', $cookies['foo.bar']);
|
||||
|
||||
$bag->removeCookie('bar', '/path/bar', 'foo.bar');
|
||||
$this->assertFalse($bag->has('set-cookie'));
|
||||
|
||||
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||
$this->assertArrayNotHasKey('foo.bar', $cookies);
|
||||
}
|
||||
|
||||
public function testRemoveCookieWithNullRemove()
|
||||
{
|
||||
$bag = new ResponseHeaderBag();
|
||||
$bag->setCookie(new Cookie('foo', 'bar', 0));
|
||||
$bag->setCookie(new Cookie('bar', 'foo', 0));
|
||||
|
||||
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||
$this->assertArrayHasKey('/', $cookies['']);
|
||||
|
||||
$bag->removeCookie('foo', null);
|
||||
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||
$this->assertArrayNotHasKey('foo', $cookies['']['/']);
|
||||
|
||||
$bag->removeCookie('bar', null);
|
||||
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
|
||||
$this->assertFalse(isset($cookies['']['/']['bar']));
|
||||
}
|
||||
|
||||
public function testSetCookieHeader()
|
||||
{
|
||||
$bag = new ResponseHeaderBag();
|
||||
$bag->set('set-cookie', 'foo=bar');
|
||||
$this->assertEquals(array(new Cookie('foo', 'bar', 0, '/', null, false, false, true)), $bag->getCookies());
|
||||
|
||||
$bag->set('set-cookie', 'foo2=bar2', false);
|
||||
$this->assertEquals(array(
|
||||
new Cookie('foo', 'bar', 0, '/', null, false, false, true),
|
||||
new Cookie('foo2', 'bar2', 0, '/', null, false, false, true),
|
||||
), $bag->getCookies());
|
||||
|
||||
$bag->remove('set-cookie');
|
||||
$this->assertEquals(array(), $bag->getCookies());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testGetCookiesWithInvalidArgument()
|
||||
{
|
||||
$bag = new ResponseHeaderBag();
|
||||
|
||||
$bag->getCookies('invalid_argument');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testMakeDispositionInvalidDisposition()
|
||||
{
|
||||
$headers = new ResponseHeaderBag();
|
||||
|
||||
$headers->makeDisposition('invalid', 'foo.html');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMakeDisposition
|
||||
*/
|
||||
public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
|
||||
{
|
||||
$headers = new ResponseHeaderBag();
|
||||
|
||||
$this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback));
|
||||
}
|
||||
|
||||
public function testToStringDoesntMessUpHeaders()
|
||||
{
|
||||
$headers = new ResponseHeaderBag();
|
||||
|
||||
$headers->set('Location', 'http://www.symfony.com');
|
||||
$headers->set('Content-type', 'text/html');
|
||||
|
||||
(string) $headers;
|
||||
|
||||
$allHeaders = $headers->allPreserveCase();
|
||||
$this->assertEquals(array('http://www.symfony.com'), $allHeaders['Location']);
|
||||
$this->assertEquals(array('text/html'), $allHeaders['Content-type']);
|
||||
}
|
||||
|
||||
public function provideMakeDisposition()
|
||||
{
|
||||
return array(
|
||||
array('attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'),
|
||||
array('attachment', 'foo.html', '', 'attachment; filename="foo.html"'),
|
||||
array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'),
|
||||
array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'),
|
||||
array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'),
|
||||
array('attachment', 'föö.html', 'foo.html', 'attachment; filename="foo.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMakeDispositionFail
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testMakeDispositionFail($disposition, $filename)
|
||||
{
|
||||
$headers = new ResponseHeaderBag();
|
||||
|
||||
$headers->makeDisposition($disposition, $filename);
|
||||
}
|
||||
|
||||
public function provideMakeDispositionFail()
|
||||
{
|
||||
return array(
|
||||
array('attachment', 'foo%20bar.html'),
|
||||
array('attachment', 'foo/bar.html'),
|
||||
array('attachment', '/foo.html'),
|
||||
array('attachment', 'foo\bar.html'),
|
||||
array('attachment', '\foo.html'),
|
||||
array('attachment', 'föö.html'),
|
||||
);
|
||||
}
|
||||
|
||||
public function testDateHeaderAddedOnCreation()
|
||||
{
|
||||
$now = time();
|
||||
|
||||
$bag = new ResponseHeaderBag();
|
||||
$this->assertTrue($bag->has('Date'));
|
||||
|
||||
$this->assertEquals($now, $bag->getDate('Date')->getTimestamp());
|
||||
}
|
||||
|
||||
public function testDateHeaderCanBeSetOnCreation()
|
||||
{
|
||||
$someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
|
||||
$bag = new ResponseHeaderBag(array('Date' => $someDate));
|
||||
|
||||
$this->assertEquals($someDate, $bag->get('Date'));
|
||||
}
|
||||
|
||||
public function testDateHeaderWillBeRecreatedWhenRemoved()
|
||||
{
|
||||
$someDate = 'Thu, 23 Mar 2017 09:15:12 GMT';
|
||||
$bag = new ResponseHeaderBag(array('Date' => $someDate));
|
||||
$bag->remove('Date');
|
||||
|
||||
// a (new) Date header is still present
|
||||
$this->assertTrue($bag->has('Date'));
|
||||
$this->assertNotEquals($someDate, $bag->get('Date'));
|
||||
}
|
||||
|
||||
public function testDateHeaderWillBeRecreatedWhenHeadersAreReplaced()
|
||||
{
|
||||
$bag = new ResponseHeaderBag();
|
||||
$bag->replace(array());
|
||||
|
||||
$this->assertTrue($bag->has('Date'));
|
||||
}
|
||||
|
||||
private function assertSetCookieHeader($expected, ResponseHeaderBag $actual)
|
||||
{
|
||||
$this->assertRegExp('#^Set-Cookie:\s+'.preg_quote($expected, '#').'$#m', str_replace("\r\n", "\n", (string) $actual));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
abstract class ResponseTestCase extends TestCase
|
||||
{
|
||||
public function testNoCacheControlHeaderOnAttachmentUsingHTTPSAndMSIE()
|
||||
{
|
||||
// Check for HTTPS and IE 8
|
||||
$request = new Request();
|
||||
$request->server->set('HTTPS', true);
|
||||
$request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)');
|
||||
|
||||
$response = $this->provideResponse();
|
||||
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertFalse($response->headers->has('Cache-Control'));
|
||||
|
||||
// Check for IE 10 and HTTPS
|
||||
$request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)');
|
||||
|
||||
$response = $this->provideResponse();
|
||||
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertTrue($response->headers->has('Cache-Control'));
|
||||
|
||||
// Check for IE 9 and HTTPS
|
||||
$request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)');
|
||||
|
||||
$response = $this->provideResponse();
|
||||
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertTrue($response->headers->has('Cache-Control'));
|
||||
|
||||
// Check for IE 9 and HTTP
|
||||
$request->server->set('HTTPS', false);
|
||||
|
||||
$response = $this->provideResponse();
|
||||
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertTrue($response->headers->has('Cache-Control'));
|
||||
|
||||
// Check for IE 8 and HTTP
|
||||
$request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)');
|
||||
|
||||
$response = $this->provideResponse();
|
||||
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertTrue($response->headers->has('Cache-Control'));
|
||||
|
||||
// Check for non-IE and HTTPS
|
||||
$request->server->set('HTTPS', true);
|
||||
$request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17');
|
||||
|
||||
$response = $this->provideResponse();
|
||||
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertTrue($response->headers->has('Cache-Control'));
|
||||
|
||||
// Check for non-IE and HTTP
|
||||
$request->server->set('HTTPS', false);
|
||||
|
||||
$response = $this->provideResponse();
|
||||
$response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"');
|
||||
$response->prepare($request);
|
||||
|
||||
$this->assertTrue($response->headers->has('Cache-Control'));
|
||||
}
|
||||
|
||||
abstract protected function provideResponse();
|
||||
}
|
||||
|
|
@ -1,170 +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\ServerBag;
|
||||
|
||||
/**
|
||||
* ServerBagTest.
|
||||
*
|
||||
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
|
||||
*/
|
||||
class ServerBagTest extends TestCase
|
||||
{
|
||||
public function testShouldExtractHeadersFromServerArray()
|
||||
{
|
||||
$server = array(
|
||||
'SOME_SERVER_VARIABLE' => 'value',
|
||||
'SOME_SERVER_VARIABLE2' => 'value',
|
||||
'ROOT' => 'value',
|
||||
'HTTP_CONTENT_TYPE' => 'text/html',
|
||||
'HTTP_CONTENT_LENGTH' => '0',
|
||||
'HTTP_ETAG' => 'asdf',
|
||||
'PHP_AUTH_USER' => 'foo',
|
||||
'PHP_AUTH_PW' => 'bar',
|
||||
);
|
||||
|
||||
$bag = new ServerBag($server);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'CONTENT_TYPE' => 'text/html',
|
||||
'CONTENT_LENGTH' => '0',
|
||||
'ETAG' => 'asdf',
|
||||
'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
|
||||
'PHP_AUTH_USER' => 'foo',
|
||||
'PHP_AUTH_PW' => 'bar',
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
public function testHttpPasswordIsOptional()
|
||||
{
|
||||
$bag = new ServerBag(array('PHP_AUTH_USER' => 'foo'));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
|
||||
'PHP_AUTH_USER' => 'foo',
|
||||
'PHP_AUTH_PW' => '',
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
public function testHttpBasicAuthWithPhpCgi()
|
||||
{
|
||||
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar')));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'),
|
||||
'PHP_AUTH_USER' => 'foo',
|
||||
'PHP_AUTH_PW' => 'bar',
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
public function testHttpBasicAuthWithPhpCgiBogus()
|
||||
{
|
||||
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic_'.base64_encode('foo:bar')));
|
||||
|
||||
// Username and passwords should not be set as the header is bogus
|
||||
$headers = $bag->getHeaders();
|
||||
$this->assertArrayNotHasKey('PHP_AUTH_USER', $headers);
|
||||
$this->assertArrayNotHasKey('PHP_AUTH_PW', $headers);
|
||||
}
|
||||
|
||||
public function testHttpBasicAuthWithPhpCgiRedirect()
|
||||
{
|
||||
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word')));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'),
|
||||
'PHP_AUTH_USER' => 'username',
|
||||
'PHP_AUTH_PW' => 'pass:word',
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
public function testHttpBasicAuthWithPhpCgiEmptyPassword()
|
||||
{
|
||||
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:')));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => 'Basic '.base64_encode('foo:'),
|
||||
'PHP_AUTH_USER' => 'foo',
|
||||
'PHP_AUTH_PW' => '',
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
public function testHttpDigestAuthWithPhpCgi()
|
||||
{
|
||||
$digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
|
||||
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => $digest,
|
||||
'PHP_AUTH_DIGEST' => $digest,
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
public function testHttpDigestAuthWithPhpCgiBogus()
|
||||
{
|
||||
$digest = 'Digest_username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
|
||||
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest));
|
||||
|
||||
// Username and passwords should not be set as the header is bogus
|
||||
$headers = $bag->getHeaders();
|
||||
$this->assertArrayNotHasKey('PHP_AUTH_USER', $headers);
|
||||
$this->assertArrayNotHasKey('PHP_AUTH_PW', $headers);
|
||||
}
|
||||
|
||||
public function testHttpDigestAuthWithPhpCgiRedirect()
|
||||
{
|
||||
$digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"';
|
||||
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $digest));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => $digest,
|
||||
'PHP_AUTH_DIGEST' => $digest,
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
public function testOAuthBearerAuth()
|
||||
{
|
||||
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
|
||||
$bag = new ServerBag(array('HTTP_AUTHORIZATION' => $headerContent));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => $headerContent,
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
public function testOAuthBearerAuthWithRedirect()
|
||||
{
|
||||
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
|
||||
$bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $headerContent));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => $headerContent,
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/symfony/symfony/issues/17345
|
||||
*/
|
||||
public function testItDoesNotOverwriteTheAuthorizationHeaderIfItIsAlreadySet()
|
||||
{
|
||||
$headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo';
|
||||
$bag = new ServerBag(array('PHP_AUTH_USER' => 'foo', 'HTTP_AUTHORIZATION' => $headerContent));
|
||||
|
||||
$this->assertEquals(array(
|
||||
'AUTHORIZATION' => $headerContent,
|
||||
'PHP_AUTH_USER' => 'foo',
|
||||
'PHP_AUTH_PW' => '',
|
||||
), $bag->getHeaders());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||
|
||||
/**
|
||||
* Tests AttributeBag.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class AttributeBagTest extends TestCase
|
||||
{
|
||||
private $array = array();
|
||||
|
||||
/**
|
||||
* @var AttributeBag
|
||||
*/
|
||||
private $bag;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->array = array(
|
||||
'hello' => 'world',
|
||||
'always' => 'be happy',
|
||||
'user.login' => 'drak',
|
||||
'csrf.token' => array(
|
||||
'a' => '1234',
|
||||
'b' => '4321',
|
||||
),
|
||||
'category' => array(
|
||||
'fishing' => array(
|
||||
'first' => 'cod',
|
||||
'second' => 'sole',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->bag = new AttributeBag('_sf2');
|
||||
$this->bag->initialize($this->array);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->bag = null;
|
||||
$this->array = array();
|
||||
}
|
||||
|
||||
public function testInitialize()
|
||||
{
|
||||
$bag = new AttributeBag();
|
||||
$bag->initialize($this->array);
|
||||
$this->assertEquals($this->array, $bag->all());
|
||||
$array = array('should' => 'change');
|
||||
$bag->initialize($array);
|
||||
$this->assertEquals($array, $bag->all());
|
||||
}
|
||||
|
||||
public function testGetStorageKey()
|
||||
{
|
||||
$this->assertEquals('_sf2', $this->bag->getStorageKey());
|
||||
$attributeBag = new AttributeBag('test');
|
||||
$this->assertEquals('test', $attributeBag->getStorageKey());
|
||||
}
|
||||
|
||||
public function testGetSetName()
|
||||
{
|
||||
$this->assertEquals('attributes', $this->bag->getName());
|
||||
$this->bag->setName('foo');
|
||||
$this->assertEquals('foo', $this->bag->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider attributesProvider
|
||||
*/
|
||||
public function testHas($key, $value, $exists)
|
||||
{
|
||||
$this->assertEquals($exists, $this->bag->has($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider attributesProvider
|
||||
*/
|
||||
public function testGet($key, $value, $expected)
|
||||
{
|
||||
$this->assertEquals($value, $this->bag->get($key));
|
||||
}
|
||||
|
||||
public function testGetDefaults()
|
||||
{
|
||||
$this->assertNull($this->bag->get('user2.login'));
|
||||
$this->assertEquals('default', $this->bag->get('user2.login', 'default'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider attributesProvider
|
||||
*/
|
||||
public function testSet($key, $value, $expected)
|
||||
{
|
||||
$this->bag->set($key, $value);
|
||||
$this->assertEquals($value, $this->bag->get($key));
|
||||
}
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$this->assertEquals($this->array, $this->bag->all());
|
||||
|
||||
$this->bag->set('hello', 'fabien');
|
||||
$array = $this->array;
|
||||
$array['hello'] = 'fabien';
|
||||
$this->assertEquals($array, $this->bag->all());
|
||||
}
|
||||
|
||||
public function testReplace()
|
||||
{
|
||||
$array = array();
|
||||
$array['name'] = 'jack';
|
||||
$array['foo.bar'] = 'beep';
|
||||
$this->bag->replace($array);
|
||||
$this->assertEquals($array, $this->bag->all());
|
||||
$this->assertNull($this->bag->get('hello'));
|
||||
$this->assertNull($this->bag->get('always'));
|
||||
$this->assertNull($this->bag->get('user.login'));
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
{
|
||||
$this->assertEquals('world', $this->bag->get('hello'));
|
||||
$this->bag->remove('hello');
|
||||
$this->assertNull($this->bag->get('hello'));
|
||||
|
||||
$this->assertEquals('be happy', $this->bag->get('always'));
|
||||
$this->bag->remove('always');
|
||||
$this->assertNull($this->bag->get('always'));
|
||||
|
||||
$this->assertEquals('drak', $this->bag->get('user.login'));
|
||||
$this->bag->remove('user.login');
|
||||
$this->assertNull($this->bag->get('user.login'));
|
||||
}
|
||||
|
||||
public function testClear()
|
||||
{
|
||||
$this->bag->clear();
|
||||
$this->assertEquals(array(), $this->bag->all());
|
||||
}
|
||||
|
||||
public function attributesProvider()
|
||||
{
|
||||
return array(
|
||||
array('hello', 'world', true),
|
||||
array('always', 'be happy', true),
|
||||
array('user.login', 'drak', true),
|
||||
array('csrf.token', array('a' => '1234', 'b' => '4321'), true),
|
||||
array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true),
|
||||
array('user2.login', null, false),
|
||||
array('never', null, false),
|
||||
array('bye', null, false),
|
||||
array('bye/for/now', null, false),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetIterator()
|
||||
{
|
||||
$i = 0;
|
||||
foreach ($this->bag as $key => $val) {
|
||||
$this->assertEquals($this->array[$key], $val);
|
||||
++$i;
|
||||
}
|
||||
|
||||
$this->assertEquals(\count($this->array), $i);
|
||||
}
|
||||
|
||||
public function testCount()
|
||||
{
|
||||
$this->assertCount(\count($this->array), $this->bag);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
|
||||
|
||||
/**
|
||||
* Tests NamespacedAttributeBag.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class NamespacedAttributeBagTest extends TestCase
|
||||
{
|
||||
private $array = array();
|
||||
|
||||
/**
|
||||
* @var NamespacedAttributeBag
|
||||
*/
|
||||
private $bag;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->array = array(
|
||||
'hello' => 'world',
|
||||
'always' => 'be happy',
|
||||
'user.login' => 'drak',
|
||||
'csrf.token' => array(
|
||||
'a' => '1234',
|
||||
'b' => '4321',
|
||||
),
|
||||
'category' => array(
|
||||
'fishing' => array(
|
||||
'first' => 'cod',
|
||||
'second' => 'sole',
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->bag = new NamespacedAttributeBag('_sf2', '/');
|
||||
$this->bag->initialize($this->array);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->bag = null;
|
||||
$this->array = array();
|
||||
}
|
||||
|
||||
public function testInitialize()
|
||||
{
|
||||
$bag = new NamespacedAttributeBag();
|
||||
$bag->initialize($this->array);
|
||||
$this->assertEquals($this->array, $this->bag->all());
|
||||
$array = array('should' => 'not stick');
|
||||
$bag->initialize($array);
|
||||
|
||||
// should have remained the same
|
||||
$this->assertEquals($this->array, $this->bag->all());
|
||||
}
|
||||
|
||||
public function testGetStorageKey()
|
||||
{
|
||||
$this->assertEquals('_sf2', $this->bag->getStorageKey());
|
||||
$attributeBag = new NamespacedAttributeBag('test');
|
||||
$this->assertEquals('test', $attributeBag->getStorageKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider attributesProvider
|
||||
*/
|
||||
public function testHas($key, $value, $exists)
|
||||
{
|
||||
$this->assertEquals($exists, $this->bag->has($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider attributesProvider
|
||||
*/
|
||||
public function testHasNoSideEffect($key, $value, $expected)
|
||||
{
|
||||
$expected = json_encode($this->bag->all());
|
||||
$this->bag->has($key);
|
||||
|
||||
$this->assertEquals($expected, json_encode($this->bag->all()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider attributesProvider
|
||||
*/
|
||||
public function testGet($key, $value, $expected)
|
||||
{
|
||||
$this->assertEquals($value, $this->bag->get($key));
|
||||
}
|
||||
|
||||
public function testGetDefaults()
|
||||
{
|
||||
$this->assertNull($this->bag->get('user2.login'));
|
||||
$this->assertEquals('default', $this->bag->get('user2.login', 'default'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider attributesProvider
|
||||
*/
|
||||
public function testGetNoSideEffect($key, $value, $expected)
|
||||
{
|
||||
$expected = json_encode($this->bag->all());
|
||||
$this->bag->get($key);
|
||||
|
||||
$this->assertEquals($expected, json_encode($this->bag->all()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider attributesProvider
|
||||
*/
|
||||
public function testSet($key, $value, $expected)
|
||||
{
|
||||
$this->bag->set($key, $value);
|
||||
$this->assertEquals($value, $this->bag->get($key));
|
||||
}
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$this->assertEquals($this->array, $this->bag->all());
|
||||
|
||||
$this->bag->set('hello', 'fabien');
|
||||
$array = $this->array;
|
||||
$array['hello'] = 'fabien';
|
||||
$this->assertEquals($array, $this->bag->all());
|
||||
}
|
||||
|
||||
public function testReplace()
|
||||
{
|
||||
$array = array();
|
||||
$array['name'] = 'jack';
|
||||
$array['foo.bar'] = 'beep';
|
||||
$this->bag->replace($array);
|
||||
$this->assertEquals($array, $this->bag->all());
|
||||
$this->assertNull($this->bag->get('hello'));
|
||||
$this->assertNull($this->bag->get('always'));
|
||||
$this->assertNull($this->bag->get('user.login'));
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
{
|
||||
$this->assertEquals('world', $this->bag->get('hello'));
|
||||
$this->bag->remove('hello');
|
||||
$this->assertNull($this->bag->get('hello'));
|
||||
|
||||
$this->assertEquals('be happy', $this->bag->get('always'));
|
||||
$this->bag->remove('always');
|
||||
$this->assertNull($this->bag->get('always'));
|
||||
|
||||
$this->assertEquals('drak', $this->bag->get('user.login'));
|
||||
$this->bag->remove('user.login');
|
||||
$this->assertNull($this->bag->get('user.login'));
|
||||
}
|
||||
|
||||
public function testRemoveExistingNamespacedAttribute()
|
||||
{
|
||||
$this->assertSame('cod', $this->bag->remove('category/fishing/first'));
|
||||
}
|
||||
|
||||
public function testRemoveNonexistingNamespacedAttribute()
|
||||
{
|
||||
$this->assertNull($this->bag->remove('foo/bar/baz'));
|
||||
}
|
||||
|
||||
public function testClear()
|
||||
{
|
||||
$this->bag->clear();
|
||||
$this->assertEquals(array(), $this->bag->all());
|
||||
}
|
||||
|
||||
public function attributesProvider()
|
||||
{
|
||||
return array(
|
||||
array('hello', 'world', true),
|
||||
array('always', 'be happy', true),
|
||||
array('user.login', 'drak', true),
|
||||
array('csrf.token', array('a' => '1234', 'b' => '4321'), true),
|
||||
array('csrf.token/a', '1234', true),
|
||||
array('csrf.token/b', '4321', true),
|
||||
array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true),
|
||||
array('category/fishing', array('first' => 'cod', 'second' => 'sole'), true),
|
||||
array('category/fishing/missing/first', null, false),
|
||||
array('category/fishing/first', 'cod', true),
|
||||
array('category/fishing/second', 'sole', true),
|
||||
array('category/fishing/missing/second', null, false),
|
||||
array('user2.login', null, false),
|
||||
array('never', null, false),
|
||||
array('bye', null, false),
|
||||
array('bye/for/now', null, false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Flash;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag as FlashBag;
|
||||
|
||||
/**
|
||||
* AutoExpireFlashBagTest.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class AutoExpireFlashBagTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag
|
||||
*/
|
||||
private $bag;
|
||||
|
||||
protected $array = array();
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->bag = new FlashBag();
|
||||
$this->array = array('new' => array('notice' => array('A previous flash message')));
|
||||
$this->bag->initialize($this->array);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->bag = null;
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testInitialize()
|
||||
{
|
||||
$bag = new FlashBag();
|
||||
$array = array('new' => array('notice' => array('A previous flash message')));
|
||||
$bag->initialize($array);
|
||||
$this->assertEquals(array('A previous flash message'), $bag->peek('notice'));
|
||||
$array = array('new' => array(
|
||||
'notice' => array('Something else'),
|
||||
'error' => array('a'),
|
||||
));
|
||||
$bag->initialize($array);
|
||||
$this->assertEquals(array('Something else'), $bag->peek('notice'));
|
||||
$this->assertEquals(array('a'), $bag->peek('error'));
|
||||
}
|
||||
|
||||
public function testGetStorageKey()
|
||||
{
|
||||
$this->assertEquals('_symfony_flashes', $this->bag->getStorageKey());
|
||||
$attributeBag = new FlashBag('test');
|
||||
$this->assertEquals('test', $attributeBag->getStorageKey());
|
||||
}
|
||||
|
||||
public function testGetSetName()
|
||||
{
|
||||
$this->assertEquals('flashes', $this->bag->getName());
|
||||
$this->bag->setName('foo');
|
||||
$this->assertEquals('foo', $this->bag->getName());
|
||||
}
|
||||
|
||||
public function testPeek()
|
||||
{
|
||||
$this->assertEquals(array(), $this->bag->peek('non_existing'));
|
||||
$this->assertEquals(array('default'), $this->bag->peek('non_existing', array('default')));
|
||||
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
|
||||
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
|
||||
}
|
||||
|
||||
public function testSet()
|
||||
{
|
||||
$this->bag->set('notice', 'Foo');
|
||||
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
|
||||
}
|
||||
|
||||
public function testHas()
|
||||
{
|
||||
$this->assertFalse($this->bag->has('nothing'));
|
||||
$this->assertTrue($this->bag->has('notice'));
|
||||
}
|
||||
|
||||
public function testKeys()
|
||||
{
|
||||
$this->assertEquals(array('notice'), $this->bag->keys());
|
||||
}
|
||||
|
||||
public function testPeekAll()
|
||||
{
|
||||
$array = array(
|
||||
'new' => array(
|
||||
'notice' => 'Foo',
|
||||
'error' => 'Bar',
|
||||
),
|
||||
);
|
||||
|
||||
$this->bag->initialize($array);
|
||||
$this->assertEquals(array(
|
||||
'notice' => 'Foo',
|
||||
'error' => 'Bar',
|
||||
), $this->bag->peekAll()
|
||||
);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'notice' => 'Foo',
|
||||
'error' => 'Bar',
|
||||
), $this->bag->peekAll()
|
||||
);
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$this->assertEquals(array(), $this->bag->get('non_existing'));
|
||||
$this->assertEquals(array('default'), $this->bag->get('non_existing', array('default')));
|
||||
$this->assertEquals(array('A previous flash message'), $this->bag->get('notice'));
|
||||
$this->assertEquals(array(), $this->bag->get('notice'));
|
||||
}
|
||||
|
||||
public function testSetAll()
|
||||
{
|
||||
$this->bag->setAll(array('a' => 'first', 'b' => 'second'));
|
||||
$this->assertFalse($this->bag->has('a'));
|
||||
$this->assertFalse($this->bag->has('b'));
|
||||
}
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$this->bag->set('notice', 'Foo');
|
||||
$this->bag->set('error', 'Bar');
|
||||
$this->assertEquals(array(
|
||||
'notice' => array('A previous flash message'),
|
||||
), $this->bag->all()
|
||||
);
|
||||
|
||||
$this->assertEquals(array(), $this->bag->all());
|
||||
}
|
||||
|
||||
public function testClear()
|
||||
{
|
||||
$this->assertEquals(array('notice' => array('A previous flash message')), $this->bag->clear());
|
||||
}
|
||||
|
||||
public function testDoNotRemoveTheNewFlashesWhenDisplayingTheExistingOnes()
|
||||
{
|
||||
$this->bag->add('success', 'Something');
|
||||
$this->bag->all();
|
||||
|
||||
$this->assertEquals(array('new' => array('success' => array('Something')), 'display' => array()), $this->array);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Flash;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||
|
||||
/**
|
||||
* FlashBagTest.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class FlashBagTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface
|
||||
*/
|
||||
private $bag;
|
||||
|
||||
protected $array = array();
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->bag = new FlashBag();
|
||||
$this->array = array('notice' => array('A previous flash message'));
|
||||
$this->bag->initialize($this->array);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->bag = null;
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testInitialize()
|
||||
{
|
||||
$bag = new FlashBag();
|
||||
$bag->initialize($this->array);
|
||||
$this->assertEquals($this->array, $bag->peekAll());
|
||||
$array = array('should' => array('change'));
|
||||
$bag->initialize($array);
|
||||
$this->assertEquals($array, $bag->peekAll());
|
||||
}
|
||||
|
||||
public function testGetStorageKey()
|
||||
{
|
||||
$this->assertEquals('_symfony_flashes', $this->bag->getStorageKey());
|
||||
$attributeBag = new FlashBag('test');
|
||||
$this->assertEquals('test', $attributeBag->getStorageKey());
|
||||
}
|
||||
|
||||
public function testGetSetName()
|
||||
{
|
||||
$this->assertEquals('flashes', $this->bag->getName());
|
||||
$this->bag->setName('foo');
|
||||
$this->assertEquals('foo', $this->bag->getName());
|
||||
}
|
||||
|
||||
public function testPeek()
|
||||
{
|
||||
$this->assertEquals(array(), $this->bag->peek('non_existing'));
|
||||
$this->assertEquals(array('default'), $this->bag->peek('not_existing', array('default')));
|
||||
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
|
||||
$this->assertEquals(array('A previous flash message'), $this->bag->peek('notice'));
|
||||
}
|
||||
|
||||
public function testAdd()
|
||||
{
|
||||
$tab = array('bar' => 'baz');
|
||||
$this->bag->add('string_message', 'lorem');
|
||||
$this->bag->add('object_message', new \stdClass());
|
||||
$this->bag->add('array_message', $tab);
|
||||
|
||||
$this->assertEquals(array('lorem'), $this->bag->get('string_message'));
|
||||
$this->assertEquals(array(new \stdClass()), $this->bag->get('object_message'));
|
||||
$this->assertEquals(array($tab), $this->bag->get('array_message'));
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$this->assertEquals(array(), $this->bag->get('non_existing'));
|
||||
$this->assertEquals(array('default'), $this->bag->get('not_existing', array('default')));
|
||||
$this->assertEquals(array('A previous flash message'), $this->bag->get('notice'));
|
||||
$this->assertEquals(array(), $this->bag->get('notice'));
|
||||
}
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$this->bag->set('notice', 'Foo');
|
||||
$this->bag->set('error', 'Bar');
|
||||
$this->assertEquals(array(
|
||||
'notice' => array('Foo'),
|
||||
'error' => array('Bar'), ), $this->bag->all()
|
||||
);
|
||||
|
||||
$this->assertEquals(array(), $this->bag->all());
|
||||
}
|
||||
|
||||
public function testSet()
|
||||
{
|
||||
$this->bag->set('notice', 'Foo');
|
||||
$this->bag->set('notice', 'Bar');
|
||||
$this->assertEquals(array('Bar'), $this->bag->peek('notice'));
|
||||
}
|
||||
|
||||
public function testHas()
|
||||
{
|
||||
$this->assertFalse($this->bag->has('nothing'));
|
||||
$this->assertTrue($this->bag->has('notice'));
|
||||
}
|
||||
|
||||
public function testKeys()
|
||||
{
|
||||
$this->assertEquals(array('notice'), $this->bag->keys());
|
||||
}
|
||||
|
||||
public function testSetAll()
|
||||
{
|
||||
$this->bag->add('one_flash', 'Foo');
|
||||
$this->bag->add('another_flash', 'Bar');
|
||||
$this->assertTrue($this->bag->has('one_flash'));
|
||||
$this->assertTrue($this->bag->has('another_flash'));
|
||||
$this->bag->setAll(array('unique_flash' => 'FooBar'));
|
||||
$this->assertFalse($this->bag->has('one_flash'));
|
||||
$this->assertFalse($this->bag->has('another_flash'));
|
||||
$this->assertSame(array('unique_flash' => 'FooBar'), $this->bag->all());
|
||||
$this->assertSame(array(), $this->bag->all());
|
||||
}
|
||||
|
||||
public function testPeekAll()
|
||||
{
|
||||
$this->bag->set('notice', 'Foo');
|
||||
$this->bag->set('error', 'Bar');
|
||||
$this->assertEquals(array(
|
||||
'notice' => array('Foo'),
|
||||
'error' => array('Bar'),
|
||||
), $this->bag->peekAll()
|
||||
);
|
||||
$this->assertTrue($this->bag->has('notice'));
|
||||
$this->assertTrue($this->bag->has('error'));
|
||||
$this->assertEquals(array(
|
||||
'notice' => array('Foo'),
|
||||
'error' => array('Bar'),
|
||||
), $this->bag->peekAll()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,242 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
|
||||
/**
|
||||
* SessionTest.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Robert Schönthal <seroscho@googlemail.com>
|
||||
* @author Drak <drak@zikula.org>
|
||||
*/
|
||||
class SessionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\Session\SessionInterface
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->storage = new MockArraySessionStorage();
|
||||
$this->session = new Session($this->storage, new AttributeBag(), new FlashBag());
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->storage = null;
|
||||
$this->session = null;
|
||||
}
|
||||
|
||||
public function testStart()
|
||||
{
|
||||
$this->assertEquals('', $this->session->getId());
|
||||
$this->assertTrue($this->session->start());
|
||||
$this->assertNotEquals('', $this->session->getId());
|
||||
}
|
||||
|
||||
public function testIsStarted()
|
||||
{
|
||||
$this->assertFalse($this->session->isStarted());
|
||||
$this->session->start();
|
||||
$this->assertTrue($this->session->isStarted());
|
||||
}
|
||||
|
||||
public function testSetId()
|
||||
{
|
||||
$this->assertEquals('', $this->session->getId());
|
||||
$this->session->setId('0123456789abcdef');
|
||||
$this->session->start();
|
||||
$this->assertEquals('0123456789abcdef', $this->session->getId());
|
||||
}
|
||||
|
||||
public function testSetName()
|
||||
{
|
||||
$this->assertEquals('MOCKSESSID', $this->session->getName());
|
||||
$this->session->setName('session.test.com');
|
||||
$this->session->start();
|
||||
$this->assertEquals('session.test.com', $this->session->getName());
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
// tests defaults
|
||||
$this->assertNull($this->session->get('foo'));
|
||||
$this->assertEquals(1, $this->session->get('foo', 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setProvider
|
||||
*/
|
||||
public function testSet($key, $value)
|
||||
{
|
||||
$this->session->set($key, $value);
|
||||
$this->assertEquals($value, $this->session->get($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setProvider
|
||||
*/
|
||||
public function testHas($key, $value)
|
||||
{
|
||||
$this->session->set($key, $value);
|
||||
$this->assertTrue($this->session->has($key));
|
||||
$this->assertFalse($this->session->has($key.'non_value'));
|
||||
}
|
||||
|
||||
public function testReplace()
|
||||
{
|
||||
$this->session->replace(array('happiness' => 'be good', 'symfony' => 'awesome'));
|
||||
$this->assertEquals(array('happiness' => 'be good', 'symfony' => 'awesome'), $this->session->all());
|
||||
$this->session->replace(array());
|
||||
$this->assertEquals(array(), $this->session->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setProvider
|
||||
*/
|
||||
public function testAll($key, $value, $result)
|
||||
{
|
||||
$this->session->set($key, $value);
|
||||
$this->assertEquals($result, $this->session->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setProvider
|
||||
*/
|
||||
public function testClear($key, $value)
|
||||
{
|
||||
$this->session->set('hi', 'fabien');
|
||||
$this->session->set($key, $value);
|
||||
$this->session->clear();
|
||||
$this->assertEquals(array(), $this->session->all());
|
||||
}
|
||||
|
||||
public function setProvider()
|
||||
{
|
||||
return array(
|
||||
array('foo', 'bar', array('foo' => 'bar')),
|
||||
array('foo.bar', 'too much beer', array('foo.bar' => 'too much beer')),
|
||||
array('great', 'symfony is great', array('great' => 'symfony is great')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider setProvider
|
||||
*/
|
||||
public function testRemove($key, $value)
|
||||
{
|
||||
$this->session->set('hi.world', 'have a nice day');
|
||||
$this->session->set($key, $value);
|
||||
$this->session->remove($key);
|
||||
$this->assertEquals(array('hi.world' => 'have a nice day'), $this->session->all());
|
||||
}
|
||||
|
||||
public function testInvalidate()
|
||||
{
|
||||
$this->session->set('invalidate', 123);
|
||||
$this->session->invalidate();
|
||||
$this->assertEquals(array(), $this->session->all());
|
||||
}
|
||||
|
||||
public function testMigrate()
|
||||
{
|
||||
$this->session->set('migrate', 321);
|
||||
$this->session->migrate();
|
||||
$this->assertEquals(321, $this->session->get('migrate'));
|
||||
}
|
||||
|
||||
public function testMigrateDestroy()
|
||||
{
|
||||
$this->session->set('migrate', 333);
|
||||
$this->session->migrate(true);
|
||||
$this->assertEquals(333, $this->session->get('migrate'));
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$this->session->start();
|
||||
$this->session->save();
|
||||
|
||||
$this->assertFalse($this->session->isStarted());
|
||||
}
|
||||
|
||||
public function testGetId()
|
||||
{
|
||||
$this->assertEquals('', $this->session->getId());
|
||||
$this->session->start();
|
||||
$this->assertNotEquals('', $this->session->getId());
|
||||
}
|
||||
|
||||
public function testGetFlashBag()
|
||||
{
|
||||
$this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface', $this->session->getFlashBag());
|
||||
}
|
||||
|
||||
public function testGetIterator()
|
||||
{
|
||||
$attributes = array('hello' => 'world', 'symfony' => 'rocks');
|
||||
foreach ($attributes as $key => $val) {
|
||||
$this->session->set($key, $val);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($this->session as $key => $val) {
|
||||
$this->assertEquals($attributes[$key], $val);
|
||||
++$i;
|
||||
}
|
||||
|
||||
$this->assertEquals(\count($attributes), $i);
|
||||
}
|
||||
|
||||
public function testGetCount()
|
||||
{
|
||||
$this->session->set('hello', 'world');
|
||||
$this->session->set('symfony', 'rocks');
|
||||
|
||||
$this->assertCount(2, $this->session);
|
||||
}
|
||||
|
||||
public function testGetMeta()
|
||||
{
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag());
|
||||
}
|
||||
|
||||
public function testIsEmpty()
|
||||
{
|
||||
$this->assertTrue($this->session->isEmpty());
|
||||
|
||||
$this->session->set('hello', 'world');
|
||||
$this->assertFalse($this->session->isEmpty());
|
||||
|
||||
$this->session->remove('hello');
|
||||
$this->assertTrue($this->session->isEmpty());
|
||||
|
||||
$flash = $this->session->getFlashBag();
|
||||
$flash->set('hello', 'world');
|
||||
$this->assertFalse($this->session->isEmpty());
|
||||
|
||||
$flash->get('hello');
|
||||
$this->assertTrue($this->session->isEmpty());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler;
|
||||
|
||||
/**
|
||||
* @requires extension memcache
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class MemcacheSessionHandlerTest extends TestCase
|
||||
{
|
||||
const PREFIX = 'prefix_';
|
||||
const TTL = 1000;
|
||||
|
||||
/**
|
||||
* @var MemcacheSessionHandler
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
protected $memcache;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
if (\defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcache class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
$this->memcache = $this->getMockBuilder('Memcache')->getMock();
|
||||
$this->storage = new MemcacheSessionHandler(
|
||||
$this->memcache,
|
||||
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
|
||||
);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->memcache = null;
|
||||
$this->storage = null;
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testOpenSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->open('', ''));
|
||||
}
|
||||
|
||||
public function testCloseSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->close());
|
||||
}
|
||||
|
||||
public function testReadSession()
|
||||
{
|
||||
$this->memcache
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with(self::PREFIX.'id')
|
||||
;
|
||||
|
||||
$this->assertEquals('', $this->storage->read('id'));
|
||||
}
|
||||
|
||||
public function testWriteSession()
|
||||
{
|
||||
$this->memcache
|
||||
->expects($this->once())
|
||||
->method('set')
|
||||
->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2))
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($this->storage->write('id', 'data'));
|
||||
}
|
||||
|
||||
public function testDestroySession()
|
||||
{
|
||||
$this->memcache
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->with(self::PREFIX.'id')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($this->storage->destroy('id'));
|
||||
}
|
||||
|
||||
public function testGcSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->gc(123));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getOptionFixtures
|
||||
*/
|
||||
public function testSupportedOptions($options, $supported)
|
||||
{
|
||||
try {
|
||||
new MemcacheSessionHandler($this->memcache, $options);
|
||||
$this->assertTrue($supported);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->assertFalse($supported);
|
||||
}
|
||||
}
|
||||
|
||||
public function getOptionFixtures()
|
||||
{
|
||||
return array(
|
||||
array(array('prefix' => 'session'), true),
|
||||
array(array('expiretime' => 100), true),
|
||||
array(array('prefix' => 'session', 'expiretime' => 200), true),
|
||||
array(array('expiretime' => 100, 'foo' => 'bar'), false),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetConnection()
|
||||
{
|
||||
$method = new \ReflectionMethod($this->storage, 'getMemcache');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertInstanceOf('\Memcache', $method->invoke($this->storage));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler;
|
||||
|
||||
/**
|
||||
* @requires extension memcached
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class MemcachedSessionHandlerTest extends TestCase
|
||||
{
|
||||
const PREFIX = 'prefix_';
|
||||
const TTL = 1000;
|
||||
|
||||
/**
|
||||
* @var MemcachedSessionHandler
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
protected $memcached;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
if (\defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('PHPUnit_MockObject cannot mock the Memcached class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
|
||||
if (version_compare(phpversion('memcached'), '2.2.0', '>=') && version_compare(phpversion('memcached'), '3.0.0b1', '<')) {
|
||||
$this->markTestSkipped('Tests can only be run with memcached extension 2.1.0 or lower, or 3.0.0b1 or higher');
|
||||
}
|
||||
|
||||
$this->memcached = $this->getMockBuilder('Memcached')->getMock();
|
||||
$this->storage = new MemcachedSessionHandler(
|
||||
$this->memcached,
|
||||
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
|
||||
);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->memcached = null;
|
||||
$this->storage = null;
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testOpenSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->open('', ''));
|
||||
}
|
||||
|
||||
public function testCloseSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->close());
|
||||
}
|
||||
|
||||
public function testReadSession()
|
||||
{
|
||||
$this->memcached
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with(self::PREFIX.'id')
|
||||
;
|
||||
|
||||
$this->assertEquals('', $this->storage->read('id'));
|
||||
}
|
||||
|
||||
public function testWriteSession()
|
||||
{
|
||||
$this->memcached
|
||||
->expects($this->once())
|
||||
->method('set')
|
||||
->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2))
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($this->storage->write('id', 'data'));
|
||||
}
|
||||
|
||||
public function testDestroySession()
|
||||
{
|
||||
$this->memcached
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->with(self::PREFIX.'id')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($this->storage->destroy('id'));
|
||||
}
|
||||
|
||||
public function testGcSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->gc(123));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getOptionFixtures
|
||||
*/
|
||||
public function testSupportedOptions($options, $supported)
|
||||
{
|
||||
try {
|
||||
new MemcachedSessionHandler($this->memcached, $options);
|
||||
$this->assertTrue($supported);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->assertFalse($supported);
|
||||
}
|
||||
}
|
||||
|
||||
public function getOptionFixtures()
|
||||
{
|
||||
return array(
|
||||
array(array('prefix' => 'session'), true),
|
||||
array(array('expiretime' => 100), true),
|
||||
array(array('prefix' => 'session', 'expiretime' => 200), true),
|
||||
array(array('expiretime' => 100, 'foo' => 'bar'), false),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetConnection()
|
||||
{
|
||||
$method = new \ReflectionMethod($this->storage, 'getMemcached');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertInstanceOf('\Memcached', $method->invoke($this->storage));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,333 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler;
|
||||
|
||||
/**
|
||||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class MongoDbSessionHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $mongo;
|
||||
private $storage;
|
||||
public $options;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
if (\extension_loaded('mongodb')) {
|
||||
if (!class_exists('MongoDB\Client')) {
|
||||
$this->markTestSkipped('The mongodb/mongodb package is required.');
|
||||
}
|
||||
} elseif (!\extension_loaded('mongo')) {
|
||||
$this->markTestSkipped('The Mongo or MongoDB extension is required.');
|
||||
}
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$mongoClass = 'MongoDB\Client';
|
||||
} else {
|
||||
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
|
||||
}
|
||||
|
||||
$this->mongo = $this->getMockBuilder($mongoClass)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->options = array(
|
||||
'id_field' => '_id',
|
||||
'data_field' => 'data',
|
||||
'time_field' => 'time',
|
||||
'expiry_field' => 'expires_at',
|
||||
'database' => 'sf2-test',
|
||||
'collection' => 'session-test',
|
||||
);
|
||||
|
||||
$this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testConstructorShouldThrowExceptionForInvalidMongo()
|
||||
{
|
||||
new MongoDbSessionHandler(new \stdClass(), $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testConstructorShouldThrowExceptionForMissingOptions()
|
||||
{
|
||||
new MongoDbSessionHandler($this->mongo, array());
|
||||
}
|
||||
|
||||
public function testOpenMethodAlwaysReturnTrue()
|
||||
{
|
||||
$this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true');
|
||||
}
|
||||
|
||||
public function testCloseMethodAlwaysReturnTrue()
|
||||
{
|
||||
$this->assertTrue($this->storage->close(), 'The "close" method should always return true');
|
||||
}
|
||||
|
||||
public function testRead()
|
||||
{
|
||||
$collection = $this->createMongoCollectionMock();
|
||||
|
||||
$this->mongo->expects($this->once())
|
||||
->method('selectCollection')
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
// defining the timeout before the actual method call
|
||||
// allows to test for "greater than" values in the $criteria
|
||||
$testTimeout = time() + 1;
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method('findOne')
|
||||
->will($this->returnCallback(function ($criteria) use ($testTimeout) {
|
||||
$this->assertArrayHasKey($this->options['id_field'], $criteria);
|
||||
$this->assertEquals($criteria[$this->options['id_field']], 'foo');
|
||||
|
||||
$this->assertArrayHasKey($this->options['expiry_field'], $criteria);
|
||||
$this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
|
||||
$this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout);
|
||||
} else {
|
||||
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
|
||||
$this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
$this->options['id_field'] => 'foo',
|
||||
);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
|
||||
$fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
|
||||
} else {
|
||||
$fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
|
||||
$fields[$this->options['id_field']] = new \MongoDate();
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}));
|
||||
|
||||
$this->assertEquals('bar', $this->storage->read('foo'));
|
||||
}
|
||||
|
||||
public function testWrite()
|
||||
{
|
||||
$collection = $this->createMongoCollectionMock();
|
||||
|
||||
$this->mongo->expects($this->once())
|
||||
->method('selectCollection')
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method($methodName)
|
||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
||||
$this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals(array('upsert' => true), $options);
|
||||
} else {
|
||||
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
|
||||
}
|
||||
|
||||
$data = $updateData['$set'];
|
||||
}));
|
||||
|
||||
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
|
||||
$this->assertTrue($this->storage->write('foo', 'bar'));
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
|
||||
$this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000));
|
||||
} else {
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
|
||||
$this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
|
||||
}
|
||||
}
|
||||
|
||||
public function testWriteWhenUsingExpiresField()
|
||||
{
|
||||
$this->options = array(
|
||||
'id_field' => '_id',
|
||||
'data_field' => 'data',
|
||||
'time_field' => 'time',
|
||||
'database' => 'sf2-test',
|
||||
'collection' => 'session-test',
|
||||
'expiry_field' => 'expiresAt',
|
||||
);
|
||||
|
||||
$this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
|
||||
|
||||
$collection = $this->createMongoCollectionMock();
|
||||
|
||||
$this->mongo->expects($this->once())
|
||||
->method('selectCollection')
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method($methodName)
|
||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
||||
$this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals(array('upsert' => true), $options);
|
||||
} else {
|
||||
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
|
||||
}
|
||||
|
||||
$data = $updateData['$set'];
|
||||
}));
|
||||
|
||||
$this->assertTrue($this->storage->write('foo', 'bar'));
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
|
||||
} else {
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
|
||||
}
|
||||
}
|
||||
|
||||
public function testReplaceSessionData()
|
||||
{
|
||||
$collection = $this->createMongoCollectionMock();
|
||||
|
||||
$this->mongo->expects($this->once())
|
||||
->method('selectCollection')
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->exactly(2))
|
||||
->method($methodName)
|
||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
||||
$data = $updateData;
|
||||
}));
|
||||
|
||||
$this->storage->write('foo', 'bar');
|
||||
$this->storage->write('foo', 'foobar');
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
|
||||
} else {
|
||||
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
|
||||
}
|
||||
}
|
||||
|
||||
public function testDestroy()
|
||||
{
|
||||
$collection = $this->createMongoCollectionMock();
|
||||
|
||||
$this->mongo->expects($this->once())
|
||||
->method('selectCollection')
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method($methodName)
|
||||
->with(array($this->options['id_field'] => 'foo'));
|
||||
|
||||
$this->assertTrue($this->storage->destroy('foo'));
|
||||
}
|
||||
|
||||
public function testGc()
|
||||
{
|
||||
$collection = $this->createMongoCollectionMock();
|
||||
|
||||
$this->mongo->expects($this->once())
|
||||
->method('selectCollection')
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'deleteMany' : 'remove';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method($methodName)
|
||||
->will($this->returnCallback(function ($criteria) {
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']);
|
||||
$this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000));
|
||||
} else {
|
||||
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
|
||||
$this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
|
||||
}
|
||||
}));
|
||||
|
||||
$this->assertTrue($this->storage->gc(1));
|
||||
}
|
||||
|
||||
public function testGetConnection()
|
||||
{
|
||||
$method = new \ReflectionMethod($this->storage, 'getMongo');
|
||||
$method->setAccessible(true);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$mongoClass = 'MongoDB\Client';
|
||||
} else {
|
||||
$mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient';
|
||||
}
|
||||
|
||||
$this->assertInstanceOf($mongoClass, $method->invoke($this->storage));
|
||||
}
|
||||
|
||||
private function createMongoCollectionMock()
|
||||
{
|
||||
$collectionClass = 'MongoCollection';
|
||||
if (phpversion('mongodb')) {
|
||||
$collectionClass = 'MongoDB\Collection';
|
||||
}
|
||||
|
||||
$collection = $this->getMockBuilder($collectionClass)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||
|
||||
/**
|
||||
* Test class for NativeFileSessionHandler.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
class NativeFileSessionHandlerTest extends TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir()));
|
||||
|
||||
$this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName());
|
||||
$this->assertEquals('user', ini_get('session.save_handler'));
|
||||
|
||||
$this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path'));
|
||||
$this->assertEquals('TESTING', ini_get('session.name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider savePathDataProvider
|
||||
*/
|
||||
public function testConstructSavePath($savePath, $expectedSavePath, $path)
|
||||
{
|
||||
$handler = new NativeFileSessionHandler($savePath);
|
||||
$this->assertEquals($expectedSavePath, ini_get('session.save_path'));
|
||||
$this->assertTrue(is_dir(realpath($path)));
|
||||
|
||||
rmdir($path);
|
||||
}
|
||||
|
||||
public function savePathDataProvider()
|
||||
{
|
||||
$base = sys_get_temp_dir();
|
||||
|
||||
return array(
|
||||
array("$base/foo", "$base/foo", "$base/foo"),
|
||||
array("5;$base/foo", "5;$base/foo", "$base/foo"),
|
||||
array("5;0600;$base/foo", "5;0600;$base/foo", "$base/foo"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testConstructException()
|
||||
{
|
||||
$handler = new NativeFileSessionHandler('something;invalid;with;too-many-args');
|
||||
}
|
||||
|
||||
public function testConstructDefault()
|
||||
{
|
||||
$path = ini_get('session.save_path');
|
||||
$storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler());
|
||||
|
||||
$this->assertEquals($path, ini_get('session.save_path'));
|
||||
}
|
||||
}
|
||||
|
|
@ -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\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
|
||||
|
||||
/**
|
||||
* Test class for NativeSessionHandler.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @group legacy
|
||||
*/
|
||||
class NativeSessionHandlerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedDeprecation The Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler class is deprecated since Symfony 3.4 and will be removed in 4.0. Use the \SessionHandler class instead.
|
||||
*/
|
||||
public function testConstruct()
|
||||
{
|
||||
$handler = new NativeSessionHandler();
|
||||
|
||||
$this->assertInstanceOf('SessionHandler', $handler);
|
||||
$this->assertTrue($handler instanceof NativeSessionHandler);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||
|
||||
/**
|
||||
* Test class for NullSessionHandler.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
class NullSessionHandlerTest extends TestCase
|
||||
{
|
||||
public function testSaveHandlers()
|
||||
{
|
||||
$storage = $this->getStorage();
|
||||
$this->assertEquals('user', ini_get('session.save_handler'));
|
||||
}
|
||||
|
||||
public function testSession()
|
||||
{
|
||||
session_id('nullsessionstorage');
|
||||
$storage = $this->getStorage();
|
||||
$session = new Session($storage);
|
||||
$this->assertNull($session->get('something'));
|
||||
$session->set('something', 'unique');
|
||||
$this->assertEquals('unique', $session->get('something'));
|
||||
}
|
||||
|
||||
public function testNothingIsPersisted()
|
||||
{
|
||||
session_id('nullsessionstorage');
|
||||
$storage = $this->getStorage();
|
||||
$session = new Session($storage);
|
||||
$session->start();
|
||||
$this->assertEquals('nullsessionstorage', $session->getId());
|
||||
$this->assertNull($session->get('something'));
|
||||
}
|
||||
|
||||
public function getStorage()
|
||||
{
|
||||
return new NativeSessionStorage(array(), new NullSessionHandler());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,411 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
||||
|
||||
/**
|
||||
* @requires extension pdo_sqlite
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class PdoSessionHandlerTest extends TestCase
|
||||
{
|
||||
private $dbFile;
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
// make sure the temporary database file is deleted when it has been created (even when a test fails)
|
||||
if ($this->dbFile) {
|
||||
@unlink($this->dbFile);
|
||||
}
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
protected function getPersistentSqliteDsn()
|
||||
{
|
||||
$this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions');
|
||||
|
||||
return 'sqlite:'.$this->dbFile;
|
||||
}
|
||||
|
||||
protected function getMemorySqlitePdo()
|
||||
{
|
||||
$pdo = new \PDO('sqlite::memory:');
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$storage = new PdoSessionHandler($pdo);
|
||||
$storage->createTable();
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testWrongPdoErrMode()
|
||||
{
|
||||
$pdo = $this->getMemorySqlitePdo();
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
|
||||
|
||||
$storage = new PdoSessionHandler($pdo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testInexistentTable()
|
||||
{
|
||||
$storage = new PdoSessionHandler($this->getMemorySqlitePdo(), array('db_table' => 'inexistent_table'));
|
||||
$storage->open('', 'sid');
|
||||
$storage->read('id');
|
||||
$storage->write('id', 'data');
|
||||
$storage->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testCreateTableTwice()
|
||||
{
|
||||
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
|
||||
$storage->createTable();
|
||||
}
|
||||
|
||||
public function testWithLazyDsnConnection()
|
||||
{
|
||||
$dsn = $this->getPersistentSqliteDsn();
|
||||
|
||||
$storage = new PdoSessionHandler($dsn);
|
||||
$storage->createTable();
|
||||
$storage->open('', 'sid');
|
||||
$data = $storage->read('id');
|
||||
$storage->write('id', 'data');
|
||||
$storage->close();
|
||||
$this->assertSame('', $data, 'New session returns empty string data');
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$data = $storage->read('id');
|
||||
$storage->close();
|
||||
$this->assertSame('data', $data, 'Written value can be read back correctly');
|
||||
}
|
||||
|
||||
public function testWithLazySavePathConnection()
|
||||
{
|
||||
$dsn = $this->getPersistentSqliteDsn();
|
||||
|
||||
// Open is called with what ini_set('session.save_path', $dsn) would mean
|
||||
$storage = new PdoSessionHandler(null);
|
||||
$storage->open($dsn, 'sid');
|
||||
$storage->createTable();
|
||||
$data = $storage->read('id');
|
||||
$storage->write('id', 'data');
|
||||
$storage->close();
|
||||
$this->assertSame('', $data, 'New session returns empty string data');
|
||||
|
||||
$storage->open($dsn, 'sid');
|
||||
$data = $storage->read('id');
|
||||
$storage->close();
|
||||
$this->assertSame('data', $data, 'Written value can be read back correctly');
|
||||
}
|
||||
|
||||
public function testReadWriteReadWithNullByte()
|
||||
{
|
||||
$sessionData = 'da'."\0".'ta';
|
||||
|
||||
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
|
||||
$storage->open('', 'sid');
|
||||
$readData = $storage->read('id');
|
||||
$storage->write('id', $sessionData);
|
||||
$storage->close();
|
||||
$this->assertSame('', $readData, 'New session returns empty string data');
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$readData = $storage->read('id');
|
||||
$storage->close();
|
||||
$this->assertSame($sessionData, $readData, 'Written value can be read back correctly');
|
||||
}
|
||||
|
||||
public function testReadConvertsStreamToString()
|
||||
{
|
||||
if (\defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
|
||||
}
|
||||
|
||||
$pdo = new MockPdo('pgsql');
|
||||
$pdo->prepareResult = $this->getMockBuilder('PDOStatement')->getMock();
|
||||
|
||||
$content = 'foobar';
|
||||
$stream = $this->createStream($content);
|
||||
|
||||
$pdo->prepareResult->expects($this->once())->method('fetchAll')
|
||||
->will($this->returnValue(array(array($stream, 42, time()))));
|
||||
|
||||
$storage = new PdoSessionHandler($pdo);
|
||||
$result = $storage->read('foo');
|
||||
|
||||
$this->assertSame($content, $result);
|
||||
}
|
||||
|
||||
public function testReadLockedConvertsStreamToString()
|
||||
{
|
||||
if (\defined('HHVM_VERSION')) {
|
||||
$this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
|
||||
}
|
||||
if (ini_get('session.use_strict_mode')) {
|
||||
$this->markTestSkipped('Strict mode needs no locking for new sessions.');
|
||||
}
|
||||
|
||||
$pdo = new MockPdo('pgsql');
|
||||
$selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
|
||||
$insertStmt = $this->getMockBuilder('PDOStatement')->getMock();
|
||||
|
||||
$pdo->prepareResult = function ($statement) use ($selectStmt, $insertStmt) {
|
||||
return 0 === strpos($statement, 'INSERT') ? $insertStmt : $selectStmt;
|
||||
};
|
||||
|
||||
$content = 'foobar';
|
||||
$stream = $this->createStream($content);
|
||||
$exception = null;
|
||||
|
||||
$selectStmt->expects($this->atLeast(2))->method('fetchAll')
|
||||
->will($this->returnCallback(function () use (&$exception, $stream) {
|
||||
return $exception ? array(array($stream, 42, time())) : array();
|
||||
}));
|
||||
|
||||
$insertStmt->expects($this->once())->method('execute')
|
||||
->will($this->returnCallback(function () use (&$exception) {
|
||||
throw $exception = new \PDOException('', '23');
|
||||
}));
|
||||
|
||||
$storage = new PdoSessionHandler($pdo);
|
||||
$result = $storage->read('foo');
|
||||
|
||||
$this->assertSame($content, $result);
|
||||
}
|
||||
|
||||
public function testReadingRequiresExactlySameId()
|
||||
{
|
||||
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
|
||||
$storage->open('', 'sid');
|
||||
$storage->write('id', 'data');
|
||||
$storage->write('test', 'data');
|
||||
$storage->write('space ', 'data');
|
||||
$storage->close();
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$readDataCaseSensitive = $storage->read('ID');
|
||||
$readDataNoCharFolding = $storage->read('tést');
|
||||
$readDataKeepSpace = $storage->read('space ');
|
||||
$readDataExtraSpace = $storage->read('space ');
|
||||
$storage->close();
|
||||
|
||||
$this->assertSame('', $readDataCaseSensitive, 'Retrieval by ID should be case-sensitive (collation setting)');
|
||||
$this->assertSame('', $readDataNoCharFolding, 'Retrieval by ID should not do character folding (collation setting)');
|
||||
$this->assertSame('data', $readDataKeepSpace, 'Retrieval by ID requires spaces as-is');
|
||||
$this->assertSame('', $readDataExtraSpace, 'Retrieval by ID requires spaces as-is');
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace).
|
||||
*/
|
||||
public function testWriteDifferentSessionIdThanRead()
|
||||
{
|
||||
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
|
||||
$storage->open('', 'sid');
|
||||
$storage->read('id');
|
||||
$storage->destroy('id');
|
||||
$storage->write('new_id', 'data_of_new_session_id');
|
||||
$storage->close();
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$data = $storage->read('new_id');
|
||||
$storage->close();
|
||||
|
||||
$this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available');
|
||||
}
|
||||
|
||||
public function testWrongUsageStillWorks()
|
||||
{
|
||||
// wrong method sequence that should no happen, but still works
|
||||
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
|
||||
$storage->write('id', 'data');
|
||||
$storage->write('other_id', 'other_data');
|
||||
$storage->destroy('inexistent');
|
||||
$storage->open('', 'sid');
|
||||
$data = $storage->read('id');
|
||||
$otherData = $storage->read('other_id');
|
||||
$storage->close();
|
||||
|
||||
$this->assertSame('data', $data);
|
||||
$this->assertSame('other_data', $otherData);
|
||||
}
|
||||
|
||||
public function testSessionDestroy()
|
||||
{
|
||||
$pdo = $this->getMemorySqlitePdo();
|
||||
$storage = new PdoSessionHandler($pdo);
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$storage->read('id');
|
||||
$storage->write('id', 'data');
|
||||
$storage->close();
|
||||
$this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$storage->read('id');
|
||||
$storage->destroy('id');
|
||||
$storage->close();
|
||||
$this->assertEquals(0, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$data = $storage->read('id');
|
||||
$storage->close();
|
||||
$this->assertSame('', $data, 'Destroyed session returns empty string');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testSessionGC()
|
||||
{
|
||||
$previousLifeTime = ini_set('session.gc_maxlifetime', 1000);
|
||||
$pdo = $this->getMemorySqlitePdo();
|
||||
$storage = new PdoSessionHandler($pdo);
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$storage->read('id');
|
||||
$storage->write('id', 'data');
|
||||
$storage->close();
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$storage->read('gc_id');
|
||||
ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read
|
||||
$storage->write('gc_id', 'data');
|
||||
$storage->close();
|
||||
$this->assertEquals(2, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called');
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$data = $storage->read('gc_id');
|
||||
$storage->gc(-1);
|
||||
$storage->close();
|
||||
|
||||
ini_set('session.gc_maxlifetime', $previousLifeTime);
|
||||
|
||||
$this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet');
|
||||
$this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned');
|
||||
}
|
||||
|
||||
public function testGetConnection()
|
||||
{
|
||||
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
|
||||
|
||||
$method = new \ReflectionMethod($storage, 'getConnection');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertInstanceOf('\PDO', $method->invoke($storage));
|
||||
}
|
||||
|
||||
public function testGetConnectionConnectsIfNeeded()
|
||||
{
|
||||
$storage = new PdoSessionHandler('sqlite::memory:');
|
||||
|
||||
$method = new \ReflectionMethod($storage, 'getConnection');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertInstanceOf('\PDO', $method->invoke($storage));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideUrlDsnPairs
|
||||
*/
|
||||
public function testUrlDsn($url, $expectedDsn, $expectedUser = null, $expectedPassword = null)
|
||||
{
|
||||
$storage = new PdoSessionHandler($url);
|
||||
|
||||
$this->assertAttributeEquals($expectedDsn, 'dsn', $storage);
|
||||
|
||||
if (null !== $expectedUser) {
|
||||
$this->assertAttributeEquals($expectedUser, 'username', $storage);
|
||||
}
|
||||
|
||||
if (null !== $expectedPassword) {
|
||||
$this->assertAttributeEquals($expectedPassword, 'password', $storage);
|
||||
}
|
||||
}
|
||||
|
||||
public function provideUrlDsnPairs()
|
||||
{
|
||||
yield array('mysql://localhost/test', 'mysql:host=localhost;dbname=test;');
|
||||
yield array('mysql://localhost:56/test', 'mysql:host=localhost;port=56;dbname=test;');
|
||||
yield array('mysql2://root:pwd@localhost/test', 'mysql:host=localhost;dbname=test;', 'root', 'pwd');
|
||||
yield array('postgres://localhost/test', 'pgsql:host=localhost;dbname=test;');
|
||||
yield array('postgresql://localhost:5634/test', 'pgsql:host=localhost;port=5634;dbname=test;');
|
||||
yield array('postgres://root:pwd@localhost/test', 'pgsql:host=localhost;dbname=test;', 'root', 'pwd');
|
||||
yield 'sqlite relative path' => array('sqlite://localhost/tmp/test', 'sqlite:tmp/test');
|
||||
yield 'sqlite absolute path' => array('sqlite://localhost//tmp/test', 'sqlite:/tmp/test');
|
||||
yield 'sqlite relative path without host' => array('sqlite:///tmp/test', 'sqlite:tmp/test');
|
||||
yield 'sqlite absolute path without host' => array('sqlite3:////tmp/test', 'sqlite:/tmp/test');
|
||||
yield array('sqlite://localhost/:memory:', 'sqlite::memory:');
|
||||
yield array('mssql://localhost/test', 'sqlsrv:server=localhost;Database=test');
|
||||
yield array('mssql://localhost:56/test', 'sqlsrv:server=localhost,56;Database=test');
|
||||
}
|
||||
|
||||
private function createStream($content)
|
||||
{
|
||||
$stream = tmpfile();
|
||||
fwrite($stream, $content);
|
||||
fseek($stream, 0);
|
||||
|
||||
return $stream;
|
||||
}
|
||||
}
|
||||
|
||||
class MockPdo extends \PDO
|
||||
{
|
||||
public $prepareResult;
|
||||
private $driverName;
|
||||
private $errorMode;
|
||||
|
||||
public function __construct($driverName = null, $errorMode = null)
|
||||
{
|
||||
$this->driverName = $driverName;
|
||||
$this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION;
|
||||
}
|
||||
|
||||
public function getAttribute($attribute)
|
||||
{
|
||||
if (\PDO::ATTR_ERRMODE === $attribute) {
|
||||
return $this->errorMode;
|
||||
}
|
||||
|
||||
if (\PDO::ATTR_DRIVER_NAME === $attribute) {
|
||||
return $this->driverName;
|
||||
}
|
||||
|
||||
return parent::getAttribute($attribute);
|
||||
}
|
||||
|
||||
public function prepare($statement, $driverOptions = array())
|
||||
{
|
||||
return \is_callable($this->prepareResult)
|
||||
? \call_user_func($this->prepareResult, $statement, $driverOptions)
|
||||
: $this->prepareResult;
|
||||
}
|
||||
|
||||
public function beginTransaction()
|
||||
{
|
||||
}
|
||||
|
||||
public function rollBack()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler;
|
||||
|
||||
/**
|
||||
* @author Adrien Brault <adrien.brault@gmail.com>
|
||||
*
|
||||
* @group legacy
|
||||
*/
|
||||
class WriteCheckSessionHandlerTest extends TestCase
|
||||
{
|
||||
public function test()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('close')
|
||||
->with()
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($writeCheckSessionHandler->close());
|
||||
}
|
||||
|
||||
public function testWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('write')
|
||||
->with('foo', 'bar')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
|
||||
}
|
||||
|
||||
public function testSkippedWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('read')
|
||||
->with('foo')
|
||||
->will($this->returnValue('bar'))
|
||||
;
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->never())
|
||||
->method('write')
|
||||
;
|
||||
|
||||
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
|
||||
$this->assertTrue($writeCheckSessionHandler->write('foo', 'bar'));
|
||||
}
|
||||
|
||||
public function testNonSkippedWrite()
|
||||
{
|
||||
$wrappedSessionHandlerMock = $this->getMockBuilder('SessionHandlerInterface')->getMock();
|
||||
$writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock);
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('read')
|
||||
->with('foo')
|
||||
->will($this->returnValue('bar'))
|
||||
;
|
||||
|
||||
$wrappedSessionHandlerMock
|
||||
->expects($this->once())
|
||||
->method('write')
|
||||
->with('foo', 'baZZZ')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertEquals('bar', $writeCheckSessionHandler->read('foo'));
|
||||
$this->assertTrue($writeCheckSessionHandler->write('foo', 'baZZZ'));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue