177 changed files with 26310 additions and 22 deletions
-
3lam/composer.json
-
307lam/composer.lock
-
2lam/lib/2factor.inc
-
3lam/lib/3rdParty/composer/composer/autoload_files.php
-
5lam/lib/3rdParty/composer/composer/autoload_psr4.php
-
28lam/lib/3rdParty/composer/composer/autoload_static.php
-
317lam/lib/3rdParty/composer/composer/installed.json
-
3lam/lib/3rdParty/composer/symfony/http-foundation/.gitattributes
-
165lam/lib/3rdParty/composer/symfony/http-foundation/AcceptHeader.php
-
177lam/lib/3rdParty/composer/symfony/http-foundation/AcceptHeaderItem.php
-
354lam/lib/3rdParty/composer/symfony/http-foundation/BinaryFileResponse.php
-
244lam/lib/3rdParty/composer/symfony/http-foundation/CHANGELOG.md
-
302lam/lib/3rdParty/composer/symfony/http-foundation/Cookie.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/Exception/ConflictingHeadersException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/Exception/RequestExceptionInterface.php
-
20lam/lib/3rdParty/composer/symfony/http-foundation/Exception/SuspiciousOperationException.php
-
47lam/lib/3rdParty/composer/symfony/http-foundation/ExpressionRequestMatcher.php
-
25lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/AccessDeniedException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/CannotWriteFileException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/ExtensionFileException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/FileException.php
-
25lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/FileNotFoundException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/FormSizeFileException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/IniSizeFileException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/NoFileException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/NoTmpDirFileException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/PartialFileException.php
-
20lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/UnexpectedTypeException.php
-
21lam/lib/3rdParty/composer/symfony/http-foundation/File/Exception/UploadException.php
-
130lam/lib/3rdParty/composer/symfony/http-foundation/File/File.php
-
28lam/lib/3rdParty/composer/symfony/http-foundation/File/Stream.php
-
281lam/lib/3rdParty/composer/symfony/http-foundation/File/UploadedFile.php
-
142lam/lib/3rdParty/composer/symfony/http-foundation/FileBag.php
-
288lam/lib/3rdParty/composer/symfony/http-foundation/HeaderBag.php
-
224lam/lib/3rdParty/composer/symfony/http-foundation/HeaderUtils.php
-
185lam/lib/3rdParty/composer/symfony/http-foundation/IpUtils.php
-
215lam/lib/3rdParty/composer/symfony/http-foundation/JsonResponse.php
-
19lam/lib/3rdParty/composer/symfony/http-foundation/LICENSE
-
205lam/lib/3rdParty/composer/symfony/http-foundation/ParameterBag.php
-
14lam/lib/3rdParty/composer/symfony/http-foundation/README.md
-
105lam/lib/3rdParty/composer/symfony/http-foundation/RedirectResponse.php
-
2060lam/lib/3rdParty/composer/symfony/http-foundation/Request.php
-
188lam/lib/3rdParty/composer/symfony/http-foundation/RequestMatcher.php
-
27lam/lib/3rdParty/composer/symfony/http-foundation/RequestMatcherInterface.php
-
103lam/lib/3rdParty/composer/symfony/http-foundation/RequestStack.php
-
1224lam/lib/3rdParty/composer/symfony/http-foundation/Response.php
-
293lam/lib/3rdParty/composer/symfony/http-foundation/ResponseHeaderBag.php
-
99lam/lib/3rdParty/composer/symfony/http-foundation/ServerBag.php
-
148lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/AttributeBag.php
-
61lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
-
157lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
-
161lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
-
152lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/FlashBag.php
-
88lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/FlashBagInterface.php
-
268lam/lib/3rdParty/composer/symfony/http-foundation/Session/Session.php
-
46lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionBagInterface.php
-
83lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionBagProxy.php
-
166lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionInterface.php
-
59lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionUtils.php
-
141lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php
-
119lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php
-
124lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php
-
187lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php
-
55lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php
-
76lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php
-
899lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php
-
120lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php
-
85lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php
-
103lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php
-
166lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MetadataBag.php
-
252lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php
-
148lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php
-
466lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/NativeSessionStorage.php
-
64lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php
-
118lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php
-
101lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php
-
131lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/SessionStorageInterface.php
-
135lam/lib/3rdParty/composer/symfony/http-foundation/StreamedResponse.php
-
55lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php
-
85lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php
-
77lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php
-
53lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php
-
55lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php
-
56lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php
-
56lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php
-
63lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php
-
102lam/lib/3rdParty/composer/symfony/http-foundation/UrlHelper.php
-
39lam/lib/3rdParty/composer/symfony/http-foundation/composer.json
-
3lam/lib/3rdParty/composer/symfony/mime/.gitattributes
-
125lam/lib/3rdParty/composer/symfony/mime/Address.php
-
20lam/lib/3rdParty/composer/symfony/mime/BodyRendererInterface.php
-
20lam/lib/3rdParty/composer/symfony/mime/CHANGELOG.md
-
221lam/lib/3rdParty/composer/symfony/mime/CharacterStream.php
-
111lam/lib/3rdParty/composer/symfony/mime/Crypto/SMime.php
-
63lam/lib/3rdParty/composer/symfony/mime/Crypto/SMimeEncrypter.php
-
71lam/lib/3rdParty/composer/symfony/mime/Crypto/SMimeSigner.php
-
46lam/lib/3rdParty/composer/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php
-
599lam/lib/3rdParty/composer/symfony/mime/Email.php
-
28lam/lib/3rdParty/composer/symfony/mime/Encoder/AddressEncoderInterface.php
-
48lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64ContentEncoder.php
@ -0,0 +1,3 @@ |
|||
/Tests export-ignore |
|||
/phpunit.xml.dist export-ignore |
|||
/.gitignore export-ignore |
@ -0,0 +1,165 @@ |
|||
<?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; |
|||
|
|||
/** |
|||
* Represents an Accept-* header. |
|||
* |
|||
* An accept header is compound with a list of items, |
|||
* sorted by descending quality. |
|||
* |
|||
* @author Jean-Franรงois Simon <contact@jfsimon.fr> |
|||
*/ |
|||
class AcceptHeader |
|||
{ |
|||
/** |
|||
* @var AcceptHeaderItem[] |
|||
*/ |
|||
private $items = []; |
|||
|
|||
/** |
|||
* @var bool |
|||
*/ |
|||
private $sorted = true; |
|||
|
|||
/** |
|||
* @param AcceptHeaderItem[] $items |
|||
*/ |
|||
public function __construct(array $items) |
|||
{ |
|||
foreach ($items as $item) { |
|||
$this->add($item); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Builds an AcceptHeader instance from a string. |
|||
* |
|||
* @return self |
|||
*/ |
|||
public static function fromString(?string $headerValue) |
|||
{ |
|||
$index = 0; |
|||
|
|||
$parts = HeaderUtils::split($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; |
|||
}, $parts)); |
|||
} |
|||
|
|||
/** |
|||
* Returns header value's string representation. |
|||
* |
|||
* @return string |
|||
*/ |
|||
public function __toString() |
|||
{ |
|||
return implode(',', $this->items); |
|||
} |
|||
|
|||
/** |
|||
* Tests if header has given value. |
|||
* |
|||
* @return bool |
|||
*/ |
|||
public function has(string $value) |
|||
{ |
|||
return isset($this->items[$value]); |
|||
} |
|||
|
|||
/** |
|||
* Returns given value's item, if exists. |
|||
* |
|||
* @return AcceptHeaderItem|null |
|||
*/ |
|||
public function get(string $value) |
|||
{ |
|||
return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null; |
|||
} |
|||
|
|||
/** |
|||
* Adds an item. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function add(AcceptHeaderItem $item) |
|||
{ |
|||
$this->items[$item->getValue()] = $item; |
|||
$this->sorted = false; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Returns all items. |
|||
* |
|||
* @return AcceptHeaderItem[] |
|||
*/ |
|||
public function all() |
|||
{ |
|||
$this->sort(); |
|||
|
|||
return $this->items; |
|||
} |
|||
|
|||
/** |
|||
* Filters items on their value using given regex. |
|||
* |
|||
* @return self |
|||
*/ |
|||
public function filter(string $pattern) |
|||
{ |
|||
return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) { |
|||
return preg_match($pattern, $item->getValue()); |
|||
})); |
|||
} |
|||
|
|||
/** |
|||
* Returns first item. |
|||
* |
|||
* @return AcceptHeaderItem|null |
|||
*/ |
|||
public function first() |
|||
{ |
|||
$this->sort(); |
|||
|
|||
return !empty($this->items) ? reset($this->items) : null; |
|||
} |
|||
|
|||
/** |
|||
* Sorts items by descending quality. |
|||
*/ |
|||
private function sort(): void |
|||
{ |
|||
if (!$this->sorted) { |
|||
uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) { |
|||
$qA = $a->getQuality(); |
|||
$qB = $b->getQuality(); |
|||
|
|||
if ($qA === $qB) { |
|||
return $a->getIndex() > $b->getIndex() ? 1 : -1; |
|||
} |
|||
|
|||
return $qA > $qB ? -1 : 1; |
|||
}); |
|||
|
|||
$this->sorted = true; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,177 @@ |
|||
<?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; |
|||
|
|||
/** |
|||
* Represents an Accept-* header item. |
|||
* |
|||
* @author Jean-Franรงois Simon <contact@jfsimon.fr> |
|||
*/ |
|||
class AcceptHeaderItem |
|||
{ |
|||
private $value; |
|||
private $quality = 1.0; |
|||
private $index = 0; |
|||
private $attributes = []; |
|||
|
|||
public function __construct(string $value, array $attributes = []) |
|||
{ |
|||
$this->value = $value; |
|||
foreach ($attributes as $name => $value) { |
|||
$this->setAttribute($name, $value); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Builds an AcceptHeaderInstance instance from a string. |
|||
* |
|||
* @return self |
|||
*/ |
|||
public static function fromString(?string $itemValue) |
|||
{ |
|||
$parts = HeaderUtils::split($itemValue ?? '', ';='); |
|||
|
|||
$part = array_shift($parts); |
|||
$attributes = HeaderUtils::combine($parts); |
|||
|
|||
return new self($part[0], $attributes); |
|||
} |
|||
|
|||
/** |
|||
* Returns header value's string representation. |
|||
* |
|||
* @return string |
|||
*/ |
|||
public function __toString() |
|||
{ |
|||
$string = $this->value.($this->quality < 1 ? ';q='.$this->quality : ''); |
|||
if (\count($this->attributes) > 0) { |
|||
$string .= '; '.HeaderUtils::toString($this->attributes, ';'); |
|||
} |
|||
|
|||
return $string; |
|||
} |
|||
|
|||
/** |
|||
* Set the item value. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function setValue(string $value) |
|||
{ |
|||
$this->value = $value; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Returns the item value. |
|||
* |
|||
* @return string |
|||
*/ |
|||
public function getValue() |
|||
{ |
|||
return $this->value; |
|||
} |
|||
|
|||
/** |
|||
* Set the item quality. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function setQuality(float $quality) |
|||
{ |
|||
$this->quality = $quality; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Returns the item quality. |
|||
* |
|||
* @return float |
|||
*/ |
|||
public function getQuality() |
|||
{ |
|||
return $this->quality; |
|||
} |
|||
|
|||
/** |
|||
* Set the item index. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function setIndex(int $index) |
|||
{ |
|||
$this->index = $index; |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Returns the item index. |
|||
* |
|||
* @return int |
|||
*/ |
|||
public function getIndex() |
|||
{ |
|||
return $this->index; |
|||
} |
|||
|
|||
/** |
|||
* Tests if an attribute exists. |
|||
* |
|||
* @return bool |
|||
*/ |
|||
public function hasAttribute(string $name) |
|||
{ |
|||
return isset($this->attributes[$name]); |
|||
} |
|||
|
|||
/** |
|||
* Returns an attribute by its name. |
|||
* |
|||
* @param mixed $default |
|||
* |
|||
* @return mixed |
|||
*/ |
|||
public function getAttribute(string $name, $default = null) |
|||
{ |
|||
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; |
|||
} |
|||
|
|||
/** |
|||
* Returns all attributes. |
|||
* |
|||
* @return array |
|||
*/ |
|||
public function getAttributes() |
|||
{ |
|||
return $this->attributes; |
|||
} |
|||
|
|||
/** |
|||
* Set an attribute. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function setAttribute(string $name, string $value) |
|||
{ |
|||
if ('q' === $name) { |
|||
$this->quality = (float) $value; |
|||
} else { |
|||
$this->attributes[$name] = $value; |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
} |
@ -0,0 +1,354 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of the Symfony package. |
|||
* |
|||
* (c) Fabien Potencier <fabien@symfony.com> |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
namespace Symfony\Component\HttpFoundation; |
|||
|
|||
use Symfony\Component\HttpFoundation\File\Exception\FileException; |
|||
use Symfony\Component\HttpFoundation\File\File; |
|||
|
|||
/** |
|||
* BinaryFileResponse represents an HTTP response delivering a file. |
|||
* |
|||
* @author Niklas Fiekas <niklas.fiekas@tu-clausthal.de> |
|||
* @author stealth35 <stealth35-php@live.fr> |
|||
* @author Igor Wiedler <igor@wiedler.ch> |
|||
* @author Jordan Alliot <jordan.alliot@gmail.com> |
|||
* @author Sergey Linnik <linniksa@gmail.com> |
|||
*/ |
|||
class BinaryFileResponse extends Response |
|||
{ |
|||
protected static $trustXSendfileTypeHeader = false; |
|||
|
|||
/** |
|||
* @var File |
|||
*/ |
|||
protected $file; |
|||
protected $offset = 0; |
|||
protected $maxlen = -1; |
|||
protected $deleteFileAfterSend = false; |
|||
|
|||
/** |
|||
* @param \SplFileInfo|string $file The file to stream |
|||
* @param int $status The response status code |
|||
* @param array $headers An array of response headers |
|||
* @param bool $public Files are public by default |
|||
* @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, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) |
|||
{ |
|||
parent::__construct(null, $status, $headers); |
|||
|
|||
$this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified); |
|||
|
|||
if ($public) { |
|||
$this->setPublic(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @param \SplFileInfo|string $file The file to stream |
|||
* @param int $status The response status code |
|||
* @param array $headers An array of response headers |
|||
* @param bool $public Files are public by default |
|||
* @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, int $status = 200, array $headers = [], bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) |
|||
{ |
|||
return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified); |
|||
} |
|||
|
|||
/** |
|||
* Sets the file to stream. |
|||
* |
|||
* @param \SplFileInfo|string $file The file to stream |
|||
* |
|||
* @return $this |
|||
* |
|||
* @throws FileException |
|||
*/ |
|||
public function setFile($file, string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true) |
|||
{ |
|||
if (!$file instanceof File) { |
|||
if ($file instanceof \SplFileInfo) { |
|||
$file = new File($file->getPathname()); |
|||
} else { |
|||
$file = new File((string) $file); |
|||
} |
|||
} |
|||
|
|||
if (!$file->isReadable()) { |
|||
throw new FileException('File must be readable.'); |
|||
} |
|||
|
|||
$this->file = $file; |
|||
|
|||
if ($autoEtag) { |
|||
$this->setAutoEtag(); |
|||
} |
|||
|
|||
if ($autoLastModified) { |
|||
$this->setAutoLastModified(); |
|||
} |
|||
|
|||
if ($contentDisposition) { |
|||
$this->setContentDisposition($contentDisposition); |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Gets the file. |
|||
* |
|||
* @return File The file to stream |
|||
*/ |
|||
public function getFile() |
|||
{ |
|||
return $this->file; |
|||
} |
|||
|
|||
/** |
|||
* Automatically sets the Last-Modified header according the file modification date. |
|||
*/ |
|||
public function setAutoLastModified() |
|||
{ |
|||
$this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime())); |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Automatically sets the ETag header according to the checksum of the file. |
|||
*/ |
|||
public function setAutoEtag() |
|||
{ |
|||
$this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true))); |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* Sets the Content-Disposition header with the given filename. |
|||
* |
|||
* @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT |
|||
* @param string $filename Optionally use this UTF-8 encoded filename instead of the real name of the file |
|||
* @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function setContentDisposition(string $disposition, string $filename = '', string $filenameFallback = '') |
|||
{ |
|||
if ('' === $filename) { |
|||
$filename = $this->file->getFilename(); |
|||
} |
|||
|
|||
if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) { |
|||
$encoding = mb_detect_encoding($filename, null, true) ?: '8bit'; |
|||
|
|||
for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) { |
|||
$char = mb_substr($filename, $i, 1, $encoding); |
|||
|
|||
if ('%' === $char || \ord($char) < 32 || \ord($char) > 126) { |
|||
$filenameFallback .= '_'; |
|||
} else { |
|||
$filenameFallback .= $char; |
|||
} |
|||
} |
|||
} |
|||
|
|||
$dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback); |
|||
$this->headers->set('Content-Disposition', $dispositionHeader); |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function prepare(Request $request) |
|||
{ |
|||
if (!$this->headers->has('Content-Type')) { |
|||
$this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream'); |
|||
} |
|||
|
|||
if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) { |
|||
$this->setProtocolVersion('1.1'); |
|||
} |
|||
|
|||
$this->ensureIEOverSSLCompatibility($request); |
|||
|
|||
$this->offset = 0; |
|||
$this->maxlen = -1; |
|||
|
|||
if (false === $fileSize = $this->file->getSize()) { |
|||
return $this; |
|||
} |
|||
$this->headers->set('Content-Length', $fileSize); |
|||
|
|||
if (!$this->headers->has('Accept-Ranges')) { |
|||
// Only accept ranges on safe HTTP methods
|
|||
$this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none'); |
|||
} |
|||
|
|||
if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) { |
|||
// Use X-Sendfile, do not send any content.
|
|||
$type = $request->headers->get('X-Sendfile-Type'); |
|||
$path = $this->file->getRealPath(); |
|||
// Fall back to scheme://path for stream wrapped locations.
|
|||
if (false === $path) { |
|||
$path = $this->file->getPathname(); |
|||
} |
|||
if ('x-accel-redirect' === strtolower($type)) { |
|||
// Do X-Accel-Mapping substitutions.
|
|||
// @link http://wiki.nginx.org/X-accel#X-Accel-Redirect
|
|||
$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; |
|||
} |
|||
} 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) + [0]; |
|||
|
|||
$end = ('' === $end) ? $fileSize - 1 : (int) $end; |
|||
|
|||
if ('' === $start) { |
|||
$start = $fileSize - $end; |
|||
$end = $fileSize - 1; |
|||
} else { |
|||
$start = (int) $start; |
|||
} |
|||
|
|||
if ($start <= $end) { |
|||
if ($start < 0 || $end > $fileSize - 1) { |
|||
$this->setStatusCode(416); |
|||
$this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize)); |
|||
} elseif (0 !== $start || $end !== $fileSize - 1) { |
|||
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1; |
|||
$this->offset = $start; |
|||
|
|||
$this->setStatusCode(206); |
|||
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize)); |
|||
$this->headers->set('Content-Length', $end - $start + 1); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
private function hasValidIfRangeHeader(?string $header): bool |
|||
{ |
|||
if ($this->getEtag() === $header) { |
|||
return true; |
|||
} |
|||
|
|||
if (null === $lastModified = $this->getLastModified()) { |
|||
return false; |
|||
} |
|||
|
|||
return $lastModified->format('D, d M Y H:i:s').' GMT' === $header; |
|||
} |
|||
|
|||
/** |
|||
* Sends the file. |
|||
* |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function sendContent() |
|||
{ |
|||
if (!$this->isSuccessful()) { |
|||
return parent::sendContent(); |
|||
} |
|||
|
|||
if (0 === $this->maxlen) { |
|||
return $this; |
|||
} |
|||
|
|||
$out = fopen('php://output', 'wb'); |
|||
$file = fopen($this->file->getPathname(), 'rb'); |
|||
|
|||
stream_copy_to_stream($file, $out, $this->maxlen, $this->offset); |
|||
|
|||
fclose($out); |
|||
fclose($file); |
|||
|
|||
if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) { |
|||
unlink($this->file->getPathname()); |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
* |
|||
* @throws \LogicException when the content is not null |
|||
*/ |
|||
public function setContent(?string $content) |
|||
{ |
|||
if (null !== $content) { |
|||
throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.'); |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function getContent() |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* Trust X-Sendfile-Type header. |
|||
*/ |
|||
public static function trustXSendfileTypeHeader() |
|||
{ |
|||
self::$trustXSendfileTypeHeader = true; |
|||
} |
|||
|
|||
/** |
|||
* If this is set to true, the file will be unlinked after the request is send |
|||
* Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used. |
|||
* |
|||
* @return $this |
|||
*/ |
|||
public function deleteFileAfterSend(bool $shouldDelete = true) |
|||
{ |
|||
$this->deleteFileAfterSend = $shouldDelete; |
|||
|
|||
return $this; |
|||
} |
|||
} |
@ -0,0 +1,244 @@ |
|||
CHANGELOG |
|||
========= |
|||
|
|||
5.0.0 |
|||
----- |
|||
|
|||
* made `Cookie` auto-secure and lax by default |
|||
* removed classes in the `MimeType` namespace, use the Symfony Mime component instead |
|||
* removed method `UploadedFile::getClientSize()` and the related constructor argument |
|||
* made `Request::getSession()` throw if the session has not been set before |
|||
* removed `Response::HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL` |
|||
* passing a null url when instantiating a `RedirectResponse` is not allowed |
|||
|
|||
4.4.0 |
|||
----- |
|||
|
|||
* passing arguments to `Request::isMethodSafe()` is deprecated. |
|||
* `ApacheRequest` is deprecated, use the `Request` class instead. |
|||
* passing a third argument to `HeaderBag::get()` is deprecated, use method `all()` instead |
|||
* `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` |
|||