N°6002 - Explicitly add symfony/http-foundation and symfony/http-kernel to composer.json for easier lib update

This commit is contained in:
Molkobain
2023-02-22 22:38:15 +01:00
parent d997e36de0
commit 826fbe10c8
28 changed files with 262 additions and 144 deletions

View File

@@ -25,6 +25,8 @@
"symfony/console": "5.4.*",
"symfony/dotenv": "5.4.*",
"symfony/framework-bundle": "5.4.*",
"symfony/http-foundation": "5.4.*",
"symfony/http-kernel": "5.4.*",
"symfony/twig-bundle": "5.4.*",
"symfony/yaml": "5.4.*",
"thenetworg/oauth2-azure": "^2.0"

31
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1fee7c7fd7649286a09641ae53e216de",
"content-hash": "778cb837668f4ab5686021bf761d0a83",
"packages": [
{
"name": "apereo/phpcas",
@@ -3214,16 +3214,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v5.4.11",
"version": "v5.4.20",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "0a5868e0999e9d47859ba3d918548ff6943e6389"
"reference": "d0435363362a47c14e9cf50663cb8ffbf491875a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/0a5868e0999e9d47859ba3d918548ff6943e6389",
"reference": "0a5868e0999e9d47859ba3d918548ff6943e6389",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0435363362a47c14e9cf50663cb8ffbf491875a",
"reference": "d0435363362a47c14e9cf50663cb8ffbf491875a",
"shasum": ""
},
"require": {
@@ -3235,8 +3235,11 @@
"require-dev": {
"predis/predis": "~1.0",
"symfony/cache": "^4.4|^5.0|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/expression-language": "^4.4|^5.0|^6.0",
"symfony/mime": "^4.4|^5.0|^6.0"
"symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4",
"symfony/mime": "^4.4|^5.0|^6.0",
"symfony/rate-limiter": "^5.2|^6.0"
},
"suggest": {
"symfony/mime": "To use the file extension guesser"
@@ -3267,7 +3270,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v5.4.11"
"source": "https://github.com/symfony/http-foundation/tree/v5.4.20"
},
"funding": [
{
@@ -3283,20 +3286,20 @@
"type": "tidelift"
}
],
"time": "2022-07-20T13:00:38+00:00"
"time": "2023-01-29T11:11:52+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v5.4.11",
"version": "v5.4.20",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "4fd590a2ef3f62560dbbf6cea511995dd77321ee"
"reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/4fd590a2ef3f62560dbbf6cea511995dd77321ee",
"reference": "4fd590a2ef3f62560dbbf6cea511995dd77321ee",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/aaeec341582d3c160cc9ecfa8b2419ba6c69954e",
"reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e",
"shasum": ""
},
"require": {
@@ -3379,7 +3382,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v5.4.11"
"source": "https://github.com/symfony/http-kernel/tree/v5.4.20"
},
"funding": [
{
@@ -3395,7 +3398,7 @@
"type": "tidelift"
}
],
"time": "2022-07-29T12:30:22+00:00"
"time": "2023-02-01T08:18:48+00:00"
},
{
"name": "symfony/polyfill-ctype",

View File

@@ -3340,17 +3340,17 @@
},
{
"name": "symfony/http-foundation",
"version": "v5.4.11",
"version_normalized": "5.4.11.0",
"version": "v5.4.20",
"version_normalized": "5.4.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "0a5868e0999e9d47859ba3d918548ff6943e6389"
"reference": "d0435363362a47c14e9cf50663cb8ffbf491875a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/0a5868e0999e9d47859ba3d918548ff6943e6389",
"reference": "0a5868e0999e9d47859ba3d918548ff6943e6389",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/d0435363362a47c14e9cf50663cb8ffbf491875a",
"reference": "d0435363362a47c14e9cf50663cb8ffbf491875a",
"shasum": ""
},
"require": {
@@ -3362,13 +3362,16 @@
"require-dev": {
"predis/predis": "~1.0",
"symfony/cache": "^4.4|^5.0|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/expression-language": "^4.4|^5.0|^6.0",
"symfony/mime": "^4.4|^5.0|^6.0"
"symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4",
"symfony/mime": "^4.4|^5.0|^6.0",
"symfony/rate-limiter": "^5.2|^6.0"
},
"suggest": {
"symfony/mime": "To use the file extension guesser"
},
"time": "2022-07-20T13:00:38+00:00",
"time": "2023-01-29T11:11:52+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -3396,7 +3399,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v5.4.11"
"source": "https://github.com/symfony/http-foundation/tree/v5.4.20"
},
"funding": [
{
@@ -3416,17 +3419,17 @@
},
{
"name": "symfony/http-kernel",
"version": "v5.4.11",
"version_normalized": "5.4.11.0",
"version": "v5.4.20",
"version_normalized": "5.4.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "4fd590a2ef3f62560dbbf6cea511995dd77321ee"
"reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/4fd590a2ef3f62560dbbf6cea511995dd77321ee",
"reference": "4fd590a2ef3f62560dbbf6cea511995dd77321ee",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/aaeec341582d3c160cc9ecfa8b2419ba6c69954e",
"reference": "aaeec341582d3c160cc9ecfa8b2419ba6c69954e",
"shasum": ""
},
"require": {
@@ -3483,7 +3486,7 @@
"symfony/console": "",
"symfony/dependency-injection": ""
},
"time": "2022-07-29T12:30:22+00:00",
"time": "2023-02-01T08:18:48+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -3511,7 +3514,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v5.4.11"
"source": "https://github.com/symfony/http-kernel/tree/v5.4.20"
},
"funding": [
{

View File

@@ -5,7 +5,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '5ee603f2ba067f521d8b98a14c42d09820dec2fb',
'reference' => 'd997e36de0f7ad4a820dbae8851c60f55fcd1c25',
'name' => 'combodo/itop',
'dev' => true,
),
@@ -25,7 +25,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '5ee603f2ba067f521d8b98a14c42d09820dec2fb',
'reference' => 'd997e36de0f7ad4a820dbae8851c60f55fcd1c25',
'dev_requirement' => false,
),
'combodo/tcpdf' => array(
@@ -498,21 +498,21 @@
'dev_requirement' => false,
),
'symfony/http-foundation' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'pretty_version' => 'v5.4.20',
'version' => '5.4.20.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/http-foundation',
'aliases' => array(),
'reference' => '0a5868e0999e9d47859ba3d918548ff6943e6389',
'reference' => 'd0435363362a47c14e9cf50663cb8ffbf491875a',
'dev_requirement' => false,
),
'symfony/http-kernel' => array(
'pretty_version' => 'v5.4.11',
'version' => '5.4.11.0',
'pretty_version' => 'v5.4.20',
'version' => '5.4.20.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/http-kernel',
'aliases' => array(),
'reference' => '4fd590a2ef3f62560dbbf6cea511995dd77321ee',
'reference' => 'aaeec341582d3c160cc9ecfa8b2419ba6c69954e',
'dev_requirement' => false,
),
'symfony/polyfill-ctype' => array(

View File

@@ -206,15 +206,19 @@ class BinaryFileResponse extends Response
*/
public function prepare(Request $request)
{
if ($this->isInformational() || $this->isEmpty()) {
parent::prepare($request);
$this->maxlen = 0;
return $this;
}
if (!$this->headers->has('Content-Type')) {
$this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
}
if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
$this->setProtocolVersion('1.1');
}
$this->ensureIEOverSSLCompatibility($request);
parent::prepare($request);
$this->offset = 0;
$this->maxlen = -1;
@@ -222,6 +226,7 @@ class BinaryFileResponse extends Response
if (false === $fileSize = $this->file->getSize()) {
return $this;
}
$this->headers->remove('Transfer-Encoding');
$this->headers->set('Content-Length', $fileSize);
if (!$this->headers->has('Accept-Ranges')) {
@@ -291,6 +296,10 @@ class BinaryFileResponse extends Response
}
}
if ($request->isMethod('HEAD')) {
$this->maxlen = 0;
}
return $this;
}
@@ -312,40 +321,42 @@ class BinaryFileResponse extends Response
*/
public function sendContent()
{
if (!$this->isSuccessful()) {
return parent::sendContent();
}
if (0 === $this->maxlen) {
return $this;
}
$out = fopen('php://output', 'w');
$file = fopen($this->file->getPathname(), 'r');
ignore_user_abort(true);
if (0 !== $this->offset) {
fseek($file, $this->offset);
}
$length = $this->maxlen;
while ($length && !feof($file)) {
$read = ($length > $this->chunkSize) ? $this->chunkSize : $length;
$length -= $read;
stream_copy_to_stream($file, $out, $read);
if (connection_aborted()) {
break;
try {
if (!$this->isSuccessful()) {
return parent::sendContent();
}
}
fclose($out);
fclose($file);
if (0 === $this->maxlen) {
return $this;
}
if ($this->deleteFileAfterSend && is_file($this->file->getPathname())) {
unlink($this->file->getPathname());
$out = fopen('php://output', 'w');
$file = fopen($this->file->getPathname(), 'r');
ignore_user_abort(true);
if (0 !== $this->offset) {
fseek($file, $this->offset);
}
$length = $this->maxlen;
while ($length && !feof($file)) {
$read = ($length > $this->chunkSize) ? $this->chunkSize : $length;
$length -= $read;
stream_copy_to_stream($file, $out, $read);
if (connection_aborted()) {
break;
}
}
fclose($out);
fclose($file);
} finally {
if ($this->deleteFileAfterSend && is_file($this->file->getPathname())) {
unlink($this->file->getPathname());
}
}
return $this;

View File

@@ -12,7 +12,7 @@
namespace Symfony\Component\HttpFoundation\Exception;
/**
* Raised when a session does not exists. This happens in the following cases:
* Raised when a session does not exist. This happens in the following cases:
* - the session is not enabled
* - attempt to read a session outside a request context (ie. cli script).
*

View File

@@ -86,7 +86,7 @@ class IpUtils
[$address, $netmask] = explode('/', $ip, 2);
if ('0' === $netmask) {
return self::$checkedIps[$cacheKey] = filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4);
return self::$checkedIps[$cacheKey] = false !== filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4);
}
if ($netmask < 0 || $netmask > 32) {
@@ -135,9 +135,18 @@ class IpUtils
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
}
// Check to see if we were given a IP4 $requestIp or $ip by mistake
if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
return self::$checkedIps[$cacheKey] = false;
}
if (str_contains($ip, '/')) {
[$address, $netmask] = explode('/', $ip, 2);
if (!filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
return self::$checkedIps[$cacheKey] = false;
}
if ('0' === $netmask) {
return (bool) unpack('n*', @inet_pton($address));
}
@@ -146,6 +155,10 @@ class IpUtils
return self::$checkedIps[$cacheKey] = false;
}
} else {
if (!filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
return self::$checkedIps[$cacheKey] = false;
}
$address = $ip;
$netmask = 128;
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2022 Fabien Potencier
Copyright (c) 2004-2023 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

View File

@@ -35,9 +35,7 @@ abstract class AbstractRequestRateLimiter implements RequestRateLimiterInterface
foreach ($limiters as $limiter) {
$rateLimit = $limiter->consume(1);
if (null === $minimalRateLimit || $rateLimit->getRemainingTokens() < $minimalRateLimit->getRemainingTokens()) {
$minimalRateLimit = $rateLimit;
}
$minimalRateLimit = $minimalRateLimit ? self::getMinimalRateLimit($minimalRateLimit, $rateLimit) : $rateLimit;
}
return $minimalRateLimit;
@@ -54,4 +52,20 @@ abstract class AbstractRequestRateLimiter implements RequestRateLimiterInterface
* @return LimiterInterface[] a set of limiters using keys extracted from the request
*/
abstract protected function getLimiters(Request $request): array;
private static function getMinimalRateLimit(RateLimit $first, RateLimit $second): RateLimit
{
if ($first->isAccepted() !== $second->isAccepted()) {
return $first->isAccepted() ? $second : $first;
}
$firstRemainingTokens = $first->getRemainingTokens();
$secondRemainingTokens = $second->getRemainingTokens();
if ($firstRemainingTokens === $secondRemainingTokens) {
return $first->getRetryAfter() < $second->getRetryAfter() ? $second : $first;
}
return $firstRemainingTokens > $secondRemainingTokens ? $second : $first;
}
}

View File

@@ -522,10 +522,10 @@ class Request
$cookies = [];
foreach ($this->cookies as $k => $v) {
$cookies[] = $k.'='.$v;
$cookies[] = \is_array($v) ? http_build_query([$k => $v], '', '; ', \PHP_QUERY_RFC3986) : "$k=$v";
}
if (!empty($cookies)) {
if ($cookies) {
$cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
}
@@ -1689,7 +1689,8 @@ class Request
$languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
$this->languages = [];
foreach ($languages as $lang => $acceptHeaderItem) {
foreach ($languages as $acceptHeaderItem) {
$lang = $acceptHeaderItem->getValue();
if (str_contains($lang, '-')) {
$codes = explode('-', $lang);
if ('i' === $codes[0]) {
@@ -1727,7 +1728,7 @@ class Request
return $this->charsets;
}
return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
return $this->charsets = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all()));
}
/**
@@ -1741,7 +1742,7 @@ class Request
return $this->encodings;
}
return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all());
return $this->encodings = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all()));
}
/**
@@ -1755,7 +1756,7 @@ class Request
return $this->acceptableContentTypes;
}
return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
return $this->acceptableContentTypes = array_map('strval', array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all()));
}
/**

View File

@@ -399,6 +399,7 @@ class Response
litespeed_finish_request();
} elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
static::closeOutputBuffers(0, true);
flush();
}
return $this;
@@ -1245,7 +1246,6 @@ class Response
while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) {
if ($flush) {
ob_end_flush();
flush();
} else {
ob_end_clean();
}

View File

@@ -77,7 +77,7 @@ class MemcachedSessionHandler extends AbstractSessionHandler
#[\ReturnTypeWillChange]
public function updateTimestamp($sessionId, $data)
{
$this->memcached->touch($this->prefix.$sessionId, time() + (int) ($this->ttl ?? \ini_get('session.gc_maxlifetime')));
$this->memcached->touch($this->prefix.$sessionId, $this->getCompatibleTtl());
return true;
}
@@ -87,7 +87,20 @@ class MemcachedSessionHandler extends AbstractSessionHandler
*/
protected function doWrite(string $sessionId, string $data)
{
return $this->memcached->set($this->prefix.$sessionId, $data, time() + (int) ($this->ttl ?? \ini_get('session.gc_maxlifetime')));
return $this->memcached->set($this->prefix.$sessionId, $data, $this->getCompatibleTtl());
}
private function getCompatibleTtl(): int
{
$ttl = (int) ($this->ttl ?? \ini_get('session.gc_maxlifetime'));
// If the relative TTL that is used exceeds 30 days, memcached will treat the value as Unix time.
// We have to convert it to an absolute Unix time at this point, to make sure the TTL is correct.
if ($ttl > 60 * 60 * 24 * 30) {
$ttl += time();
}
return $ttl;
}
/**

View File

@@ -30,6 +30,16 @@ class StrictSessionHandler extends AbstractSessionHandler
$this->handler = $handler;
}
/**
* Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
*
* @internal
*/
public function isWrapper(): bool
{
return $this->handler instanceof \SessionHandler;
}
/**
* @return bool
*/

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
/**
* @author Drak <drak@zikula.org>
*/
@@ -22,7 +24,7 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
{
$this->handler = $handler;
$this->wrapper = $handler instanceof \SessionHandler;
$this->saveHandlerName = $this->wrapper ? \ini_get('session.save_handler') : 'user';
$this->saveHandlerName = $this->wrapper || ($handler instanceof StrictSessionHandler && $handler->isWrapper()) ? \ini_get('session.save_handler') : 'user';
}
/**

View File

@@ -24,8 +24,11 @@
"require-dev": {
"predis/predis": "~1.0",
"symfony/cache": "^4.4|^5.0|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4",
"symfony/mime": "^4.4|^5.0|^6.0",
"symfony/expression-language": "^4.4|^5.0|^6.0"
"symfony/expression-language": "^4.4|^5.0|^6.0",
"symfony/rate-limiter": "^5.2|^6.0"
},
"suggest" : {
"symfony/mime": "To use the file extension guesser"

View File

@@ -69,8 +69,9 @@ final class NotTaggedControllerValueResolver implements ArgumentValueResolverInt
}
if (!$this->container->has($controller)) {
$i = strrpos($controller, ':');
$controller = substr($controller, 0, $i).strtolower(substr($controller, $i));
$controller = (false !== $i = strrpos($controller, ':'))
? substr($controller, 0, $i).strtolower(substr($controller, $i))
: $controller.'::__invoke';
}
$what = sprintf('argument $%s of "%s()"', $argument->getName(), $controller);

View File

@@ -33,7 +33,7 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
$class = $reflection->class;
} else {
$reflection = new \ReflectionFunction($controller);
if ($class = str_contains($reflection->name, '{closure}') ? null : $reflection->getClosureScopeClass()) {
if ($class = str_contains($reflection->name, '{closure}') ? null : (\PHP_VERSION_ID >= 80111 ? $reflection->getClosureCalledClass() : $reflection->getClosureScopeClass())) {
$class = $class->name;
}
}

View File

@@ -144,7 +144,7 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
$allChannels = [];
foreach ($this->getProcessedLogs() as $log) {
if ('' === trim($log['channel'])) {
if ('' === trim($log['channel'] ?? '')) {
continue;
}

View File

@@ -110,7 +110,7 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
'session_metadata' => $sessionMetadata,
'session_attributes' => $sessionAttributes,
'session_usages' => array_values($this->sessionUsages),
'stateless_check' => $this->requestStack && $this->requestStack->getMainRequest()->attributes->get('_stateless', false),
'stateless_check' => $this->requestStack && ($mainRequest = $this->requestStack->getMainRequest()) && $mainRequest->attributes->get('_stateless', false),
'flashes' => $flashes,
'path_info' => $request->getPathInfo(),
'controller' => 'n/a',
@@ -479,7 +479,7 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
}
$controller['method'] = $r->name;
if ($class = $r->getClosureScopeClass()) {
if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
$controller['class'] = $class->name;
} else {
return $r->name;

View File

@@ -24,6 +24,7 @@ use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
/**
@@ -174,7 +175,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
$invalidBehavior = ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE;
}
if (Request::class === $type || SessionInterface::class === $type) {
if (Request::class === $type || SessionInterface::class === $type || Response::class === $type) {
continue;
}

View File

@@ -158,6 +158,11 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
$isSessionEmpty = $session->isEmpty() && empty($_SESSION); // checking $_SESSION to keep compatibility with native sessions
if ($requestSessionCookieId && $isSessionEmpty) {
// PHP internally sets the session cookie value to "deleted" when setcookie() is called with empty string $value argument
// which happens in \Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler::destroy
// when the session gets invalidated (for example on logout) so we must handle this case here too
// otherwise we would send two Set-Cookie headers back with the response
SessionUtils::popSessionCookie($sessionName, 'deleted');
$response->headers->clearCookie(
$sessionName,
$sessionCookiePath,
@@ -195,10 +200,11 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
}
if ($autoCacheControl) {
$maxAge = $response->headers->hasCacheControlDirective('public') ? 0 : (int) $response->getMaxAge();
$response
->setExpires(new \DateTime())
->setExpires(new \DateTimeImmutable('+'.$maxAge.' seconds'))
->setPrivate()
->setMaxAge(0)
->setMaxAge($maxAge)
->headers->addCacheControlDirective('must-revalidate');
}

View File

@@ -33,8 +33,14 @@ class ErrorListener implements EventSubscriberInterface
protected $controller;
protected $logger;
protected $debug;
/**
* @var array<class-string, array{log_level: string|null, status_code: int<100,599>|null}>
*/
protected $exceptionsMapping;
/**
* @param array<class-string, array{log_level: string|null, status_code: int<100,599>|null}> $exceptionsMapping
*/
public function __construct($controller, LoggerInterface $logger = null, bool $debug = false, array $exceptionsMapping = [])
{
$this->controller = $controller;

View File

@@ -29,17 +29,28 @@ class Store implements StoreInterface
private $keyCache;
/** @var array<string, resource> */
private $locks = [];
private $options;
/**
* Constructor.
*
* The available options are:
*
* * private_headers Set of response headers that should not be stored
* when a response is cached. (default: Set-Cookie)
*
* @throws \RuntimeException
*/
public function __construct(string $root)
public function __construct(string $root, array $options = [])
{
$this->root = $root;
if (!is_dir($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root));
}
$this->keyCache = new \SplObjectStorage();
$this->options = array_merge([
'private_headers' => ['Set-Cookie'],
], $options);
}
/**
@@ -216,6 +227,10 @@ class Store implements StoreInterface
$headers = $this->persistResponse($response);
unset($headers['age']);
foreach ($this->options['private_headers'] as $h) {
unset($headers[strtolower($h)]);
}
array_unshift($entries, [$storedEnv, $headers]);
if (!$this->save($key, serialize($entries))) {

View File

@@ -70,6 +70,7 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
{
$request->headers->set('X-Php-Ob-Level', (string) ob_get_level());
$this->requestStack->push($request);
try {
return $this->handleRaw($request, $type);
} catch (\Exception $e) {
@@ -83,6 +84,8 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
}
return $this->handleThrowable($e, $request, $type);
} finally {
$this->requestStack->pop();
}
}
@@ -103,7 +106,17 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
throw $exception;
}
$response = $this->handleThrowable($exception, $request, self::MAIN_REQUEST);
if ($pop = $request !== $this->requestStack->getMainRequest()) {
$this->requestStack->push($request);
}
try {
$response = $this->handleThrowable($exception, $request, self::MAIN_REQUEST);
} finally {
if ($pop) {
$this->requestStack->pop();
}
}
$response->sendHeaders();
$response->sendContent();
@@ -121,8 +134,6 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
*/
private function handleRaw(Request $request, int $type = self::MAIN_REQUEST): Response
{
$this->requestStack->push($request);
// request
$event = new RequestEvent($this, $request, $type);
$this->dispatcher->dispatch($event, KernelEvents::REQUEST);
@@ -199,7 +210,6 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
private function finishRequest(Request $request, int $type)
{
$this->dispatcher->dispatch(new FinishRequestEvent($this, $request, $type), KernelEvents::FINISH_REQUEST);
$this->requestStack->pop();
}
/**

View File

@@ -78,11 +78,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static $freshCache = [];
public const VERSION = '5.4.11';
public const VERSION_ID = 50411;
public const VERSION = '5.4.20';
public const VERSION_ID = 50420;
public const MAJOR_VERSION = 5;
public const MINOR_VERSION = 4;
public const RELEASE_VERSION = 11;
public const RELEASE_VERSION = 20;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '11/2024';

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2022 Fabien Potencier
Copyright (c) 2004-2023 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

View File

@@ -49,10 +49,14 @@ class Logger extends AbstractLogger
if (isset($_ENV['SHELL_VERBOSITY']) || isset($_SERVER['SHELL_VERBOSITY'])) {
switch ((int) ($_ENV['SHELL_VERBOSITY'] ?? $_SERVER['SHELL_VERBOSITY'])) {
case -1: $minLevel = LogLevel::ERROR; break;
case 1: $minLevel = LogLevel::NOTICE; break;
case 2: $minLevel = LogLevel::INFO; break;
case 3: $minLevel = LogLevel::DEBUG; break;
case -1: $minLevel = LogLevel::ERROR;
break;
case 1: $minLevel = LogLevel::NOTICE;
break;
case 2: $minLevel = LogLevel::INFO;
break;
case 3: $minLevel = LogLevel::DEBUG;
break;
}
}
}
@@ -85,7 +89,7 @@ class Logger extends AbstractLogger
$formatter = $this->formatter;
if ($this->handle) {
@fwrite($this->handle, $formatter($level, $message, $context));
@fwrite($this->handle, $formatter($level, $message, $context).\PHP_EOL);
} else {
error_log($formatter($level, $message, $context, false));
}
@@ -110,7 +114,7 @@ class Logger extends AbstractLogger
$message = strtr($message, $replacements);
}
$log = sprintf('[%s] %s', $level, $message).\PHP_EOL;
$log = sprintf('[%s] %s', $level, $message);
if ($prefixDate) {
$log = date(\DateTime::RFC3339).' '.$log;
}

View File

@@ -115,19 +115,7 @@ class FileProfilerStorage implements ProfilerStorageInterface
*/
public function read(string $token): ?Profile
{
if (!$token || !file_exists($file = $this->getFilename($token))) {
return null;
}
if (\function_exists('gzcompress')) {
$file = 'compress.zlib://'.$file;
}
if (!$data = unserialize(file_get_contents($file))) {
return null;
}
return $this->createProfileFromData($token, $data);
return $this->doRead($token);
}
/**
@@ -169,14 +157,13 @@ class FileProfilerStorage implements ProfilerStorageInterface
'status_code' => $profile->getStatusCode(),
];
$context = stream_context_create();
$data = serialize($data);
if (\function_exists('gzcompress')) {
$file = 'compress.zlib://'.$file;
stream_context_set_option($context, 'zlib', 'level', 3);
if (\function_exists('gzencode')) {
$data = gzencode($data, 3);
}
if (false === file_put_contents($file, serialize($data), 0, $context)) {
if (false === file_put_contents($file, $data, \LOCK_EX)) {
return false;
}
@@ -291,21 +278,34 @@ class FileProfilerStorage implements ProfilerStorageInterface
}
foreach ($data['children'] as $token) {
if (!$token || !file_exists($file = $this->getFilename($token))) {
continue;
if (null !== $childProfile = $this->doRead($token, $profile)) {
$profile->addChild($childProfile);
}
if (\function_exists('gzcompress')) {
$file = 'compress.zlib://'.$file;
}
if (!$childData = unserialize(file_get_contents($file))) {
continue;
}
$profile->addChild($this->createProfileFromData($token, $childData, $profile));
}
return $profile;
}
private function doRead($token, Profile $profile = null): ?Profile
{
if (!$token || !file_exists($file = $this->getFilename($token))) {
return null;
}
$h = fopen($file, 'r');
flock($h, \LOCK_SH);
$data = stream_get_contents($h);
flock($h, \LOCK_UN);
fclose($h);
if (\function_exists('gzdecode')) {
$data = @gzdecode($data) ?: $data;
}
if (!$data = unserialize($data)) {
return null;
}
return $this->createProfileFromData($token, $data, $profile);
}
}