Updates to vendors etc

This commit is contained in:
Chris Hunt
2025-07-11 15:57:48 +01:00
parent d972cbcd0a
commit 8fb6438254
8043 changed files with 248005 additions and 189479 deletions

View File

@@ -11,6 +11,7 @@
namespace Symfony\Component\HttpFoundation;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
use Symfony\Component\HttpFoundation\Exception\JsonException;
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
@@ -65,70 +66,56 @@ class Request
/**
* @var string[]
*/
protected static $trustedProxies = [];
protected static array $trustedProxies = [];
/**
* @var string[]
*/
protected static $trustedHostPatterns = [];
protected static array $trustedHostPatterns = [];
/**
* @var string[]
*/
protected static $trustedHosts = [];
protected static array $trustedHosts = [];
protected static $httpMethodParameterOverride = false;
protected static bool $httpMethodParameterOverride = false;
/**
* Custom parameters.
*
* @var ParameterBag
*/
public $attributes;
public ParameterBag $attributes;
/**
* Request body parameters ($_POST).
*
* @see getPayload() for portability between content types
*
* @var InputBag
*/
public $request;
public InputBag $request;
/**
* Query string parameters ($_GET).
*
* @var InputBag
*/
public $query;
public InputBag $query;
/**
* Server and execution environment parameters ($_SERVER).
*
* @var ServerBag
*/
public $server;
public ServerBag $server;
/**
* Uploaded files ($_FILES).
*
* @var FileBag
*/
public $files;
public FileBag $files;
/**
* Cookies ($_COOKIE).
*
* @var InputBag
*/
public $cookies;
public InputBag $cookies;
/**
* Headers (taken from the $_SERVER).
*
* @var HeaderBag
*/
public $headers;
public HeaderBag $headers;
/**
* @var string|resource|false|null
@@ -138,76 +125,42 @@ class Request
/**
* @var string[]|null
*/
protected $languages;
protected ?array $languages = null;
/**
* @var string[]|null
*/
protected $charsets;
protected ?array $charsets = null;
/**
* @var string[]|null
*/
protected $encodings;
protected ?array $encodings = null;
/**
* @var string[]|null
*/
protected $acceptableContentTypes;
protected ?array $acceptableContentTypes = null;
/**
* @var string|null
*/
protected $pathInfo;
/**
* @var string|null
*/
protected $requestUri;
/**
* @var string|null
*/
protected $baseUrl;
/**
* @var string|null
*/
protected $basePath;
/**
* @var string|null
*/
protected $method;
/**
* @var string|null
*/
protected $format;
/**
* @var SessionInterface|callable():SessionInterface|null
*/
protected $session;
/**
* @var string|null
*/
protected $locale;
/**
* @var string
*/
protected $defaultLocale = 'en';
protected ?string $pathInfo = null;
protected ?string $requestUri = null;
protected ?string $baseUrl = null;
protected ?string $basePath = null;
protected ?string $method = null;
protected ?string $format = null;
protected SessionInterface|\Closure|null $session = null;
protected ?string $locale = null;
protected string $defaultLocale = 'en';
/**
* @var array<string, string[]>|null
*/
protected static $formats;
protected static ?array $formats = null;
protected static $requestFactory;
protected static ?\Closure $requestFactory = null;
private ?string $preferredFormat = null;
private bool $isHostValid = true;
private bool $isForwardedValid = true;
private bool $isSafeContentPreferred;
@@ -241,8 +194,7 @@ class Request
self::HEADER_X_FORWARDED_PREFIX => 'X_FORWARDED_PREFIX',
];
/** @var bool */
private $isIisRewrite = false;
private bool $isIisRewrite = false;
/**
* @param array $query The GET parameters
@@ -270,10 +222,8 @@ class Request
* @param array $files The FILES parameters
* @param array $server The SERVER parameters
* @param string|resource|null $content The raw body data
*
* @return void
*/
public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null): void
{
$this->request = new InputBag($request);
$this->query = new InputBag($query);
@@ -304,7 +254,7 @@ class Request
$request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);
if (str_starts_with($request->headers->get('CONTENT_TYPE', ''), 'application/x-www-form-urlencoded')
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'], true)
) {
parse_str($request->getContent(), $data);
$request->request = new InputBag($data);
@@ -326,6 +276,8 @@ class Request
* @param array $files The request files ($_FILES)
* @param array $server The server parameters ($_SERVER)
* @param string|resource|null $content The raw body data
*
* @throws BadRequestException When the URI is invalid
*/
public static function create(string $uri, string $method = 'GET', array $parameters = [], array $cookies = [], array $files = [], array $server = [], $content = null): static
{
@@ -348,11 +300,20 @@ class Request
$server['PATH_INFO'] = '';
$server['REQUEST_METHOD'] = strtoupper($method);
$components = parse_url($uri);
if (false === $components) {
trigger_deprecation('symfony/http-foundation', '6.3', 'Calling "%s()" with an invalid URI is deprecated.', __METHOD__);
$components = [];
if (false === $components = parse_url(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#')) {
throw new BadRequestException('Invalid URI.');
}
if (false !== ($i = strpos($uri, '\\')) && $i < strcspn($uri, '?#')) {
throw new BadRequestException('Invalid URI: A URI cannot contain a backslash.');
}
if (\strlen($uri) !== strcspn($uri, "\r\n\t")) {
throw new BadRequestException('Invalid URI: A URI cannot contain CR/LF/TAB characters.');
}
if ('' !== $uri && (\ord($uri[0]) <= 32 || \ord($uri[-1]) <= 32)) {
throw new BadRequestException('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.');
}
if (isset($components['host'])) {
$server['SERVER_NAME'] = $components['host'];
$server['HTTP_HOST'] = $components['host'];
@@ -430,12 +391,10 @@ class Request
* This is mainly useful when you need to override the Request class
* to keep BC with an existing system. It should not be used for any
* other purpose.
*
* @return void
*/
public static function setFactory(?callable $callable)
public static function setFactory(?callable $callable): void
{
self::$requestFactory = $callable;
self::$requestFactory = null === $callable ? null : $callable(...);
}
/**
@@ -525,7 +484,7 @@ class Request
}
return
sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
\sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
$this->headers.
$cookieHeader."\r\n".
$content;
@@ -536,10 +495,8 @@ class Request
*
* It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
* $_FILES is never overridden, see rfc1867
*
* @return void
*/
public function overrideGlobals()
public function overrideGlobals(): void
{
$this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), '', '&')));
@@ -576,22 +533,26 @@ class Request
*
* You should only list the reverse proxies that you manage directly.
*
* @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR']
* @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies
*
* @return void
* @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR'] and 'PRIVATE_SUBNETS' by IpUtils::PRIVATE_SUBNETS
* @param int-mask-of<Request::HEADER_*> $trustedHeaderSet A bit field to set which headers to trust from your proxies
*/
public static function setTrustedProxies(array $proxies, int $trustedHeaderSet)
public static function setTrustedProxies(array $proxies, int $trustedHeaderSet): void
{
self::$trustedProxies = array_reduce($proxies, function ($proxies, $proxy) {
if ('REMOTE_ADDR' !== $proxy) {
$proxies[] = $proxy;
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$proxies[] = $_SERVER['REMOTE_ADDR'];
if (false !== $i = array_search('REMOTE_ADDR', $proxies, true)) {
if (isset($_SERVER['REMOTE_ADDR'])) {
$proxies[$i] = $_SERVER['REMOTE_ADDR'];
} else {
unset($proxies[$i]);
$proxies = array_values($proxies);
}
}
return $proxies;
}, []);
if (false !== ($i = array_search('PRIVATE_SUBNETS', $proxies, true)) || false !== ($i = array_search('private_ranges', $proxies, true))) {
unset($proxies[$i]);
$proxies = array_merge($proxies, IpUtils::PRIVATE_SUBNETS);
}
self::$trustedProxies = $proxies;
self::$trustedHeaderSet = $trustedHeaderSet;
}
@@ -621,12 +582,10 @@ class Request
* You should only list the hosts you manage using regexs.
*
* @param array $hostPatterns A list of trusted host patterns
*
* @return void
*/
public static function setTrustedHosts(array $hostPatterns)
public static function setTrustedHosts(array $hostPatterns): void
{
self::$trustedHostPatterns = array_map(fn ($hostPattern) => sprintf('{%s}i', $hostPattern), $hostPatterns);
self::$trustedHostPatterns = array_map(fn ($hostPattern) => \sprintf('{%s}i', $hostPattern), $hostPatterns);
// we need to reset trusted hosts on trusted host patterns change
self::$trustedHosts = [];
}
@@ -669,10 +628,8 @@ class Request
* If these methods are not protected against CSRF, this presents a possible vulnerability.
*
* The HTTP method can only be overridden when the real HTTP method is POST.
*
* @return void
*/
public static function enableHttpMethodParameterOverride()
public static function enableHttpMethodParameterOverride(): void
{
self::$httpMethodParameterOverride = true;
}
@@ -756,10 +713,7 @@ class Request
return null !== $this->session && (!$skipIfUninitialized || $this->session instanceof SessionInterface);
}
/**
* @return void
*/
public function setSession(SessionInterface $session)
public function setSession(SessionInterface $session): void
{
$this->session = $session;
}
@@ -814,9 +768,7 @@ class Request
*/
public function getClientIp(): ?string
{
$ipAddresses = $this->getClientIps();
return $ipAddresses[0];
return $this->getClientIps()[0];
}
/**
@@ -1112,7 +1064,7 @@ class Request
$https = $this->server->get('HTTPS');
return !empty($https) && 'off' !== strtolower($https);
return $https && 'off' !== strtolower($https);
}
/**
@@ -1148,13 +1100,13 @@ class Request
}
$this->isHostValid = false;
throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host));
throw new SuspiciousOperationException(\sprintf('Invalid Host "%s".', $host));
}
if (\count(self::$trustedHostPatterns) > 0) {
// to avoid host header injection attacks, you should provide a list of trusted host patterns
if (\in_array($host, self::$trustedHosts)) {
if (\in_array($host, self::$trustedHosts, true)) {
return $host;
}
@@ -1171,7 +1123,7 @@ class Request
}
$this->isHostValid = false;
throw new SuspiciousOperationException(sprintf('Untrusted Host "%s".', $host));
throw new SuspiciousOperationException(\sprintf('Untrusted Host "%s".', $host));
}
return $host;
@@ -1179,10 +1131,8 @@ class Request
/**
* Sets the request method.
*
* @return void
*/
public function setMethod(string $method)
public function setMethod(string $method): void
{
$this->method = null;
$this->server->set('REQUEST_METHOD', $method);
@@ -1230,7 +1180,7 @@ class Request
}
if (!preg_match('/^[A-Z]++$/D', $method)) {
throw new SuspiciousOperationException(sprintf('Invalid method override "%s".', $method));
throw new SuspiciousOperationException('Invalid HTTP method override.');
}
return $this->method = $method;
@@ -1287,10 +1237,10 @@ class Request
}
foreach (static::$formats as $format => $mimeTypes) {
if (\in_array($mimeType, (array) $mimeTypes)) {
if (\in_array($mimeType, (array) $mimeTypes, true)) {
return $format;
}
if (null !== $canonicalMimeType && \in_array($canonicalMimeType, (array) $mimeTypes)) {
if (null !== $canonicalMimeType && \in_array($canonicalMimeType, (array) $mimeTypes, true)) {
return $format;
}
}
@@ -1302,10 +1252,8 @@ class Request
* Associates a format with mime types.
*
* @param string|string[] $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type)
*
* @return void
*/
public function setFormat(?string $format, string|array $mimeTypes)
public function setFormat(?string $format, string|array $mimeTypes): void
{
if (null === static::$formats) {
static::initializeFormats();
@@ -1334,26 +1282,12 @@ class Request
/**
* Sets the request format.
*
* @return void
*/
public function setRequestFormat(?string $format)
public function setRequestFormat(?string $format): void
{
$this->format = $format;
}
/**
* Gets the usual name of the format associated with the request's media type (provided in the Content-Type header).
*
* @deprecated since Symfony 6.2, use getContentTypeFormat() instead
*/
public function getContentType(): ?string
{
trigger_deprecation('symfony/http-foundation', '6.2', 'The "%s()" method is deprecated, use "getContentTypeFormat()" instead.', __METHOD__);
return $this->getContentTypeFormat();
}
/**
* Gets the usual name of the format associated with the request's media type (provided in the Content-Type header).
*
@@ -1366,10 +1300,8 @@ class Request
/**
* Sets the default locale.
*
* @return void
*/
public function setDefaultLocale(string $locale)
public function setDefaultLocale(string $locale): void
{
$this->defaultLocale = $locale;
@@ -1388,10 +1320,8 @@ class Request
/**
* Sets the locale.
*
* @return void
*/
public function setLocale(string $locale)
public function setLocale(string $locale): void
{
$this->setPhpDefaultLocale($this->locale = $locale);
}
@@ -1454,7 +1384,7 @@ class Request
public function getProtocolVersion(): ?string
{
if ($this->isFromTrustedProxy()) {
preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via') ?? '', $matches);
preg_match('~^(HTTP/)?([1-9]\.[0-9])\b~', $this->headers->get('Via') ?? '', $matches);
if ($matches) {
return 'HTTP/'.$matches[2];
@@ -1533,7 +1463,7 @@ class Request
}
if (!\is_array($content)) {
throw new JsonException(sprintf('JSON content was expected to decode to an array, "%s" returned.', get_debug_type($content)));
throw new JsonException(\sprintf('JSON content was expected to decode to an array, "%s" returned.', get_debug_type($content)));
}
return new InputBag($content);
@@ -1559,7 +1489,7 @@ class Request
}
if (!\is_array($content)) {
throw new JsonException(sprintf('JSON content was expected to decode to an array, "%s" returned.', get_debug_type($content)));
throw new JsonException(\sprintf('JSON content was expected to decode to an array, "%s" returned.', get_debug_type($content)));
}
return $content;
@@ -1588,7 +1518,11 @@ class Request
*/
public function getPreferredFormat(?string $default = 'html'): ?string
{
if ($this->preferredFormat ??= $this->getRequestFormat(null)) {
if (!isset($this->preferredFormat) && null !== $preferredFormat = $this->getRequestFormat(null)) {
$this->preferredFormat = $preferredFormat;
}
if ($this->preferredFormat ?? null) {
return $this->preferredFormat;
}
@@ -1610,28 +1544,25 @@ class Request
{
$preferredLanguages = $this->getLanguages();
if (empty($locales)) {
if (!$locales) {
return $preferredLanguages[0] ?? null;
}
$locales = array_map($this->formatLocale(...), $locales);
if (!$preferredLanguages) {
return $locales[0];
}
$extendedPreferredLanguages = [];
foreach ($preferredLanguages as $language) {
$extendedPreferredLanguages[] = $language;
if (false !== $position = strpos($language, '_')) {
$superLanguage = substr($language, 0, $position);
if (!\in_array($superLanguage, $preferredLanguages)) {
$extendedPreferredLanguages[] = $superLanguage;
$combinations = array_merge(...array_map($this->getLanguageCombinations(...), $preferredLanguages));
foreach ($combinations as $combination) {
foreach ($locales as $locale) {
if (str_starts_with($locale, $combination)) {
return $locale;
}
}
}
$preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
return $preferredLanguages[0] ?? $locales[0];
return $locales[0];
}
/**
@@ -1649,32 +1580,91 @@ class Request
$this->languages = [];
foreach ($languages as $acceptHeaderItem) {
$lang = $acceptHeaderItem->getValue();
if (str_contains($lang, '-')) {
$codes = explode('-', $lang);
if ('i' === $codes[0]) {
// Language not listed in ISO 639 that are not variants
// of any listed language, which can be registered with the
// i-prefix, such as i-cherokee
if (\count($codes) > 1) {
$lang = $codes[1];
}
} else {
for ($i = 0, $max = \count($codes); $i < $max; ++$i) {
if (0 === $i) {
$lang = strtolower($codes[0]);
} else {
$lang .= '_'.strtoupper($codes[$i]);
}
}
}
}
$this->languages[] = $lang;
$this->languages[] = self::formatLocale($lang);
}
$this->languages = array_unique($this->languages);
return $this->languages;
}
/**
* Strips the locale to only keep the canonicalized language value.
*
* Depending on the $locale value, this method can return values like :
* - language_Script_REGION: "fr_Latn_FR", "zh_Hans_TW"
* - language_Script: "fr_Latn", "zh_Hans"
* - language_REGION: "fr_FR", "zh_TW"
* - language: "fr", "zh"
*
* Invalid locale values are returned as is.
*
* @see https://wikipedia.org/wiki/IETF_language_tag
* @see https://datatracker.ietf.org/doc/html/rfc5646
*/
private static function formatLocale(string $locale): string
{
[$language, $script, $region] = self::getLanguageComponents($locale);
return implode('_', array_filter([$language, $script, $region]));
}
/**
* Returns an array of all possible combinations of the language components.
*
* For instance, if the locale is "fr_Latn_FR", this method will return:
* - "fr_Latn_FR"
* - "fr_Latn"
* - "fr_FR"
* - "fr"
*
* @return string[]
*/
private static function getLanguageCombinations(string $locale): array
{
[$language, $script, $region] = self::getLanguageComponents($locale);
return array_unique([
implode('_', array_filter([$language, $script, $region])),
implode('_', array_filter([$language, $script])),
implode('_', array_filter([$language, $region])),
$language,
]);
}
/**
* Returns an array with the language components of the locale.
*
* For example:
* - If the locale is "fr_Latn_FR", this method will return "fr", "Latn", "FR"
* - If the locale is "fr_FR", this method will return "fr", null, "FR"
* - If the locale is "zh_Hans", this method will return "zh", "Hans", null
*
* @see https://wikipedia.org/wiki/IETF_language_tag
* @see https://datatracker.ietf.org/doc/html/rfc5646
*
* @return array{string, string|null, string|null}
*/
private static function getLanguageComponents(string $locale): array
{
$locale = str_replace('_', '-', strtolower($locale));
$pattern = '/^([a-zA-Z]{2,3}|i-[a-zA-Z]{5,})(?:-([a-zA-Z]{4}))?(?:-([a-zA-Z]{2}))?(?:-(.+))?$/';
if (!preg_match($pattern, $locale, $matches)) {
return [$locale, null, null];
}
if (str_starts_with($matches[1], 'i-')) {
// Language not listed in ISO 639 that are not variants
// of any listed language, which can be registered with the
// i-prefix, such as i-cherokee
$matches[1] = substr($matches[1], 2);
}
return [
$matches[1],
isset($matches[2]) ? ucfirst(strtolower($matches[2])) : null,
isset($matches[3]) ? strtoupper($matches[3]) : null,
];
}
/**
* Gets a list of charsets acceptable by the client browser in preferable order.
*
@@ -1745,10 +1735,7 @@ class Request
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (https://www.zend.com/)
*/
/**
* @return string
*/
protected function prepareRequestUri()
protected function prepareRequestUri(): string
{
$requestUri = '';
@@ -1844,7 +1831,7 @@ class Request
}
$basename = basename($baseUrl ?? '');
if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
if (!$basename || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
// no match whatsoever; set it blank
return '';
}
@@ -1865,7 +1852,7 @@ class Request
protected function prepareBasePath(): string
{
$baseUrl = $this->getBaseUrl();
if (empty($baseUrl)) {
if (!$baseUrl) {
return '';
}
@@ -1905,7 +1892,7 @@ class Request
}
$pathInfo = substr($requestUri, \strlen($baseUrl));
if (false === $pathInfo || '' === $pathInfo) {
if ('' === $pathInfo) {
// If substr() returns false then PATH_INFO is set to an empty string
return '/';
}
@@ -1915,10 +1902,8 @@ class Request
/**
* Initializes HTTP request formats.
*
* @return void
*/
protected static function initializeFormats()
protected static function initializeFormats(): void
{
static::$formats = [
'html' => ['text/html', 'application/xhtml+xml'],
@@ -1966,7 +1951,7 @@ class Request
$len = \strlen($prefix);
if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
if (preg_match(\sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
return $match[0];
}
@@ -2058,7 +2043,7 @@ class Request
}
$this->isForwardedValid = false;
throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::TRUSTED_HEADERS[self::HEADER_FORWARDED], self::TRUSTED_HEADERS[$type]));
throw new ConflictingHeadersException(\sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::TRUSTED_HEADERS[self::HEADER_FORWARDED], self::TRUSTED_HEADERS[$type]));
}
private function normalizeAndFilterClientIps(array $clientIps, string $ip): array